How to set up Kerberos with OpenLDAP backend

Kerberos supports a few different database backends. The default one (which we have used in our other Kerberos guides so far) is called db2. The DB types documentation shows all the options, one of which is LDAP.

Why use LDAP?

There are several reasons why one would want to have the Kerberos principals stored in LDAP as opposed to a local on-disk database. There are also cases when it is not a good idea. Each site has to evaluate the pros and cons. Here are a few:

  • Pros:
    • OpenLDAP replication is faster and more robust than the native Kerberos one, based on a cron job
    • If you already have OpenLDAP set up for other things, such as storing users and groups, adding the Kerberos attributes can be beneficial, providing an integrated story
  • Cons:
    • Setting up the LDAP backend isn’t a trivial task and shouldn’t be attempted by administrators without prior knowledge of OpenLDAP
    • As highlighted in the LDAP section of DB types, since krb5kdc is single-threaded there may be higher latency in servicing requests when using the OpenLDAP backend

In this guide

In this section we’ll configure a primary and secondary Kerberos server to use OpenLDAP for the principal database. Note that as of version 1.18, the Key Distribution Center (KDC) from MIT Kerberos does not support a primary KDC using a read-only consumer (secondary) LDAP server. What we have to consider here is that a primary KDC is read-write, and it needs a read-write backend. The secondary KDCs can use both a read-write and read-only backend, because they are expected to be read-only. Therefore there are only some possible layouts we can use:

  1. Simple case:
    • Primary KDC connected to primary OpenLDAP
    • Secondary KDC connected to both primary and secondary OpenLDAP
  2. Extended simple case:
    • Multiple primary KDCs connected to one primary OpenLDAP
    • Multiple secondary KDCs connected to primary and secondary OpenLDAP
  3. OpenLDAP with multi-master replication:
    • Multiple primary KDCs connected to all primary OpenLDAP servers

We haven’t covered OpenLDAP multi-master replication in this guide, so we will show the simple case only. The second scenario is an extension: just add another primary KDC to the mix, talking to the same primary OpenLDAP server.

Configure OpenLDAP

We are going to install the OpenLDAP server on the same host as the KDC, to simplify the communication between them. In such a setup, we can use the ldapi:/// transport, which is via a UNIX socket, and we don’t need to set up SSL certificates to secure the communication between the Kerberos services and OpenLDAP. Note, however, that SSL is still needed for the OpenLDAP replication. See LDAP with TLS for details.

If you want to use an existing OpenLDAP server, that’s also possible, but keep in mind that you should then use SSL for the communication between the KDC and this OpenLDAP server.

First, the necessary schema needs to be loaded on an OpenLDAP server that has network connectivity to both the primary and secondary KDCs. The rest of this section assumes that you also have LDAP replication configured between at least two servers. For information on setting up OpenLDAP see OpenLDAP Server.

