OpenLDAP

From Leo's Notes
Last edited on 7 March 2022, at 07:28.

Cheat sheet

Some common flags that are used with all OpenLDAP commands

Flag Description
-D Bind DN
-w Password for bind DN
-H the LDAP URI. It's a URI, so the protocol (ldap:// or ldaps://) is required.
-Z Use StartTLS. -ZZ requires TLS
-L show in LDIF v1
-LL show LDIF without comments
-LLL print without ldif
-b the base DN (used when searching)

Most commands that you use will require the host (-H ldap://....), bind DN (-D cn=user,ou=...,dc=...), and the bind DN password (-w password).

Task Command
Changing a user's password ldappasswd -D $BIND_DN -w $BIND_DN_PASSWORD [-S] cn=leo,ou=users,dc=home,dc=steamr,dc=com
  • use -S to prompt.
Import a ldif file ldapadd -D $BIND_DN -w $BIND_DN_PASSWORD -f bootstrap.ldif
Search LDAP ldapsearch -LLL -D srv-arc -w $LDAP_PASSWORD -b $BASE_DN cn=leo uidNumber
  • -LLL for a non-ldif output
  • Search criteria here is cn=leo
  • Query is for uidNumber

Tasks

How to create a small OpenLDAP server in a container

In some cases where you need to spin up a small LDAP server for testing or 'temporary production' environments, the following docker-compose stack should fit the bill. This stack will utilize a bootstrap.ldif file to populate the LDAP directory. You may choose to keep this LDAP server without any state (useful for testing) by simply not mounting the data volumes. In such cases, a restart of the container will re-create all users and groups based on the bootstrap.ldif.

This docker-compose stack also includes an instance of PHPLdapAdmin, a web tool that lets you view and manage users and groups. You may remove this if desired by commenting out the lines in the docker-compose.yml file.

Create a docker-compose.yml file with the following contents. Adjust as necessary. Notice the admin LDAP account password is given as an environment variable.

version: '3.3'
services:
  ldap:
    image: osixia/openldap
    restart: always
    volumes:
      - ./config:/container/service/slapd/assets/config/bootstrap/ldif/custom
     # For persistent states, you may also want tot add these volumes
     # - ./data:/var/lib/ldap
     # - ./config:/etc/ldap/slapd.d
    environment:
      - "TZ=MST7MDT,M3.2.0,M11.1.0"
      - LDAP_ORGANISATION=Home
      - LDAP_DOMAIN=home.steamr.com
      - LDAP_ADMIN_PASSWORD=super-secret-password
	  - LDAP_READONLY_USER=true
	  - LDAP_TLS_VERIFY_CLIENT=try
    expose:
      - "389"
    ports:
      - "10.1.1.1:389:389"
    networks:
      - internal
    command: --copy-service

  phpldapadmin:
    image: osixia/phpldapadmin:latest
    container_name: phpldapadmin
    environment:
      PHPLDAPADMIN_LDAP_HOSTS: "ldap"
      PHPLDAPADMIN_HTTPS: "false"
    # If you are using traefik as a reverse proxy service...
    #labels:
    #  - traefik.enable=true ... etc
    #expose:
    #  - "80"
    # Otherwise, expose port 80.
    ports:
      - "10.1.1.1:80:80"
    depends_on:
      - ldap
    networks:
      - internal

networks:
  internal:

Keep in mind of the following environment variables and their effects:

Variable Description
LDAP_TLS_VERIFY_CLIENT Sets the TLS_REQCERT option in ldap.conf. For TLS using self signed certificates, you need to set this to try in order for authentication to work.
LDAP_READONLY_USER Enables or disables the creation of a read-only user during the bootstrap stage. This is useful for services that need to bind and do a search because anonymous binding is disabled. The account is called cn=readonly with readonly as the password. This can be overridden with LDAP_READONLY_USER_USERNAME and LDAP_READONLY_USER_PASSWORD.
LDAP_ADMIN_PASSWORD Sets the admin account password. The admin account is placed at the root of the directory. Eg. cn=admin,dc=example,dc=com

Next, create your bootstrap.ldif file. This file contains all the users and groups that should be set up when the container starts up. It does not take effect if there are existing data in the /var/lib/ldap location. The admin account can be left out from this file since it will be set up by the container entrypoint script based on the environment variables.

Here's an example file where I create my account in addition to one group.

dn: ou=groups,dc=home,dc=steamr,dc=com
objectclass: organizationalUnit
objectclass: top
ou: groups

dn: cn=downloaders,ou=groups,dc=home,dc=steamr,dc=com
cn: downloaders
gidnumber: 250
objectclass: posixGroup
objectclass: top

ou=users,dc=home,dc=steamr,dc=com
dn: ou=users,dc=home,dc=steamr,dc=com
objectclass: organizationalUnit
objectclass: top
ou: users


dn: cn=leo,ou=users,dc=home,dc=steamr,dc=com
cn: leo
displayname: Leo Leung
gidnumber: 500
givenname: leo
homedirectory: /home/leo
loginshell: /bin/bash
mail: leo@example.com
objectclass: top
objectclass: inetOrgPerson
objectclass: posixAccount
sn: Leung
uid: leo
uidnumber: 500
userpassword: asdfasdf

Bring everything up with docker-compose up -d.

The container will generate self signed certificates which you can get by running:

# docker exec -ti <container-id> cat /container/run/service/slapd/assets/certs/ca.crt
# docker exec -ti <container-id> cat /container/run/service/slapd/assets/certs/ldap.key 
# docker exec -ti <container-id> cat /container/run/service/slapd/assets/certs/ldap.crt

With the OpenLDAP server running, you can make a Linux system authenticate against it using SSSD. Follow the instructions on the System Security Services Daemon page.

Troubleshooting

Bootstrap ldif file is not taking effect

If your bootstrap.ldif file isn't taking effect, it most likely has some issues that's preventing it from being loaded. In such circumstance, it is best to enter the container and manually try loading the ldif file.

# docker exec -ti <docker container> bash
# ldapadd -Z -D cn=admin,dc=example,dc=com -w password -f /container/service/slapd/assets/config/bootstrap/ldif/custom/bootstrap.ldif
TLS connections to the server are failing

Symptoms include the following error from the OpenLDAP server:

60f4b878 conn=1149 fd=13 closed (TLS negotiation failure)
60f4b87a conn=1150 fd=13 ACCEPT from IP=10.1.3.169:42016 (IP=0.0.0.0:636)
TLS: can't accept: An unexpected TLS packet was received..

Try making the TLS connection use 'try' instead of the default 'hard'. Do so by specifying the LDAP_TLS_VERIFY_CLIENT=try environment variable. The client (sssd) should also have a copy of the TLS certificate

Samba integration

You can have Samba use LDAP as a password backend, allowing you to store samba account credentials on LDAP rather than a local SAM database. This can be useful if you have accounts with the same password across multiple Samba servers.

Alternatively, use Kerberos
An alternative to this approach would be to have Samba use Kerberos, but (correct me if I'm wrong) this doesn't allow users the ability to use authentication as the user's machine authenticates using an existing kerberos ticket.

Before you begin, ensure that your LDAP server has the Samba schemas installed. If you are using the osixia/openldap container image, this is already be bundled.

Edit smb.conf. Add the following lines under [global]:

[global]
workgroup = MYGROUP                           # Change me to your workgroup name
passdb backend = ldapsam:ldap://10.1.1.9      # Change me to your LDAP server
ldap suffix = dc=home,dc=steamr,dc=com
ldap admin dn = cn=admin,dc=home,dc=steamr,dc=com
ldap user suffix = ou=users
ldap machine suffix = ou=computers
ldap group suffix = ou=group
ldap passwd sync = yes
ldap ssl = no

Change the workgroup name and LDAP server and base DN as required. For this example, I will not be using TLS.

Save your admin DN credentials to the secrets database by running smbpasswd -w <admin-dn password>.

Restart samba. If samba fails to start, check the samba logs. If you didn't set the correct bind DN, the restart would fail. Check the samba logs for more details if this happens.

Get the local SID:

# net getlocalsid
SID for domain SERVER is: S-1-5-21-3808049139-2148245656-2735675580

Using this SID, import this entry into your LDAP server using ldapadd -x -D ... -x ... -f import.ldif:

dn: sambaDomainName=MYGROUP,dc=home,dc=steamr,dc=com
objectclass: sambaDomain
objectclass: sambaUnixIdPool
objectclass: top
sambaDomainName: MYGROUP
sambaSID: S-1-5-21-3808049139-2148245656-2735675580
uidNumber: 550
gidNumber: 550

Restart samba, then try to add a new user:

# smbpasswd -a leo
New SMB password:
Retype new SMB password:
Added user leo

When successful, you should see a bunch of new attributes for the user in the LDAP server.

Troubleshooting Samba

NT_STATUS_NO_MEMORY:
[2021/07/17 23:22:03.698217,  0] ../../source3/passdb/secrets.c:364(fetch_ldap_pw)
  fetch_ldap_pw: neither ldap secret retrieved!
[2021/07/17 23:22:03.698463,  0] ../../source3/passdb/pdb_ldap.c:6652(pdb_init_ldapsam_common)
  pdb_init_ldapsam_common: Failed to retrieve LDAP password from secrets.tdb
[2021/07/17 23:22:03.698488,  0] ../../source3/passdb/pdb_interface.c:180(make_pdb_method_name)
  pdb backend ldapsam:ldap://10.1.1.9 did not correctly init (error was NT_STATUS_NO_MEMORY)

This was caused by bad LDAP bind credentials. You need to set the bind credentials by setting the ldap admin dn setting in smb.conf and then running:

# smbpasswd -w password
Setting stored password for "uid=admin,cn=users,cn=accounts,dc=home,dc=steamr,dc=com" in secrets.tdb
NT_STATUS_CANT_ACCESS_DOMAIN_INFO:
[2021/07/17 23:53:29.901142,  0] ../../source3/lib/smbldap.c:628(smbldap_start_tls)
  Failed to issue the StartTLS instruction: Protocol error
[2021/07/17 23:53:30.905981,  0] ../../source3/passdb/pdb_ldap.c:6753(pdb_ldapsam_init_common)
  pdb_init_ldapsam: WARNING: Could not get domain info, nor add one to the domain. We cannot work reliably without it.
[2021/07/17 23:53:30.906196,  0] ../../source3/passdb/pdb_interface.c:180(make_pdb_method_name)
  pdb backend ldapsam:ldap://10.1.1.9 did not correctly init (error was NT_STATUS_CANT_ACCESS_DOMAIN_INFO)

This was fixed after setting no ssl.


See also: