Configuring LDAP auth for Traefik (and more!)

2020-02-17

As it stands now, my setup for web-accessible Docker-hosted sites is a bit convoluted. Traffic from the gateway flows into a bastion host in my DMZ. This is a tiny box running Nginx acting as a reverse proxy. There’s a hole in the firewall poked in from this bastion host to the Docker host running on my internal network. Traefik runs in Docker and provides SSL termination among other things.

This guide assumes that you have one or more Docker containers that you want to put behind LDAP authentication. It also assumes you have an already-working Traefik setup. Also, if you’re doing something weird like I am, I won’t provide further details but I will provide the necessary Nginx config bits to ensure this works properly.

Set up Authelia container

I like to use docker-compose to make things easier. Here’s my cleaned-up docker-compose.yaml for Authelia:

version: "3.7"
services:
  authelia:
    image: authelia/authelia
    container_name: authelia
    environment:
      - TZ=America/Chicago
    labels:
      - traefik.http.routers.authelia.rule=Host(`login.example.com`)
      - traefik.http.services.authelia.loadbalancer.server.port=9091
      - traefik.http.services.authelia.loadbalancer.server.scheme=http
      - traefik.http.routers.authelia.tls=true
      - traefik.http.routers.authelia.tls.certresolver=default
      - traefik.http.routers.authelia.tls.domains[0].main=example.com
      - traefik.http.routers.authelia.tls.domains[0].sans=*.example.com
    networks:
      - lbclients
    ports:
      - 9091
    volumes:
      - ${PWD}/data/config.yml:/etc/authelia/configuration.yml:ro
      - ./data/data:/var/lib/authelia
    restart: unless-stopped

networks:
  lbclients:
    external: false
    name: loadbalancer

Authelia is configured separately using its own YAML file. That’s the config.yml file that we mount into the container. They provide a template here that’s pretty well explained. Just a couple quick notes.

Here’s a snippet where AD is the backend LDAP:

<snip>
 ldap:
    # The url to the ldap server. Scheme can be ldap:// or ldaps://
    url: ldap://dc.example.com
    # Skip verifying the server certificate (to allow self-signed certificate).
    skip_verify: false
    # The base dn for every entries
    base_dn: dc=example,dc=com
    # An additional dn to define the scope to all users
    additional_users_dn: cn=users
    # The users filter used to find the user DN
    # {0} is a matcher replaced by username.
    # 'cn={0}' by default.
    users_filter: (sAMAccountName={0})
    # An additional dn to define the scope of groups
    additional_groups_dn: cn=users
    # The groups filter used for retrieving groups of a given user.
    # {0} is a matcher replaced by username.
    # {dn} is a matcher replaced by user DN.
    # {uid} is a matcher replaced by user uid.
    # 'member={dn}' by default.
    groups_filter: (&(member={dn})(objectclass=group))
    # The attribute holding the name of the group
    group_name_attribute: cn
    # The attribute holding the mail address of the user
    mail_attribute: mail
    # The username and password of the admin user.
    user: CN=svcBindAccount,CN=Users,DC=example,DC=com
    # This secret can also be set using the env variables AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PASSWORD
    password: bindAcctPasswordHere
</snip>

Unless you already have a MySQL server somewhere, you’ll want to yoink that section and just uncomment the local section under storage. Likewise, if you don’t have Redis, that’s fine too – just remove that section.

Finally, I set default_policy to one_factor. Then, I can apply the config down below only to the containers I care about, and not specify any further rules.

Set up Traefik

Actually, nothing special at all needs to be done, assuming you have a working Traefik config and a way to provide certs for all the relevant domain names.

Set up backend container

Again, assuming you have an otherwise-working backend container, you can simply add the following labels to the labels section of your docker-compose.yaml file.

- traefik.http.routers.yourapp.middlewares=authelia
- traefik.http.middlewares.authelia.forwardAuth.address=http://authelia:9091/api/verify?rd=https://login.example.com

Assuming you kept the Authelia container’s name as authelia, you should only need to change the domain at the very end (rd=...).

Ngnix config

Again, this is only relevant if you’re doing the weird thing I’m doing. Setting up Authelia to work with Nginx is outside the scope of this post (although you may find this documentation to be useful).

All you’ll want to do is set up a snippet (mine go in /etc/nginx/snippets), call it proxy.conf. Its contents should look like this:

proxy_set_header        Host $host;
proxy_set_header        X-Real-IP $remote_addr;
proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header        X-Forwarded-Proto $scheme;
proxy_set_header        X-Forwarded-Host $http_host;
proxy_set_header        X-Forwarded-Uri $request_uri;
proxy_set_header        X-Forwarded-Ssl on;
proxy_set_header        Upgrade $http_upgrade;
proxy_set_header        Connection "upgrade";

proxy_http_version      1.1;
proxy_cache_bypass      $cookie_session;
proxy_no_cache          $cookie_session;
proxy_buffers           64 256k;

proxy_buffering         off;
client_max_body_size    0;
client_body_buffer_size 128k;

send_timeout 5m;
proxy_read_timeout 360;
proxy_send_timeout 360;
proxy_connect_timeout 360;

Then, in your site’s config, under the location block, add include snippets/proxy.conf and reload/restart the Nginx service.

Note that this only ensures that Nginx is sending the proper headers and such, and in no way configures Nginx to pass the auth. You’ll want the above-linked documentation to do that. Traefik is doing all the heavy lilfting here.

Enter your instance's address