我想在虚拟 php 网页上实现从访客到我的 Openldap 服务器的 mTLS。 我有问题,即使我相应地指定了选项,我的 php 似乎也从未将客户端证书发送到我的服务器。我的客户访客上有
php 8.2
,我的 slapd
的 openldap server
版本位于 2.5.13
版本中。
我在我的服务器上启用了 TLS 并强制执行双向 TLS (mTLS):
olcTLSCACertificateFile: /etc/ldap/certs/issuing_ca.crt
olcTLSCertificateFile: /etc/ldap/certs/ldap.test.local.crt
olcTLSCertificateKeyFile: /etc/ldap/certs/ldap.test.local.key
olcTLSVerifyClient: demand
我可以使用来自客户来宾的客户端证书成功连接 csh 中的 ldapsearch,因此它们是正确且有效的:
setenv LDAPTLS_CACERT /var/run/certs/f03dd1db.crt
setenv LDAPTLS_CERT /var/run/certs/229c13f4.crt
setenv LDAPTLS_KEY /var/run/certs/229c13f4.key
ldapsearch -H ldaps://ldap.domain.local -b "dc=domain,dc=local" -D "uid=ldap_srv,ou=users,dc=domain,dc=local" -W -d 256 -x -LLL
但是我无法让它与我的 php 一起工作:
function simple_test(){
$ldapUri = 'ldaps://ldap.domain.local:636';
$caFile = '/var/run/certs/f03dd1db.crt';
$certFile = '/var/run/certs/229c13f4.crt';
$keyFile = '/var/run/certs/229c13f4.key';
$ldapbindun = 'uid=ldap_srv,ou=users,dc=domain,dc=local';
$ldapbindpw = 'password';
$ldapOptions = [
LDAP_OPT_X_TLS_CACERTFILE => $caFile,
LDAP_OPT_X_TLS_CERTFILE => $certFile,
LDAP_OPT_X_TLS_KEYFILE => $keyFile,
LDAP_OPT_PROTOCOL_VERSION => 3,
LDAP_OPT_REFERRALS => 0,
];
//set global options
foreach($ldapOptions as $k => $v) {
ldap_set_option(null, $k, $v);
}
$error = false;
// initial connection seems to work and return success:
if (!($ldapResource = ldap_connect($ldapserver))) {
$error = true;
}
// Also tried to set options here especially for $ldap without luck also :
//ldap_set_option(NULL, LDAP_OPT_X_TLS_CERTFILE, "/var/run/certs/229c13f4.crt");
//ldap_set_option(NULL, LDAP_OPT_X_TLS_KEYFILE, "/var/run/certs/229c13f4.key");
//try to bind and check for errors (that I have):
//Same error if I switch to ldap_bind($ldapResource)
if (!($res = ldap_bind($ldapResource, $ldapbindun, $ldapbindpw))) {
$error = true;
}
if ($error == true) {
ldap_get_option($ldapResource, LDAP_OPT_DIAGNOSTIC_MESSAGE, $errMsg);
log_error(sprintf(gettext("%s (%d): %s\n"),ldap_error($ldapResource), ldap_errno($ldapResource),$errMsg));
@ldap_close($ldapResource);
}
}
客户端日志显示:
LDAP bind failed: Can't contact LDAP server (-1):
服务器日志向我展示:
slap_listener_activate(8):
>>> slap_listener(ldaps:///)
conn=1022 fd=14 ACCEPT from IP=<MY_CLIENT_IP>:32896 (IP=0.0.0.0:636)
connection_get(14): got connid=1022
connection_read(14): checking for input on id=1022
connection_get(14): got connid=1022
connection_read(14): checking for input on id=1022
TLS: can't accept: Certificate is required..
connection_read(14): TLS accept failure error=-1 id=1022, closing
connection_close: conn=1022 sd=14
conn=1022 fd=14 closed (TLS negotiation failure)
但是,当我在绑定之前打印 $ldap 选项时,似乎一切都是正确的:
Option LDAP_OPT_DEREF: 'LDAP_DEREF_SEARCHING (Searching)'
Option LDAP_OPT_SIZELIMIT: 0
Option LDAP_OPT_TIMELIMIT: 25
Option LDAP_OPT_NETWORK_TIMEOUT: 25
Option LDAP_OPT_PROTOCOL_VERSION: '3 (LDAPv3)'
Option LDAP_OPT_ERROR_NUMBER: -1
Option LDAP_OPT_ERROR_STRING: Unable to retrieve value
Option LDAP_OPT_REFERRALS: '0 (Disable)'
Option LDAP_OPT_RESTART: '0 (Disable)'
Option LDAP_OPT_HOST_NAME: 'ldap.domain.local:636'
Option LDAP_OPT_MATCHED_DN: Unable to retrieve value
Option LDAP_OPT_SERVER_CONTROLS: Unable to retrieve value
Option LDAP_OPT_CLIENT_CONTROLS: Unable to retrieve value
Option LDAP_OPT_X_KEEPALIVE_IDLE: 0
Option LDAP_OPT_X_KEEPALIVE_PROBES: 0
Option LDAP_OPT_X_KEEPALIVE_INTERVAL: 0
Option LDAP_OPT_X_TLS_CACERTDIR: '/var/run/certs'
Option LDAP_OPT_X_TLS_CACERTFILE: '/var/run/certs/f03dd1db.crt'
Option LDAP_OPT_X_TLS_CERTFILE: '/var/run/certs/229c13f4.crt'
Option LDAP_OPT_X_TLS_CIPHER_SUITE: Unable to retrieve value
Option LDAP_OPT_X_TLS_CRLCHECK: 'LDAP_OPT_X_TLS_CRL_NONE (No CRL Checking)'
Option LDAP_OPT_X_TLS_CRLFILE: Unable to retrieve value
Option LDAP_OPT_X_TLS_DHFILE: Unable to retrieve value
Option LDAP_OPT_X_TLS_KEYFILE: '/var/run/certs/229c13f4.key'
Option LDAP_OPT_X_TLS_PACKAGE: 'OpenSSL'
Option LDAP_OPT_X_TLS_PROTOCOL_MIN: 'LDAP_OPT_X_TLS_PROTOCOL_SSL2 (SSLv2)'
Option LDAP_OPT_X_TLS_REQUIRE_CERT: 'LDAP_OPT_X_TLS_HARD (Hard)'
Option LDAP_OPT_X_TLS_RANDOM_FILE: Unable to retrieve value
但不知何故,我的 php 显然做错了什么。如果我相信 来自 php 手册的用户评论,或者 来自 github php 项目的评论,它应该可以工作。我看不出这里有什么问题。
编辑
随着我的测试的进行,事情变得非常奇怪。
通过调用与
test.php
完全相同的 root
文件并从 cli 调用为
env -i php -c /usr/local/etc/php.ini -d scan.dir=/usr/local/etc/php /usr/local/www/test.php
成功,证书已发送,绑定成功。
通过从我的网络服务器(
nginx/1.24.0
)到达这个完全相同的文件,它失败了。
我的日志很清楚:
/var/run/certs/f03dd1db.0
/var/run/certs/229c13f4.crt
/var/run/certs/229c13f4.key
这两种情况下的所有这三个文件都可以正确读取(并从我的
debug.log
写到我的test.php
)
ldap_connect
似乎在两种情况下都可以工作,但是ldap_bind
仅在cli env -i php -c /usr/local/etc/php.ini -d scan.dir=/usr/local/etc/php /usr/local/www/test.php
中调用时才起作用,但从我的nginx网络服务器访问时则不起作用。
要与 cli 中的 nginx 环境一样接近(通过从
phpinfo()
调用 test.php
获取信息,并在从 nginx 到达时检查值):
env -i <command>
-c /usr/local/etc/php.ini
-d scan.dir=/usr/local/etc/php
仍然在 cli 中工作,而不是从我的网络服务器中工作。
编辑2
为了获取信息,我使用
Nginx + PHP-FPM
。
这是我非常简单的 nginx 配置:
#
# nginx configuration file
user root wheel;
error_log /var/log/nginx/error.log;
events {
worker_connections 1024;
}
http {
include /usr/local/etc/nginx/mime.types;
default_type application/octet-stream;
access_log /var/log/nginx/access.log;
server {
listen 80;
listen [::]:80;
root "/usr/local/www/";
location ~ \.php$ {
fastcgi_pass unix:/var/run/php-fpm.socket;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param HTTP_PROXY "";
fastcgi_read_timeout 180;
include /usr/local/etc/nginx/fastcgi_params_dummy;
}
}
}
我的 fastcgi_params_dummy 是这样的:
fastcgi_param QUERY_STRING $query_string;
fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param CONTENT_TYPE $content_type;
fastcgi_param CONTENT_LENGTH $content_length;
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
fastcgi_param REQUEST_URI $request_uri;
fastcgi_param DOCUMENT_URI $document_uri;
fastcgi_param DOCUMENT_ROOT $document_root;
fastcgi_param SERVER_PROTOCOL $server_protocol;
fastcgi_param REQUEST_SCHEME $scheme;
fastcgi_param HTTPS $https if_not_empty;
fastcgi_param GATEWAY_INTERFACE CGI/1.1;
fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;
fastcgi_param REMOTE_ADDR $remote_addr;
fastcgi_param REMOTE_PORT $remote_port;
fastcgi_param SERVER_ADDR $server_addr;
fastcgi_param SERVER_PORT $server_port;
fastcgi_param SERVER_NAME $server_name;
# PHP only, required if PHP was built with --enable-force-cgi-redirect
fastcgi_param REDIRECT_STATUS 200;
我尝试通过添加这些行来对值进行硬编码,但它并没有改变结果。
fastcgi_param LDAPTLS_CERT /var/run/certs/229c13f4.crt;
fastcgi_param LDAPTLS_KEY /var/run/certs/229c13f4.key;
fastcgi_param LDAPTLS_CACERT /var/run/certs/f03dd1db.0;
对于运行 Nginx + PHP-FPM 的用户: 解决方案是重新启动 php-fpm 使其工作:
/bin/pkill -F /var/run/php-fpm.pid
随后是 /usr/local/sbin/php-fpm -c /usr/local/etc/php.ini -y /usr/local/lib/php-fpm.conf -RD 2>&1 >/dev/null