Note:
cn=admin,dc=example,dc=com is a default admin user that is created during the installation of the slapd package (the OpenLDAP server). The domain component will change for your server, so adjust accordingly.

  • Install the necessary packages (it’s assumed that OpenLDAP is already installed):

    sudo apt install krb5-kdc-ldap krb5-admin-server
    
  • Next, extract the kerberos.schema.gz file:

    sudo cp /usr/share/doc/krb5-kdc-ldap/kerberos.schema.gz /etc/ldap/schema/
    sudo gunzip /etc/ldap/schema/kerberos.schema.gz
    
  • The Kerberos schema needs to be added to the cn=config tree. This schema file needs to be converted to LDIF format before it can be added. For that we will use a helper tool, called schema2ldif, provided by the package of the same name which is available in the Universe archive:

    sudo apt install schema2ldif
    
  • To import the Kerberos schema, run:

    $ sudo ldap-schema-manager -i kerberos.schema
    SASL/EXTERNAL authentication started
    SASL username: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
    SASL SSF: 0
    executing 'ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/ldap/schema/kerberos.ldif'
    SASL/EXTERNAL authentication started
    SASL username: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
    SASL SSF: 0
    adding new entry "cn=kerberos,cn=schema,cn=config"
    
  • With the new schema loaded, let’s index an attribute often used in searches:

    $ sudo ldapmodify -Q -Y EXTERNAL -H ldapi:/// <<EOF
    dn: olcDatabase={1}mdb,cn=config
    add: olcDbIndex
    olcDbIndex: krbPrincipalName eq,pres,sub
    EOF
    
    modifying entry "olcDatabase={1}mdb,cn=config"
    
  • Let’s create LDAP entries for the Kerberos administrative entities that will contact the OpenLDAP server to perform operations. There are two:

    • ldap_kdc_dn: needs to have read rights on the realm container, principal container and realm sub-trees. If disable_last_success and disable_lockout are not set, however, then ldap_kdc_dn needs write access to the Kerberos container just like the admin DN below.
    • ldap_kadmind_dn: needs to have read and write rights on the realm container, principal container and realm sub-trees

    Here is the command to create these entities:

    $ ldapadd -x -D cn=admin,dc=example,dc=com -W <<EOF
    dn: uid=kdc-service,dc=example,dc=com
    uid: kdc-service
    objectClass: account
    objectClass: simpleSecurityObject
    userPassword: {CRYPT}x
    description: Account used for the Kerberos KDC
    
    dn: uid=kadmin-service,dc=example,dc=com
    uid: kadmin-service
    objectClass: account
    objectClass: simpleSecurityObject
    userPassword: {CRYPT}x
    description: Account used for the Kerberos Admin server
    EOF
    Enter LDAP Password: 
    adding new entry "uid=kdc-service,dc=example,dc=com"
    
    adding new entry "uid=kadmin-service,dc=example,dc=com"
    

    Now let’s set a password for them. Note that first the tool asks for the password you want for the specified user DN, and then for the password of the cn=admin DN:

    $ ldappasswd -x -D cn=admin,dc=example,dc=com -W -S uid=kdc-service,dc=example,dc=com
    New password:   <-- password you want for uid-kdc-service 
    Re-enter new password: 
    Enter LDAP Password:  <-- password for the dn specified with the -D option
    

    Repeat for the uid=kadmin-service dn. These passwords will be needed later.

    You can test these with ldapwhoami:

    $ ldapwhoami -x -D uid=kdc-service,dc=example,dc=com -W
    Enter LDAP Password: 
    dn:uid=kdc-service,dc=example,dc=com
    
  • Finally, update the Access Control Lists (ACL). These can be tricky, as it highly depends on what you have defined already. By default, the slapd package configures your database with the following ACLs:

    olcAccess: {0}to attrs=userPassword by self write by anonymous auth by * none
    olcAccess: {1}to attrs=shadowLastChange by self write by * read
    olcAccess: {2}to * by * read
    

    We need to insert new rules before the final to * by * read one, to control access to the Kerberos related entries and attributes:

    $ sudo ldapmodify -Q -Y EXTERNAL -H ldapi:/// <<EOF
    dn: olcDatabase={1}mdb,cn=config
    add: olcAccess
    olcAccess: {2}to attrs=krbPrincipalKey
      by anonymous auth
      by dn.exact="uid=kdc-service,dc=example,dc=com" read
      by dn.exact="uid=kadmin-service,dc=example,dc=com" write
      by self write
      by * none
    -
    add: olcAccess
    olcAccess: {3}to dn.subtree="cn=krbContainer,dc=example,dc=com"
      by dn.exact="uid=kdc-service,dc=example,dc=com" read
      by dn.exact="uid=kadmin-service,dc=example,dc=com" write
      by * none
    EOF
    
    modifying entry "olcDatabase={1}mdb,cn=config"
    
    This will make the existing {2} rule become {4}. Check with sudo slapcat -b cn=config (the output below was reformatted a bit for clarity):
    
    olcAccess: {0}to attrs=userPassword
        by self write
        by anonymous auth
        by * none
    olcAccess: {1}to attrs=shadowLastChange
        by self write
        by * read
    olcAccess: {2}to attrs=krbPrincipalKey by anonymous auth
    	by dn.exact="uid=kdc-service,dc=example,dc=com" read
    	by dn.exact="uid=kadmin-service,dc=example,dc=com" write
    	by self write
    	by * none
    olcAccess: {3}to dn.subtree="cn=krbContainer,dc=example,dc=com"
    	by dn.exact="uid=kdc-service,dc=example,dc=com" read
    	by dn.exact="uid=kadmin-service,dc=example,dc=com" write
    	by * none
    olcAccess: {4}to * by * read
    
    

