Setting up local PKI and LDAPS for Authelia

Created: 25 Jul 2025.

This page is still a working document and is undergoing implementation and verification of the steps.

Part 1: Setting up your Local Network PKI with OpenSSL on Debian

We will create a dedicated user for managing the PKI files to enhance security. For a simple homelab, you might perform these steps as root or a sudo user. For better security practices, creating an isolated pkiadmin user is recommended.

1. Install OpenSSL and Prepare PKI Directory:

First, ensure OpenSSL is installed.

1sudo apt update
2sudo apt install openssl ca-certificates # ca-certificates is useful for managing system-wide trust

Create a dedicated directory for your PKI files, ideally in a secure location (e.g., /etc/pki). We’ll set up a structure for the Root CA and Intermediate CA.

1sudo mkdir -p /etc/pki/ca
2sudo mkdir -p /etc/pki/intermediate
3sudo mkdir -p /etc/pki/certs # For signed server/client certs
4sudo mkdir -p /etc/pki/private # For private keys
5sudo mkdir -p /etc/pki/csr # For Certificate Signing Requests

Create necessary index files and serial numbers:

1sudo touch /etc/pki/ca/index.txt
2sudo sh -c 'echo 1000 > /etc/pki/ca/serial'
3
4sudo touch /etc/pki/intermediate/index.txt
5sudo sh -c 'echo 1000 > /etc/pki/intermediate/serial'

2. Configure OpenSSL for your Root CA:

Create the openssl.cnf file for your Root CA. This file defines the default settings for certificate generation.

sudo vi /etc/pki/ca/openssl.cnf

Paste the following content, adjusting default_days, organizationName_default, commonName_default, and country/state as appropriate for your network.

/etc/pki/ca/openssl.cnf
 1# /etc/pki/ca/openssl.cnf
 2[ ca ]
 3default_ca = CA_default
 4
 5[ CA_default ]
 6dir               = /etc/pki/ca            # Where everything is kept
 7certs             = $dir/certs             # Where the issued certs are kept (unused for root)
 8new_certs_dir     = $dir/newcerts          # Default place for new certs (unused for root)
 9database          = $dir/index.txt         # database index file.
10serial            = $dir/serial            # The current serial number
11crlnumber         = $dir/crlnumber         # the current crl number (optional)
12
13default_days      = 7300                   # How long to certify for (20 years)
14default_md        = sha256                 # Use SHA-256 for digests
15preserve          = no                     # keep passed DN ordering
16
17policy            = policy_strict
18
19[ policy_strict ]
20countryName             = match
21stateOrProvinceName     = match
22organizationName        = match
23organizationalUnitName  = optional
24commonName              = supplied
25emailAddress            = optional
26
27[ req ]
28default_bits        = 4096                   # RSA key size
29distinguished_name  = req_distinguished_name
30string_mask         = utf8only
31default_md          = sha256                 # Use SHA-256 for digests
32x509_extensions     = v3_ca                  # The extensions to add to the self signed cert
33
34[ req_distinguished_name ]
35countryName                     = Country Name (2 letter code)
36countryName_default             = SG
37stateOrProvinceName             = State or Province Name (full name)
38stateOrProvinceName_default     = Singapore
39localityName                    = Locality Name (eg, city)
40localityName_default            = Singapore
41organizationName                = Organization Name (eg, company)
42organizationName_default        = YourLocalNetwork
43organizationalUnitName          = Organizational Unit Name (eg, section)
44commonName                      = Common Name (e.g. server FQDN or YOUR name)
45commonName_max                  = 64
46emailAddress                    = Email Address
47emailAddress_max                = 64
48
49[ v3_ca ]
50subjectKeyIdentifier = hash
51authorityKeyIdentifier = keyid:always,issuer
52basicConstraints = critical, CA:true
53keyUsage = critical, digitalSignature, cRLSign, keyCertSign

3. Generate Root CA Key and Certificate:

