I want to achieve mTLS on dummy php webpage from a guest to my Openldap Server.
I have issue and it seems my php never sends client certificate to my server even then I specified the options accordingly. I have php 8.2
on my client guest and slapd
version for my openldap server
is in version 2.5.13
.
I enabled TLS and enforced mutual TLS (mTLS) on my server:
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
I can connect successfully with ldapsearch in csh with my client certificate from my client guest, so they are correct and valid:
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
However I can't make it work with my 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);
}
}
Client logs shows me:
LDAP bind failed: Can't contact LDAP server (-1):
Server logs shows me :
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)
However when I'm printing my $ldap options just before bind it seems everything is correct:
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
But somehow, my php obviously do something wrong. If I believe this user comment, from php manual, or this comment from github php project it should work. I can't see what's the issue here.
EDIT
As my tests are going on, it's getting super weird.
By calling the exact same test.php
file as root
and from cli as
env -i php -c /usr/local/etc/php.ini -d scan.dir=/usr/local/etc/php /usr/local/www/test.php
it works and certificates are sent, bind is successful.
By reaching this exact same file, from my webserver (nginx/1.24.0
) it fails.
My logs are clear :
/var/run/certs/f03dd1db.0
/var/run/certs/229c13f4.crt
/var/run/certs/229c13f4.key
All those three files in both case can be correctly read (and wrote to my debug.log
from my test.php
)
The ldap_connect
seems to work in both scenarios, but then ldap_bind
works only when called in cli env -i php -c /usr/local/etc/php.ini -d scan.dir=/usr/local/etc/php /usr/local/www/test.php
, but doesn't when reached from my nginx webserver.
To be as close as my nginx env in cli (Got info by calling phpinfo()
from test.php
and check values when reaching from nginx):
env -i <command>
-c /usr/local/etc/php.ini
-d scan.dir=/usr/local/etc/php
Still, work in cli, not from my webserver.
EDIT2
For information I use Nginx + PHP-FPM
.
Here is my very simple nginx conf:
#
# 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;
}
}
}
And my fastcgi_params_dummy to be like this:
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;
I tried to hardcode values here by adding those lines, it didn't change the outcome.
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;
For the ones running Nginx + PHP-FPM :
Solution was to restart php-fpm to make it work :
/bin/pkill -F /var/run/php-fpm.pid
followed by /usr/local/sbin/php-fpm -c /usr/local/etc/php.ini -y /usr/local/lib/php-fpm.conf -RD 2>&1 >/dev/null