Your LDAP directory is now ready to serve as a Kerberos principal database.

Primary KDC configuration (LDAP)

With OpenLDAP configured it is time to configure the KDC. In this example we are doing it in the same OpenLDAP server to take advantage of local UNIX socket communication.

  • Reconfigure the krb5-config package if needed to get a good starting point with /etc/krb5.conf:

    sudo dpkg-reconfigure krb5-config
    
  • Now edit /etc/krb5.conf adding the database_module option to the EXAMPLE.COM realm section:

    [realms]
            EXAMPLE.COM = {
                    kdc = kdc01.example.com
                    kdc = kdc02.example.com
                    admin_server = kdc01.example.com
                    default_domain = example.com
                    database_module = openldap_ldapconf
            }
    

    Then also add these new sections:

    [dbdefaults]
            ldap_kerberos_container_dn = cn=krbContainer,dc=example,dc=com
    
    [dbmodules]
            openldap_ldapconf = {
                    db_library = kldap
    
    				# if either of these is false, then the ldap_kdc_dn needs to
    				# have write access
    				disable_last_success = true
    				disable_lockout  = true
    
                    # this object needs to have read rights on
                    # the realm container, principal container and realm sub-trees
                    ldap_kdc_dn = "uid=kdc-service,dc=example,dc=com"
    
                    # this object needs to have read and write rights on
                    # the realm container, principal container and realm sub-trees
                    ldap_kadmind_dn = "uid=kadmin-service,dc=example,dc=com"
    
                    ldap_service_password_file = /etc/krb5kdc/service.keyfile
                    ldap_servers = ldapi:///
                    ldap_conns_per_server = 5
            }
    
  • Next, use the kdb5_ldap_util utility to create the realm:

    $ sudo kdb5_ldap_util -D cn=admin,dc=example,dc=com create -subtrees dc=example,dc=com -r EXAMPLE.COM -s -H ldapi:///
    Password for "cn=admin,dc=example,dc=com": 
    Initializing database for realm 'EXAMPLE.COM'
    You will be prompted for the database Master Password.
    It is important that you NOT FORGET this password.
    Enter KDC database master key: 
    Re-enter KDC database master key to verify: 
    
  • Create a stash of the password used to bind to the LDAP server. Run it once for each ldap_kdc_dn and ldap_kadmin_dn:

    sudo kdb5_ldap_util -D cn=admin,dc=example,dc=com stashsrvpw -f /etc/krb5kdc/service.keyfile uid=kdc-service,dc=example,dc=com
    sudo kdb5_ldap_util -D cn=admin,dc=example,dc=com stashsrvpw -f /etc/krb5kdc/service.keyfile uid=kadmin-service,dc=example,dc=com
    

    Note:
    The /etc/krb5kdc/service.keyfile file now contains clear text versions of the passwords used by the KDC to contact the LDAP server!

  • Create a /etc/krb5kdc/kadm5.acl file for the admin server, if you haven’t already:

    */admin@EXAMPLE.COM        *
    
  • Start the Kerberos KDC and admin server:

    sudo systemctl start krb5-kdc.service krb5-admin-server.service
    

You can now add Kerberos principals to the LDAP database, and they will be copied to any other LDAP servers configured for replication. To add a principal using the kadmin.local utility enter:

$ sudo kadmin.local
Authenticating as principal root/admin@EXAMPLE.COM with password.
kadmin.local:  addprinc ubuntu
WARNING: no policy specified for ubuntu@EXAMPLE.COM; defaulting to no policy
Enter password for principal "ubuntu@EXAMPLE.COM": 
Re-enter password for principal "ubuntu@EXAMPLE.COM": 
Principal "ubuntu@EXAMPLE.COM" created.
kadmin.local:  

The above will create an ubuntu principal with a DN of krbPrincipalName=ubuntu@EXAMPLE.COM,cn=EXAMPLE.COM,cn=krbContainer,dc=example,dc=com.

Let’s say, however, that you already have a user in your directory, and it’s in uid=testuser1,ou=People,dc=example,dc=com. How can you add the Kerberos attributes to it? You use the -x parameter to specify the location. For the ldap_kadmin_dn to be able to write to it, we first need to update the ACLs:

$ sudo ldapmodify -Q -Y EXTERNAL -H ldapi:/// <<EOF
dn: olcDatabase={1}mdb,cn=config
add: olcAccess
olcAccess: {4}to dn.subtree=“ou=People,dc=example,dc=com”
    by dn.exact=”uid=kdc-service,dc=example,dc=com” read
    by dn.exact=”uid=kadmin-service,dc=example,dc=com” write
    by * break
EOF

And now we can specify the new location:

$ sudo kadmin.local
Authenticating as principal root/admin@EXAMPLE.COM with password.
kadmin.local:  addprinc -x dn=uid=testuser1,ou=People,dc=example,dc=com testuser1
WARNING: no policy specified for testuser1@EXAMPLE.COM; defaulting to no policy
Enter password for principal "testuser1@EXAMPLE.COM": 
Re-enter password for principal "testuser1@EXAMPLE.COM": 
Principal "testuser1@EXAMPLE.COM" created.

Since the specified DN already exists, kadmin.local will just add the required Kerberos attributes to this existing entry. If it didn’t exist, it would be created from scratch, with only the Kerberos attributes, just like what happened with the ubuntu example above, but in the specified location.

Note:
The ldap_kadmin_dn DN (uid=kadmin-service in our example) does not have write access to the location specified by the -x parameter, you will get an Insufficient access error.

Both places are visible for kinit, since, when the realm was created with kdb5_ldap_util, the default value for the search scope and base were taken: subtree, and dc=example,dc=com.

Secondary KDC configuration (LDAP)

The setup of the secondary KDC (and its OpenLDAP replica) is very similar. Once you have the OpenLDAP replication setup, repeat these steps on the secondary:

  • Install krb5-kdc-ldap, ldap-utils. Do not install krb5-admin-server.
  • Load the Kerberos schema using schema2ldif.
  • Add the index for krbPrincipalName.
  • Add the ACLs.
  • Configure krb5.conf in the same way, initially. If you want to, and if you configured SSL properly, you can add ldaps://kdc01.example.com to the ldap_servers list after ldapi:///, so that the secondary KDC can have two LDAP backends at its disposal.
  • DO NOT run kdb5_ldap_util. There is no need to create the database since it’s being replicated from the primary.
  • Copy over the following files from the primary KDC and place them in the same location on the secondary:
    • /etc/krb5kdc/stash
    • /etc/krb5kdc/service.keyfile
  • Start the KDC: sudo systemctl start krb5-kdc.service

Resources

1 Like

This section contains a small but annoying typo:

olcAccess: {3}to dn.subtree=“cn=krbContainer,dc=example,dc=com”
by dn.exact=“uid=kdc-service,dc=example,dc=com” read
by dn.exact=“uid=kadmin-service,dc=example,dc=com” write
by * none

Should be to dn.subtree=“cn=krb5Container …”

other than that … great guide !

Evert

Sorry, I don’t see the entry with krb5Container created, it’s krbContainer. Although I’m testing with MIT kerberos 1.19.2 now, in Jammy, but I’m not aware of something like this having changed.

1 Like

I followed the guide and destroyed the whole setup about 3 times thinking I was not doing something right until I found the note on this online guide:

Note
The ldap_kadmin_dn DN (uid=kadmin-service in our example) needs to have write access to the location specified by the -x parameter, otherwise you will get an Insufficient access error.

It took me a while to figure out what this note meant in terms of a practical procedure…
Here is what was missing in the note:

Give write access to kadmin-service to location by -x parameter:
Run command as follows (adjust as required):

$ sudo ldapmodify -Q -Y EXTERNAL -H ldapi:/// <<EOF
dn: olcDatabase={1}mdb,cn=config
add: olcAccess
olcAccess: {4}to dn.subtree=“ou=People,dc=example,dc=com”
by dn.exact=”uid=kdc-service,dc=example,dc=com” read
by dn.exact=”uid=kadmin-service,dc=example,dc=com” write
by * none
EOF