IMPORTANT SECURITY NOTE: For maximum security, perform this step on a machine that can be disconnected from the network after the keys are generated, or a virtual machine snapshot that is reverted after. Protect the Root CA private key with a very strong passphrase.

 1# Generate Root CA private key
 2sudo openssl genrsa -aes256 -out /etc/pki/private/ca.key.pem 4096
 3sudo chmod 400 /etc/pki/private/ca.key.pem # Restrict permissions
 4
 5# Create Root CA self-signed certificate
 6sudo openssl req -config /etc/pki/ca/openssl.cnf \
 7    -key /etc/pki/private/ca.key.pem \
 8    -new -x509 -days 7300 -sha256 -extensions v3_ca \
 9    -out /etc/pki/ca/ca.crt.pem
10
11# Verify the Root CA certificate
12openssl x509 -in /etc/pki/ca/ca.crt.pem -text -noout

4. Configure OpenSSL for your Intermediate CA:

Using an Intermediate CA is a best practice. It allows you to keep your Root CA offline and issue certificates from the Intermediate CA.

sudo nano /etc/pki/intermediate/openssl.cnf

Paste the following content, adjusting paths, default_days, and DN fields similar to the Root CA config. Pay attention to the [ server_cert ] and [ alt_names ] sections, which will be crucial for your LDAP server certificate.

/etc/pki/intermediate/openssl.cnf
 1# /etc/pki/intermediate/openssl.cnf
 2[ ca ]
 3default_ca = CA_default
 4
 5[ CA_default ]
 6dir               = /etc/pki/intermediate  # Where everything is kept
 7certs             = $dir/certs             # Where the issued certs are kept
 8new_certs_dir     = $dir/newcerts          # Default place for new certs.
 9database          = $dir/index.txt         # database index file.
10serial            = $dir/serial            # The current serial number
11crlnumber         = $dir/crlnumber         # the current crl number (optional)
12
13default_days      = 3650                   # How long to certify for (10 years)
14default_md        = sha256                 # Use SHA-256 for digests
15preserve          = no                     # keep passed DN ordering
16
17policy            = policy_strict
18
19[ policy_strict ]
20countryName             = match
21stateOrProvinceName     = match
22organizationName        = match
23organizationalUnitName  = optional
24commonName              = supplied
25emailAddress            = optional
26
27[ req ]
28default_bits        = 2048                   # RSA key size
29distinguished_name  = req_distinguished_name
30string_mask         = utf8only
31default_md          = sha256                 # Use SHA-256 for digests
32
33[ req_distinguished_name ]
34countryName                     = Country Name (2 letter code)
35countryName_default             = SG
36stateOrProvinceName             = State or Province Name (full name)
37stateOrProvinceName_default     = Singapore
38localityName                    = Locality Name (eg, city)
39localityName_default            = Singapore
40organizationName                = Organization Name (eg, company)
41organizationName_default        = YourLocalNetwork
42organizationalUnitName          = Organizational Unit Name (eg, section)
43commonName                      = Common Name (e.g. server FQDN or YOUR name)
44commonName_max                  = 64
45emailAddress                    = Email Address
46emailAddress_max                = 64
47
48[ v3_intermediate_ca ]
49subjectKeyIdentifier = hash
50authorityKeyIdentifier = keyid:always,issuer
51basicConstraints = critical, CA:true, pathlen:0 # pathlen:0 means it can't sign other CAs
52keyUsage = critical, digitalSignature, cRLSign, keyCertSign
53
54# Extensions for server certificates (used for LDAP server)
55[ server_cert ]
56basicConstraints = CA:FALSE
57keyUsage = critical, digitalSignature, keyEncipherment
58extendedKeyUsage = serverAuth # Crucial for server certificates
59subjectAltName = @alt_names # Include Subject Alternative Names
60
61# Extensions for client certificates (if you need them)
62[ client_cert ]
63basicConstraints = CA:FALSE
64keyUsage = critical, digitalSignature, keyEncipherment
65extendedKeyUsage = clientAuth
66subjectAltName = @alt_names
67
68# Subject Alternative Names (SANs)
69# IMPORTANT: Customize this section for your specific server FQDNs and IPs.
70[ alt_names ]
71DNS.1 = ldap.yourdomain.local     # Replace with your LDAP server's FQDN
72# DNS.2 = another.ldap.hostname.local # Add more DNS names if needed
73# IP.1 = 192.168.1.100              # Optional: If you address by IP, include it

