DirectoryTree / LdapRecord

A fully-featured LDAP framework.
https://ldaprecord.com
MIT License
511 stars 44 forks source link

[Question] LDAPS with multiple certificates. #740

Open lazosweb opened 4 days ago

lazosweb commented 4 days ago

Environment:

Hello, we are looking for guidance. We are deploying a Web App as a container in Azure and we will be using the customer's LDAP (LDAPS, port 636) to authenticate the users. Customer has provided us with 2 PEM CA Certificates (chain certificates they call them).

According to PHP https://www.php.net/manual/en/function.ldap-set-option.php You can use LDAP_OPT_X_TLS_CACERTFILE for a single certificate or LDAP_OPT_X_TLS_CACERTDIR to load multiple certificates.

Have anyone ever tried LDAP_OPT_X_TLS_CACERTDIR and worked with more than one certificate?

Unecrypted LDAP works fine but LDAPS with the provided certificates from the customer all we get is "Cannot contact LDAP server".

We suspect there is an issue with the provided certificates but how can we debug this to identify that the issue is indeed with the provided certificates?

In-house LDAPS with one certificate and LDAP_OPT_X_TLS_CACERTFILE success and we had another customer that provided only one certificate and we did not have an issue...

stevebauman commented 4 days ago

Hi @lazosweb,

If you run php artisan ldap:test, a detailed error (including an error + diagnostic code) should be printed in the message column. For example:

+------------+------------+--------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------+
| Connection | Successful | Username                 | Message                                                                                                                                                                                                               | Response Time |
+------------+------------+--------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------+
| default    | ✘ No       | cn=admin,dc=local,dc=com | ldap_bind_ext(): Unable to bind to server: Can't contact LDAP server (-1). Error Code: [-1] Diagnostic Message: error:0A000086:SSL routines::certificate verify failed (self-signed certificate in certificate chain) | 95.72ms       |
+------------+------------+--------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------+
lazosweb commented 4 days ago

Hi @lazosweb,

If you run php artisan ldap:test, a detailed error (including an error + diagnostic code) should be printed in the message column. For example:

+------------+------------+--------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------+
| Connection | Successful | Username                 | Message                                                                                                                                                                                                               | Response Time |
+------------+------------+--------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------+
| default    | ✘ No       | cn=admin,dc=local,dc=com | ldap_bind_ext(): Unable to bind to server: Can't contact LDAP server (-1). Error Code: [-1] Diagnostic Message: error:0A000086:SSL routines::certificate verify failed (self-signed certificate in certificate chain) | 95.72ms       |
+------------+------------+--------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------+

Hi @stevebauman So, we ended up writing a custom LDAP connect script with debug 7 and we spotted the issue and sorted it out. The weird thing is that we have been using the ldap:test command and the only thing we could see is the "can't contact LDAP server" message and I was discussing this with a colleague why the artisan test command won't show the TLS errors. I do not doubt you but for some reason we could not see the TLS errors or where not obvious inside the container shell.

stevebauman commented 3 days ago

Thanks for the update @lazosweb. If you add the debug 7 into your connection options, are you able to see the TLS error?

For example:

// config/ldap.php

return [

    // ...

    'connections' => [

        'default' => [
            // ...
            'options' => [
                LDAP_OPT_DEBUG_LEVEL => 7,
            ],
        ],

    ],

    // ...
];

Then run:

php artisan ldap:test

If you're still having this issue with or without the option, would you be able to share your script you used so that I could resolve this for you and others who encounter this in the future?

lazosweb commented 3 days ago

@stevebauman Yes you are right. Adding the debug level in the options you can see the details.

But still in your previous comments the SSL error messages should appear inside the diagnostic message but they do not.

ldap_bind_ext(): Unable to bind to server: Can't contact LDAP server (-1). Error Code: [-1] Diagnostic Message: NULL

See the outputs.

Without DEBUG LEVEL 7

