PostgreSQL LDAP client authentication implementation

Preamble

Is PostgreSQL LDAP client authentication easy to implement ? I have already tried it for multiple database flavors and this article will greatly benefit from all the issues I have resolved while implementing LDAP client authentication for MariaDB.

I have tested it with PostgreSQL 15.2 running on Oracle Linux Server release 8.7. The LDAP server I will use is my corporate LDAP server where I have already my short login account. The big picture is:

postgresql_ldap01
postgresql_ldap01

PostgreSQL LDAP client authentication implementation

I initially had the insecure pg_hba.conf configuration for my test account on my PostgreSQL instance:

host    all             yjaquier             0.0.0.0/0            trust

Which I modified for the most secure request password option:

host    all             yjaquier             0.0.0.0/0            scram-sha-256

User created with, of course you must create the user on the instance first:

postgres=# create role yjaquier with superuser login password 'secure_password';
CREATE ROLE

To test the password request use (first trial I deliberately used a wrong password):

postgres=# SELECT pg_reload_conf();
 pg_reload_conf
----------------
 t
(1 row)
 
postgres=# \q
[postgres@server1 ~]$ /usr/pgsql-15/bin/psql --port=5433 --host=server1.domain.com --username=yjaquier --dbname=postgres
Password for user yjaquier:
psql: error: connection to server at "server1.domain.com" (192.168.56.101), port 5433 failed: FATAL:  password authentication failed for user "yjaquier"
connection to server at "server1.domain.com" (192.168.56.101), port 5433 failed: FATAL:  password authentication failed for user "yjaquier"
[postgres@server1 ~]$ /usr/pgsql-15/bin/psql --port=5433 --host=server1.domain.com --username=yjaquier --dbname=postgres
Password for user yjaquier:
psql (15.2)
Type "help" for help.
 
postgres=#

Unlike MariaDB, with PostgreSQL the LDAP client authentication is directly implemented in the engine so I did only modify my pg_hba.conf file to change the authentication method for my own account with:

host    all             yjaquier             0.0.0.0/0            ldap ldapserver=ldap-server.domain.com ldapscheme=ldaps ldapbasedn="ou=employee,dc=company,dc=com" ldapsearchattribute=uid

I tried to authenticate and got the famous certificate error:

2023-03-02 11:24:33.093 CET [30928] LOG:  could not perform initial LDAP bind for ldapbinddn "" on server "ldap-server.domain.com": Can't contact LDAP server
2023-03-02 11:24:33.093 CET [30928] DETAIL:  LDAP diagnostics: error:14090086:SSL routines:ssl3_get_server_certificate:certificate verify failed (self signed certificate in certificate chain)
2023-03-02 11:24:33.093 CET [30928] FATAL:  LDAP authentication failed for user "yjaquier"
2023-03-02 11:24:33.093 CET [30928] DETAIL:  Connection matched pg_hba.conf line 94: "host    all             yjaquier             0.0.0.0/0            ldap    ldapserver=ldap-server.domain.com ldapscheme=ldaps ldapbasedn="ou=employee,dc=company,dc=com" ldapsearchattribute=uid"

Or if you want to test it with the Open LDAP client (install openldap-clients package to be able to use it):

[postgres@server1 ~]$ ldapsearch -H "ldaps://ldap-server.domain.com/" -b "ou=employee,dc=company,dc=com" -s sub "(uid=yjaquier)"
ldap_sasl_interactive_bind_s: Can't contact LDAP server (-1)
        additional info: error:14090086:SSL routines:ssl3_get_server_certificate:certificate verify failed (self signed certificate in certificate chain)

I have done the exact same certificate setup that we have already seen with MariaDB(link)… First extracted the certification chain of my corporate LDAP server with:

[root@eserver1 ~]# cd /etc/openldap/certs
[root@server1 certs]# openssl s_client -showcerts -verify 5 -connect ldap-server.domain.com:636  < /dev/null | awk '/BEGIN/,/END/{ if(/BEGIN/)    {a++}; out="ldap-server-cert"a".pem"; print >out}'
verify depth is 5
depth=2 C = CH, O = STMicroelectronics International N.V., OU = Corporate Services, CN = STMicroelectronics Private Corporate Root CA1
verify error:num=19:self signed certificate in certificate chain
verify return:1
depth=2 C = CH, O = STMicroelectronics International N.V., OU = Corporate Services, CN = STMicroelectronics Private Corporate Root CA1
verify return:1
depth=1 C = CH, O = STMicroelectronics International N.V., OU = Corporate Services, CN = STMicroelectronics Corporate Server CA1
verify return:1
depth=0 CN = server05.domain.com, OU = DIT, O = STMicroelectronics International NV
verify return:1
DONE

Added my company Certificate Authority files to my PostgreSQL server:

[root@server1 certs]# cd /etc/pki/ca-trust/source/anchors
[root@server1 anchors]# ll
total 8
-rw-r--r-- 1 root root 2442 Mar  2 17:44 STMicroelectronicsCorporateServerCA1.cer
-rw-r--r-- 1 root root 2218 Mar  2 17:44 STMicroelectronicsPrivateCorporateRootCA1.cer
[root@server1 anchors]# update-ca-trust extract

Now my LDAP server certificates can be verified:

[root@server1 anchors]# for cert in /etc/openldap/certs/*.pem; do openssl verify -show_chain $cert ; done
/etc/openldap/certs/ldap-server-cert1.pem: OK
Chain:
depth=0: CN = server05.domain.com, OU = DIT, O = STMicroelectronics International NV (untrusted)
depth=1: C = CH, O = STMicroelectronics International N.V., OU = Corporate Services, CN = STMicroelectronics Corporate Server CA1
depth=2: C = CH, O = STMicroelectronics International N.V., OU = Corporate Services, CN = STMicroelectronics Private Corporate Root CA1
/etc/openldap/certs/ldap-server-cert2.pem: OK
Chain:
depth=0: C = CH, O = STMicroelectronics International N.V., OU = Corporate Services, CN = STMicroelectronics Corporate Server CA1 (untrusted)
depth=1: C = CH, O = STMicroelectronics International N.V., OU = Corporate Services, CN = STMicroelectronics Private Corporate Root CA1
/etc/openldap/certs/ldap-server-cert3.pem: OK
Chain:
depth=0: C = CH, O = STMicroelectronics International N.V., OU = Corporate Services, CN = STMicroelectronics Private Corporate Root CA1

And the LDAP search command is working:

[postgres@server1 ~]$ ldapsearch -x -H "ldaps://ldap-server.domain.com/" -b "ou=employee,dc=company,dc=com" -s sub "(uid=yjaquier)"
# extended LDIF
#
# LDAPv3
# base <ou=employee,dc=company,dc=com> with scope subtree
# filter: (uid=yjaquier)
# requesting: ALL
#
.
.

PostgreSQL LDAP client authentication testing

The testing is damn simple, with my correct LDAP password:

[postgres@server1 ~]$ /usr/pgsql-15/bin/psql --port=5433 --host=server1.domain.com --username=yjaquier --dbname=postgres
Password for user yjaquier:
psql (15.2)
Type "help" for help.
 
postgres=#

With the wrong password or even the password I have chosen while creating the account:

[postgres@server1 ~]$ /usr/pgsql-15/bin/psql --port=5433 --host=server1.domain.com --username=yjaquier --dbname=postgres
Password for user yjaquier:
psql: error: connection to server at "server1.domain.com" (192.168.56.101), port 5433 failed: FATAL:  LDAP authentication failed for user "yjaquier"

I have also tried to create a create a user not present in my central LDAP directory:

postgres=# create role toto with superuser login password 'secure_password';
CREATE ROLE

I have added in pg_hba.conf file:

host    all             toto             0.0.0.0/0            ldap ldapserver=ldap-server.domain.com ldapscheme=ldaps ldapbasedn="ou=employee,dc=company,dc=com" ldapsearchattribute=uid

Of course while connecting I don’t really know what to specify as a password but in PostgreSQL log file the message is interesting:

2023-03-03 00:06:50.635 CET [78738] LOG:  LDAP user "toto" does not exist
2023-03-03 00:06:50.635 CET [78738] DETAIL:  LDAP search for filter "(uid=toto)" on server "ldap-server.domain.com" returned no entries.
2023-03-03 00:06:50.636 CET [78738] FATAL:  LDAP authentication failed for user "toto"
2023-03-03 00:06:50.636 CET [78738] DETAIL:  Connection matched pg_hba.conf line 93: "host    all             toto             0.0.0.0/0            ldap ldapserver=ldap-server.domain.com ldapscheme=ldaps ldapbasedn="ou=employee,dc=company,dc=com" ldapsearchattribute=uid"

In other words a user that left the company cannot be used anymore in your database which, in real life, is not always true with locally created users…

References

About Post Author

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>