5. Generate Intermediate CA Key and Certificate:

 1# Generate Intermediate CA private key
 2sudo openssl genrsa -aes256 -out /etc/pki/intermediate/private/intermediate.key.pem 2048
 3sudo chmod 400 /etc/pki/intermediate/private/intermediate.key.pem
 4
 5# Create Intermediate CA Certificate Signing Request (CSR)
 6sudo openssl req -config /etc/pki/intermediate/openssl.cnf \
 7    -new -sha256 \
 8    -key /etc/pki/intermediate/private/intermediate.key.pem \
 9    -out /etc/pki/intermediate/csr/intermediate.csr.pem
10
11# Sign the Intermediate CA CSR with the Root CA
12# You will be prompted for the Root CA passphrase
13sudo openssl ca -config /etc/pki/ca/openssl.cnf -extensions v3_intermediate_ca \
14    -days 3650 -notext -md sha256 \
15    -in /etc/pki/intermediate/csr/intermediate.csr.pem \
16    -out /etc/pki/intermediate/certs/intermediate.crt.pem
17
18# Verify the Intermediate CA certificate
19openssl x509 -in /etc/pki/intermediate/certs/intermediate.crt.pem -text -noout
20
21# Create the certificate chain file (Intermediate CA + Root CA)
22# This file is often needed by servers to provide the full chain to clients
23sudo cat /etc/pki/intermediate/certs/intermediate.crt.pem \
24    /etc/pki/ca/ca.crt.pem > /etc/pki/intermediate/certs/ca-chain.crt.pem

6. Distribute the Root CA Certificate to Debian Systems:

For any Debian system (including the one running Authelia) to trust certificates issued by your PKI, it needs to trust your Root CA.

1# Copy the Root CA certificate to the system trust store
2sudo cp /etc/pki/ca/ca.crt.pem /usr/local/share/ca-certificates/your_local_ca.crt
3
4# Update the system's CA certificates
5sudo update-ca-certificates

You should see output similar to Adding certificate /usr/local/share/ca-certificates/your_local_ca.crt and 1 added, 0 removed; done.

7. Issue a Certificate for your LDAP Server:

This certificate will be used by your LDAP server for LDAPS. Perform these steps on the machine where your PKI is managed, then transfer the files to your LDAP server.

 1# IMPORTANT: If your LDAP server is NOT 'ldap.yourdomain.local' or 192.168.1.10,
 2# YOU MUST EDIT /etc/pki/intermediate/openssl.cnf NOW
 3# Specifically, modify the [ alt_names ] section to reflect your LDAP server's FQDN(s) and IP(s).
 4# Common Name (CN) is less important than SANs for modern TLS.
 5
 6# Generate LDAP server private key
 7sudo openssl genrsa -out /etc/pki/private/ldap.yourdomain.local.key.pem 2048
 8sudo chmod 400 /etc/pki/private/ldap.yourdomain.local.key.pem
 9