1 Like

Thanks, I will update the note

I just changed your last by * none to by * break, or else no-one else would be able to read the ou=People subtree. The kerberos attributes (like keys) should still be protected by previous ACLs, can you do a quick check if you still have your setup up and running?

1 Like

Ahasenack, thank you! Will try and let you know.

Hello Ahasenack,

I want to thank you for that change (’* break’). That change alone fixed so many things…
I was being able to add -x principals, but SSSD was not working. I spent lots of time trying to fix it, but once I made the change you added, everything now works as it is supposed, and finally I can login users with no problem. Thank you thank you thank you.

I’m glad it worked, this is quite a complicated setup (kerberos + ldap + sssd)

2 Likes

@ahasenack, In my attempt to install Kerberos, I’ve found out that “ldap_kerberos_container_dn” inside “[libdefaults]” doesn’t work with the latest version of krb5kdc on ubuntu 22.04. I managed to make it work by placin inside “openldap_ldapconf”. Otherwise, krb5kdc wouldn’t start.
Also, when configuring this part, I updated “/etc/krb5kdc/kdc.conf” to make kdc work, I’m not sure if it’s a required step or only “/etc/krb5.conf” needs to be edited.

Upon a second installation on another system, it seems that “/etc/krb5kdc/kdc.conf” doesn’t need to be modified.
However, it looks like “ldap_kerberos_container_dn” does need to be included inside “openldap_ldapconf”.

Thanks for trying out the steps from this guide, I’ll take a look

1 Like

I think you experienced a different issue. I just followed the guide again, and I have ldap_kerberos_container_dn inside [dbdefaults] in /etc/krb5.conf.
Previously I did have issues with starting the kdc, but that was because I “converted” a normal kdc to kdc+openldap, and forgot to update kdc.conf. In particular, my stash file was named differently.
That being said, if you still follow the guide and have an issue with starting the kdc, please post the error message from the logs, and we can go from there.

1 Like

I’m having a weird issue following this. I have an openldap server all set up, I have accounts, passwords validate against other services like nextcloud, etc. I am following this, I made the kdc-service and kadmin-service accounts…I can’t validate the passwords I set. One thing I noticed, for the working accounts, userPassword is “MD5 Hashed Password” but for the script in this guide, it ends up being a “SSHA Hashed Password”. I the logs aren’t shedding any light, just “invalid password” when I try to do the “ldapwhoami” despite the password changing fine (using “ldappasswd” or changing it in Apache Directory Studio). I’m not even sure how else to debug, any advice is appreciated.

Is your database in mdb format? Check in the output of slapcat -b cn=config, see what you have for your existing olcDatabase entry.

Hello, thanks for answering. Tomorrow I review the notice since today I do not have access to my work server, but I am following the step by step

Oh, I think I know. Looks like there are missing leading spaces in the section inside the EOF markers. These mark the lines as being a continuation of the previous line.

It should be like this:

$ sudo ldapmodify -Q -Y EXTERNAL -H ldapi:/// <<EOF
dn: olcDatabase={1}mdb,cn=config
add: olcAccess
olcAccess: {4}to dn.subtree=“ou=People,dc=example,dc=com”
  by dn.exact=”uid=kdc-service,dc=example,dc=com” read
  by dn.exact=”uid=kadmin-service,dc=example,dc=com” write
  by * break
EOF

Note the leading spaces in the by dn.exact=... lines. See if that works, and I’ll update the guide accordingly.

@ahasenack ok now i get this error

This puzzles me a bit, the error 80 is quite generic. Maybe if you check the slapd logs it will tell you a bit more. See the “Logging” section of https://ubuntu.com/server/docs/service-ldap for more details.

Other than that, I see you have successfully adjusted the ACLs before, given your {3} entry from the earlier comment, so that’s good.

Do you have the ou=People,dc=test=dc=cvg,dc=com entry in your LDAP server? Specifically the ou=People bit?

Oh, and please change your LDAP olcRootPW password, as you have exposed it in a screenshot earlier.