HomeLab

Docker-compose an OpenLDAP server

As your homelab grows, you’ll probably end up with more than one machine. Although, maybe the kids these days use docker for all their needs, and truly only have one machine and just run a pile of containers on it… I, on the other hand have at least 14 Virtual Machines running on the Xen Hypervisor. But for this case, running OpenLDAP in a docker container makes things a whole lot simpler.

I want to keep all my users and their passwords the same on every box. It also makes it much easier to deal with NFS, since all the UIDs and GIDs are the same. I could configure all of this using chef, but that gets old quick. No self-service passwords, no self-service anything. All must be done through the chef recipe.

LDAP is an excellent solution to provide a centralized identity service. I’m using OpenLDAP. Some alternatives include FreeIPA and Apache Directory Server. Those have substantially more features. FreeIPA includes Kerberos authentication! I just wanted to have (relatively) simple users, passwords, and LDAP based SSH keys. OpenLDAP is very mature open-source software, and used to be very easy to configure. Technically, it still is somewhat easy to configure, but not spelled out very clearly.

Containers to the rescue

Recently, in newer versions of OpenLDAP, the configuration model switched from a files based configuration, to in-directory configuration. All configuration of the server is actually done from within the directory itself, called olcConfiguration or cn=config. This style of configuration affects the server immediately, without having to have a reload triggered. It also makes it easy to replicate, if you replicate configuration.

Unfortunately, this has also made it very difficult to deploy using Chef. I ended up setting up an OpenLDAP Snowflake Server. That is a terrible thing, especially for an identity provider. If my LDAP goes down for an extended period of time, I lose significant functionality on my virtual machines. And the family will also lose access to things, and then I pull familial aggro.

I discovered a container based solution that is also open and freely available. And even an available fork of it built for armv7. This solution provides me with a bootstrap configuration that will set up Strict TLS, multi-master replication of both config and data, and seeding of the LDAP Database. All my world’s desires wrapped up into a nice package.

Docker-compose

I wanted to use the exact same configuration and setup process on my raspberry pi, as I did on the virtual machine that’s the primary source of LDAP data. If I couldn’t do it via Chef, then a docker container is probably the second best option. At least with that, I can repeatably build the same thing.

Fortunately, I didn’t have to extend the container at all. I only have to set up a docker-compose.yml:

version: '3'
services:
  ouroboros:
    container_name: ouroboros
    hostname: ouroboros
    image: pyouroboros/ouroboros
    environment:
      - CLEANUP=true
      - INTERVAL=300
      - LOG_LEVEL=info
      - SELF_UPDATE=true
      - TZ=America/Chicago
    restart: unless-stopped
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    dns:
      - 10.10.220.232
      - 10.10.220.231
    dns_search: dark.kow.is
  ldap:
    image: osixia/openldap
    #image: adann0/openldap:armv7
    container_name: dark_ldap
    hostname: pione.dark.kow.is
    domainname: pione.dark.kow.is
    environment:
      # Enable this after the initial bootstrap
      # KEEP_EXISTING_CONFIG: "true"
      LDAP_ORGANISATION: "Dark Kow.is"
      LDAP_TLS_CRT_FILENAME: "server.crt"
      LDAP_TLS_KEY_FILENAME: "server.key"
      LDAP_TLS_CA_CRT_FILENAME: "dark.kow.is.ca.crt"
      LDAP_TLS_DH_PARAM_FILENAME: "dhparam.pem"
      LDAP_DOMAIN: dark.kow.is
      LDAP_ADMIN_PASSWORD: "${LDAP_ADMIN_PASSWORD}"
      LDAP_CONFIG_PASSWORD: "${LDAP_CONFIG_PASSWORD}"
      LDAP_TLS_ENFORCE: "true"
      LDAP_TLS_VERIFY_CLIENT: try
      LDAP_REPLICATION: "true"
      LDAP_REPLICATION_HOSTS: "#PYTHON2BASH:['ldap://pione.dark.kow.is','ldap://ldap.dark.kow.is']"
    volumes:
      - ./data/bootstrap:/container/service/slapd/assets/config/bootstrap/ldif/custom
      - ./data/certificates:/container/service/slapd/assets/certs
      - ./data/database:/var/lib/ldap
      - ./data/config:/etc/ldap/slapd.d
    restart: unless-stopped
    dns:
      - 10.10.220.232
      - 10.10.220.231
    dns_search: dark.kow.is
    ports:
      - "389:389"
      - "636:636"
    command: --copy-service

Additionally, I can specify, using the baked in seeding functionality, well, seed data for my LDAP server. I added schemas and the original data backed up from my existing LDAP server. I used the ldapns schema to allow me to specify what host a user is able to log in to, enforced by sssd.

Finally, the /data/certificates directory contains the server certificates to ensure that TLS works. Very important for an identity and password repository. We do not ever want to accidentally transmit any of that in plain text.

Ouroboros

Originally, I was using watchtower to keep my docker containers up to date. I was rather disappointed in it’s notification functionality, however. It could only talk to slack. I’m trying to move myself entirely to matrix. Fortunately, a friend pointed me to Ouroboros. It’s functionally the same thing, only python based, so maybe a little heavier. It has substantially more configuration options, and is pluggable to notify via many mechanisms, one of which is matrix. Eventually I’ll have it notifying me when it updates my LDAP container as well. I’d like to also hook it into Prometheus somehow. Or at least, get it on my dashboards.

Secrets

There were a few gotchas along the way. Maybe I should put this at the top…

Server certificate filenames should be the same across all the servers. Upon replication, only one configuration can exist, differing certificate names will be clobbered. And then one of your LDAP servers no longer has valid TLS data, and you cannot talk to it any longer.

For the passwords, bash variable expansion seems to happen. Special characters in bash could result in unintended effects. Double check your generated LDAP configuration to be sure.

Finally, I have a commented out section in the docker compose where I set the environment variable KEEP_EXISTING_CONFIG. I found this to be the way to start up the container the second time. Especially if you make any changes to the docker-compose.yml. Or when you use oroborous, like I am.

Related posts

Self-Hosted WordPress for the Homelabist

David Kowis

Why I Home Lab

David Kowis

Homelab Docker Registry

David Kowis

1
Leave a Reply

avatar
1 Comment threads
0 Thread replies
0 Followers
 
Most reacted comment
Hottest comment thread
0 Comment authors
Recent comment authors
  Subscribe  
newest oldest most voted
Notify of
trackback

[…] A robust docker registry can be more difficult than anticipated to set up. Especially if you want vulnerability scanning. The registry provided by Docker is perfectly acceptable, but does not provide authentication or authorization. Here, I explain how I set up my homelab docker registry using portus and integrated it with my ldap servers. […]

Adventures in Software Development, Home Labbing, and DevOps