10# Create LDAP server Certificate Signing Request (CSR)
11# Ensure the Common Name matches one of your LDAP server's FQDNs
12sudo openssl req -config /etc/pki/intermediate/openssl.cnf \
13    -new -sha256 \
14    -key /etc/pki/private/ldap.yourdomain.local.key.pem \
15    -out /etc/pki/csr/ldap.yourdomain.local.csr.pem \
16    -subj "/C=SG/ST=Singapore/L=Singapore/O=YourLocalNetwork/CN=ldap.yourdomain.local" # Adjust DN fields
17
18# Sign the LDAP server CSR with the Intermediate CA
19# You will be prompted for the Intermediate CA passphrase
20sudo openssl ca -config /etc/pki/intermediate/openssl.cnf -extensions server_cert \
21    -days 730 -notext -md sha256 \
22    -in /etc/pki/csr/ldap.yourdomain.local.csr.pem \
23    -out /etc/pki/certs/ldap.yourdomain.local.crt.pem
24
25# Verify the LDAP server certificate
26openssl x509 -in /etc/pki/certs/ldap.yourdomain.local.crt.pem -text -noout
27
28# Create the full chain for the LDAP server (server cert + intermediate + root)
29# Some applications prefer this format.
30sudo cat /etc/pki/certs/ldap.yourdomain.local.crt.pem \
31    /etc/pki/intermediate/certs/ca-chain.crt.pem \
32    > /etc/pki/certs/ldap.yourdomain.local_fullchain.crt.pem

8. Configure your LDAP Server for LDAPS (OpenLDAP Example on Debian):

This assumes your LDAP server is also running on Debian.

Transfer Certificates: Copy the following files from your PKI management machine to your OpenLDAP server (e.g., into /etc/ssl/ldap/). Create this directory if it doesn’t exist.

/etc/pki/certs/ldap.yourdomain.local.crt.pem (Your LDAP server’s certificate)

/etc/pki/private/ldap.yourdomain.local.key.pem (Your LDAP server’s private key)

/etc/pki/ca/ca.crt.pem (Your Root CA certificate) - useful if OpenLDAP needs to verify client certificates, or for consistency.

/etc/pki/intermediate/certs/ca-chain.crt.pem (The full chain for OpenLDAP)

1# On the PKI machine:
2sudo scp /etc/pki/certs/ldap.yourdomain.local.crt.pem user@ldap_server_ip:/tmp/
3sudo scp /etc/pki/private/ldap.yourdomain.local.key.pem user@ldap_server_ip:/tmp/
4sudo scp /etc/pki/intermediate/certs/ca-chain.crt.pem user@ldap_server_ip:/tmp/
5sudo scp /etc/pki/ca/ca.crt.pem user@ldap_server_ip:/tmp/ # Optional, but good practice
 1# On the LDAP server:
 2sudo mkdir -p /etc/ssl/ldap
 3sudo mv /tmp/ldap.yourdomain.local.crt.pem /etc/ssl/ldap/
 4sudo mv /tmp/ldap.yourdomain.local.key.pem /etc/ssl/ldap/
 5sudo mv /tmp/ca-chain.crt.pem /etc/ssl/ldap/
 6sudo mv /tmp/ca.crt.pem /etc/ssl/ldap/ # Optional
 7
 8# Set appropriate permissions for the private key
 9sudo chown openldap:openldap /etc/ssl/ldap/ldap.yourdomain.local.key.pem
10sudo chmod 640 /etc/ssl/ldap/ldap.yourdomain.local.key.pem

Configure OpenLDAP for TLS (using cn=config): This is the modern way to configure OpenLDAP. Create an LDIF file.

sudo nano /tmp/tls_config.ldif

Paste the following. Adjust paths if you put your certificates elsewhere.

 1# /tmp/tls_config.ldif
 2dn: cn=config
 3changetype: modify
 4add: olcTLSCACertificateFile
 5olcTLSCACertificateFile: /etc/ssl/ldap/ca.crt.pem # Your Root CA (or ca-chain if preferred)
 6-
 7add: olcTLSCertificateFile
 8olcTLSCertificateFile: /etc/ssl/ldap/ldap.yourdomain.local.crt.pem
 9-