root@smartclear_00171723f9:/var/www/html# php artisan ldap:test   
Testing LDAP connection [default]...
+------------+------------+----------------------------------------------+----------------------------------------------------------------------------------------------------------------------+---------------+
| Connection | Successful | Username                                     | Message                                                                                                              | Response Time |
+------------+------------+----------------------------------------------+----------------------------------------------------------------------------------------------------------------------+---------------+
| default    | ✘ No       | CN=Ldap User,OU=SmartClear,DC=example,DC=org | ldap_bind_ext(): Unable to bind to server: Can't contact LDAP server (-1). Error Code: [-1] Diagnostic Message: NULL | 659.86ms      |
+------------+------------+----------------------------------------------+----------------------------------------------------------------------------------------------------------------------+---------------+

With DEBUG LEVEL 7

root@smartclear_00171723f9:/var/www/html# php artisan ldap:test
Testing LDAP connection [default]...
ldap_create
ldap_url_parse_ext(ldaps://adds-srv.example.org:636)
ldap_sasl_bind
ldap_send_initial_request
ldap_new_connection 1 1 0
ldap_int_open_connection
ldap_connect_to_host: TCP adds-srv.example.org:636
ldap_new_socket: 5
ldap_prepare_socket: 5
ldap_connect_to_host: Trying xxx.xxx.xxx.xxx:636
ldap_pvt_connect: fd: 5 tm: 5 async: 0
ldap_ndelay_on: 5
attempting to connect: 
connect errno: 115
ldap_int_poll: fd: 5 tm: 5
ldap_is_sock_ready: 5
ldap_ndelay_off: 5
ldap_pvt_connect: 0
TLS: could not use CA certificate file `/var/www/html/storage/logs/certs/ldap_ca_certificate.pem': Error while reading file. (-64)
ldap_err2string
ldap_err2string
ldap_err2string
+------------+------------+----------------------------------------------+----------------------------------------------------------------------------------------------------------------------+---------------+
| Connection | Successful | Username                                     | Message                                                                                                              | Response Time |
+------------+------------+----------------------------------------------+----------------------------------------------------------------------------------------------------------------------+---------------+
| default    | ✘ No       | CN=Ldap User,OU=SmartClear,DC=example,DC=org | ldap_bind_ext(): Unable to bind to server: Can't contact LDAP server (-1). Error Code: [-1] Diagnostic Message: NULL | 309.04ms      |
+------------+------------+----------------------------------------------+----------------------------------------------------------------------------------------------------------------------+---------------+

The above is an example. The issue we had with our customer is that we have been careless with the LDAP host(s). Customer provided us with 5 Domain Controllers like this: DC01 headoffice.example.com DC02 headoffice.example.com DC03 headoffice.example.com DC04 headoffice.example.com DC05 headoffice.example.com

In the LDAP_HOST environment variable we only entered headoffice.example.com and ldap:test was failing all the time and we had no clue why for 3 days when the unencrypted version was working just fine with that host. When we run our custom script we saw the TLS error that the host does not match the common name "DC01.headoffice.example.com" of the certificate and we realized immediately our mistake that instead of the just headoffice.example.com we had to enter dc01.headoffice.example.com instead...

I hope this will help somebody else one day :)

stevebauman commented 3 days ago

Thanks so much for the detailed reply here @lazosweb! ❤️ This will definitely help others out in the future, and help me improve the debug documentation.

I'm not sure if I'll be able to change the location of the logs that are printed from the LDAP debugging (as they are printed from the LDAP PHP extension directly into stdout), but I'll give it a shot.

I'll close this issue once I've update the LdapRecord documentation with all this information to assist others in the future. Thanks again! 🙏

lazosweb commented 3 days ago

Hi @stevebauman. Definitely update the documentation.

I do not think you need to try and catch the SSL/TLS errors. I think your artisan ldap:test command should always include DEBUG LEVEL 7 or add an argument/option in the command if the user would like to output the full debug log like the "with DEBUG LEVEL 7" example I posted.

Regarding LDAP_OPT_X_TLS_CACERTDIR but this take it with a pinch of salt. The CACERTDIR might not work on Windows but it works on Linux.

Also if your customer provides you with more than one certificate you might be able to stack them up into one certificate file and use LDAP_OPT_X_TLS_CACERTFILE. Also this one take it with a pinch of salt. It worked for us but we are not certificate experts.

Thanks for all the hard work you have put to this amazing package. We have been using your packages since Adldap2-Laravel :D