10add: olcTLSCertificateKeyFile
11olcTLSCertificateKeyFile: /etc/ssl/ldap/ldap.yourdomain.local.key.pem
12-
13add: olcTLSVerifyClient
14olcTLSVerifyClient: never # Or 'try', 'demand' depending on if you use client certs

Apply the configuration. You might need to install ldap-utils first: sudo apt install ldap-utils.

sudo ldapmodify -Y EXTERNAL -H ldapi:/// -f /tmp/tls_config.ldif

Verify the configuration (optional):

sudo ldapsearch -Y EXTERNAL -H ldapi:/// -b "cn=config" olcTLSCertificateFile olcTLSCertificateKeyFile olcTLSCACertificateFile

Restart OpenLDAP:

sudo systemctl restart slapd
sudo systemctl status slapd # Check for errors

Test LDAPS Connectivity: From a client that trusts your Root CA (e.g., your Authelia server or your workstation):

ldapsearch -H ldaps://ldap.yourdomain.local:636 -x -d 1 -LLL -b "dc=yourdomain,dc=local" "(objectClass=*)"

You should see output indicating a successful TLS handshake and TLS: peer certificate details. If it fails, check firewall rules (port 636) and certificate names/permissions.

Part 2: Integrating Authelia with LDAPS on Debian

Assuming Authelia will run in a Docker container on a Debian host, and that the host’s system trust store (updated in Part 1, Step 6) is available to the container. Even if Authelia runs directly on the host, the principles are the same.

1. Install Docker and Docker Compose (if not already done):

# Install Docker Engine
sudo apt install ca-certificates curl gnupg lsb-release
sudo mkdir -m 0755 -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian \
  $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

# Add your user to the docker group to run docker commands without sudo (requires re-login)
sudo usermod -aG docker $USER
newgrp docker # Apply group changes immediately

2. Prepare Authelia Configuration Directory:

Create a directory for Authelia’s configuration.

mkdir -p ~/authelia/config
mkdir -p ~/authelia/config/certs # For custom CA certificates

3. Copy Root CA Certificate for Authelia:

Even if the host trusts the CA, explicitly mounting it into Authelia’s configuration helps ensure it’s available within the container’s isolated environment.

cp /etc/pki/ca/ca.crt.pem ~/authelia/config/certs/

4. Create Authelia Configuration File (configuration.yml):

nano ~/authelia/config/configuration.yml

Paste the following content. Crucially, ensure address uses ldaps:// and the FQDN matches the Common Name/SAN on your LDAP server’s certificate. The certificates_directory will point to where Authelia finds your Root CA.

 1# ~/authelia/config/configuration.yml
 2authentication_backend:
 3  ldap:
 4    # Use 'ldaps' scheme for secure LDAP.
 5    # Replace ldap.yourdomain.local with your actual LDAP server's FQDN or IP.
 6    address: 'ldaps://ldap.yourdomain.local:636' # Use FQDN as in LDAP server cert SANs
 7
 8    # Set to 'custom' for general OpenLDAP setups.
 9    implementation: 'custom'
10
11    timeout: 5s
12
13    tls:
14      # Set to 'false' as you're using a trusted custom CA.
15      # Setting to 'true' would skip verification, which is insecure.
16      skip_verify: false
17
18      # Optional: Specify the server name to be used for TLS SNI.
19      # This should match the CN or a SAN on your LDAP server's certificate.
20      # server_name: ldap.yourdomain.local
21
22    # Your LDAP base DN (Distinguished Name)
23    base_dn: 'dc=yourdomain,dc=local'
24
25    # A service user for Authelia to bind to the LDAP server.
26    # This user needs read permissions for users and groups.
27    # Replace with the DN of your service user.
28    user: 'cn=authelia_bind_user,ou=ServiceAccounts,dc=yourdomain,dc=local' # Example for OpenLDAP
29    password: 'your_authelia_bind_user_password'
30
31    # Attributes to retrieve from LDAP for user information
32    attributes:
33      username: 'uid' # Common for OpenLDAP. For AD, it's 'sAMAccountName'
34      display_name: 'cn' # Common for OpenLDAP. For AD, it's 'displayName'
35      mail: 'mail'
36      member_of: 'memberOf' # For group membership. Ensure this attribute is populated in your LDAP.
37
38    # Filters for users and groups
39    # These will vary depending on your LDAP schema and implementation.
40    # For OpenLDAP, 'objectClass=inetOrgPerson' is common for users.
41    users_filter: '(&({username_attribute}={input})(objectClass=inetOrgPerson))'
42    groups_filter: '(&(member={dn})(objectClass=groupOfNames))' # Common for OpenLDAP groups.
43
44    group_search_mode: 'filter' # Or 'memberof' for Active Directory
45
46    # Path to a directory containing your custom CA certificates inside the Authelia container.
47    # This will be mapped to the host's ~/authelia/config/certs directory.
48    certificates_directory: '/config/certs'
49
50# Define other necessary Authelia configuration sections below
51# For example, jwt_secret, session, totp, notification, access_control, etc.
52# Refer to the official Authelia documentation for a complete configuration:
53# https://www.authelia.com/configuration/

5. Create Docker Compose File (docker-compose.yml):

nano ~/authelia/docker-compose.yml
 1# ~/authelia/docker-compose.yml
 2version: '3.8'
 3
 4services:
 5  authelia:
 6    image: authelia/authelia:latest
 7    container_name: authelia
 8    restart: unless-stopped
 9    ports:
10      - "9091:9091" # Authelia's default port for applications to redirect to
11    volumes:
12      - ./config:/config # Mount your configuration directory
13      - ./config/certs:/config/certs:ro # Mount your custom CA certs read-only
14    environment:
15      # Set necessary environment variables, especially for secrets
16      # It's recommended to use Docker secrets or environment variables for sensitive data
17      # For example:
18      # - AUTHELIA_JWT_SECRET=your_jwt_secret_here
19      # - AUTHELIA_LDAP_USER_PASSWORD=your_authelia_bind_user_password
20      # - AUTHELIA_NOTIFIER_SMTP_PASSWORD=your_smtp_password
21      - AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ADDRESS=ldaps://ldap.yourdomain.local:636 # Good for overriding and clarity
22      - AUTHELIA_AUTHENTICATION_BACKEND_LDAP_USER_PASSWORD=your_authelia_bind_user_password # Replace with your actual bind user password
23    networks:
24      - authelia_net # Ensure Authelia can reach your LDAP server
25
26networks:
27  authelia_net:
28    driver: bridge

6. Start Authelia:

Navigate to your ~/authelia directory and start the services.

cd ~/authelia
docker compose up -d

7. Verify Authelia Logs:

Check Authelia’s logs for any errors related to LDAP connectivity or certificate validation.

docker compose logs authelia

Look for messages indicating successful connection to LDAPS, or specific errors like x509: certificate signed by unknown authority if the CA certificate is not properly trusted.

Troubleshooting Tips:

Firewall: Ensure TCP port 636 (LDAPS) is open between your Authelia server (or its Docker container network) and your LDAP server.

DNS: Verify that ldap.yourdomain.local (or whatever FQDN you use) resolves correctly to your LDAP server’s IP address from the Authelia host.

Certificate Names: Double-check that the CN and SAN entries in your LDAP server’s certificate exactly match how Authelia tries to connect (FQDN or IP).

File Permissions: Ensure the private keys on both your PKI machine and LDAP server have highly restricted permissions.

Passphrases: Don’t forget the passphrases for your CA private keys!

Time Synchronization: Ensure all involved systems (PKI host, LDAP server, Authelia host) have correct and synchronized time using NTP.

By following these Debian-specific steps, you’ll have a robust, secure, and locally managed PKI securing your Authelia-LDAP communication.