How to setup OpenLDAP based authentication and authorization in Apache

Here in this article we will try to setup OpenLDAP based authentication and authorization in Apache HTTP server secure website content.
Test Environment
- Fedora Server 41
- Apache httpd v2.4.63
- OpenLDAP v2
Apache mod_authnz_ldap module
This module provides support as both authentication and authorization provider. It allows an LDAP directory to be used to store the database for HTTP Basic authentication. This module allows authentication front-ends such as mod_auth_basic to authenticate users through an ldap directory.
During the authentication phase, mod_authnz_ldap searches for an entry in the directory that matches the username that the HTTP client passes. If a single unique match is found, then mod_authnz_ldap attempts to bind to the directory server using the DN of the entry plus the password provided by the HTTP client. Because it does a search, then a bind, it is often referred to as the search/bind phase. For more details please read mod_authnz_ldap documentation.
This is a continuation to our previous article “How to setup Named Based Virtual Hosting in Apache HTTP server” wherein we have seen how we can setup Name Based Virtual Hosting for two different customers.
High Level Architecture

If you are interested in watching the video. Here is the YouTube video on the same step by step procedure outlined below.
Procedure
Step1: Ensure LDAP module installed
Here we are going to create a new role “linux_install_prerequisite” which will be used to install pre-requisite packages for setting up LDAP based authentication and authorization in Apache HTTP server.
admin@fedser:ahs$ cat roles/linux_install_prerequisite/tasks/main.yml
---
- name: ensure pre-requisite packages installed
dnf:
name: "{{ item }}"
state: present
with_items:
- mod_ldap
On the remote Apache HTTP server it is going to install the modules at the following location as shown below.
admin@linuxser:/etc/httpd/modules$ rpm -ql mod_ldap-2.4.63-1.fc41.x86_64
/etc/httpd/conf.modules.d/01-ldap.conf
/usr/lib/.build-id
/usr/lib/.build-id/34/bc747c90b9c6c160996ec64ef1f39968644c61
/usr/lib/.build-id/4f/40e49ab3bb277f39c8e9736ae0ba90fcad2402
/usr/lib64/httpd/modules/mod_authnz_ldap.so
/usr/lib64/httpd/modules/mod_ldap.so
Step2: Ensure OpenLDAP service running
Here we will be using the following “docker-compose.yml” to setup OpenLDAP service. We will create DIT tree with ROOT DN as “dc=stack,dc=com”, with an admin user DN “cn=Admin,dc=stack,dc=com”. Also we are going to setup two LDAP users with DN “cn=ldapuser1,ou=users,dc=stack,dc=com” and “cn=ldapuser2,ou=users,dc=stack,dc=com” who are members of group with DN “cn=readers,ou=users,dc=stack,dc=com”.
admin@fedser:openldap$ cat docker-compose.yml
version: '2'
services:
openldap:
image: bitnami/openldap:2
ports:
- '1389:1389'
- '1636:1636'
environment:
- LDAP_ADMIN_USERNAME=admin
- LDAP_ADMIN_PASSWORD=admin@1234
- LDAP_USERS=ldapuser1,ldapuser2
- LDAP_PASSWORDS=ldappassword1,ldappassword2
- LDAP_ROOT=dc=stack,dc=com
- LDAP_ADMIN_DN=cn=admin,dc=stack,dc=com
networks:
- my-network
volumes:
- 'openldap_data:/bitnami/openldap'
volumes:
openldap_data:
driver: local
networks:
my-network:
driver: bridge
Now let’s startup our OpenLDAP service using the below command. Ensure that you have docker service installed and running before starting the OpenLDAP service.
admin@fedser:openldap$ docker-compose up -d
Once the OpenLDAP services is running we can validate LDAP DIT tree structure using the below command from the workstation.
admin@fedser:openldap$ ldapsearch -H ldap://fedser.stack.com:1389 -x -b 'dc=stack,dc=com' -D 'cn=admin,dc=stack,dc=com' '(objectClass=*)' -W
Step3: Update Virtual Host configuration
Here we are going the update the Virtual Host configuration file “customer1.conf” and “customer2.conf” to enable basic authentication with Authentication Provider as “ldap”.
We are also setting the LDAP URL along with the LDAP server BindDN and password using the directives “AuthLDAPURL”, “AuthLDAPBindDN” and “AuthLDAPBindPassword”.
Once the LDAP user is authenticated, the authorization check is done using the “Require” directive where in the users in ldap-group named readers are only allowed to access the protected resource.
admin@fedser:ahs$ cat roles/linux_configure_httpd/files/customer1.conf
<VirtualHost 192.168.122.238:80>
ServerName customer1.linuxser.stack.com
DocumentRoot "/var/opt/data/customer1"
<Directory /var/opt/data/customer1>
AllowOverride None
Require all granted
</Directory>
# Secure Application Context using LDAP Authn and AuthZ
<Location /secure>
AuthType Basic
AuthName "LDAP Protected"
AuthBasicProvider ldap
AuthLDAPURL "ldap://fedser.stack.com:1389/dc=stack,dc=com"
AuthLDAPBindDN "cn=admin,dc=stack,dc=com"
AuthLDAPBindPassword "admin@1234"
Require ldap-user "ldapuser1"
#Require ldap-group cn=readers,ou=users,dc=stack,dc=com
</Location>
</VirtualHost>
admin@fedser:ahs$ cat roles/linux_configure_httpd/files/customer2.conf
<VirtualHost 192.168.122.238:80>
ServerName customer2.linuxser.stack.com
DocumentRoot "/var/opt/data/customer2"
<Directory /var/opt/data/customer2>
AllowOverride None
Require all granted
</Directory>
# Secure Application Context using LDAP Authn and AuthZ
<Location /secure>
AuthType Basic
AuthName "LDAP Protected"
AuthBasicProvider ldap
AuthLDAPURL "ldap://fedser.stack.com:1389/dc=stack,dc=com"
AuthLDAPBindDN "cn=admin,dc=stack,dc=com"
AuthLDAPBindPassword "admin@1234"
Require ldap-user "ldapuser2"
#Require ldap-group cn=readers,ou=users,dc=stack,dc=com
</Location>
</VirtualHost>
Step4: Update linux_setup_httpd playbook
Here we are adding the addition role “linux_install_prerequisite” which we have created to install the “mod_ldap” module.
admin@fedser:ahs$ cat linux_setup_httpd.yml
---
- hosts: "ahs"
serial: 1
become: true
become_user: root
roles:
- { role: "linux_ping", tags: "linux_ping" }
- { role: "linux_install_httpd", tags: "linux_install_httpd" }
- { role: "linux_configure_httpd", tags: "linux_configure_httpd" }
- { role: "linux_expose_httpd", tags: "linux_expose_httpd" }
- { role: "linux_stop_httpd", tags: "linux_stop_httpd" }
- { role: "linux_start_httpd", tags: "linux_start_httpd" }
- { role: "linux_staticdeploy_httpd", tags: "linux_staticdeploy_httpd" }
- { role: "linux_create_users", tags: "linux_create_users" }
- { role: "linux_install_prerequisite", tags: "linux_install_prerequisite" }
Step5: Update README.md
Here we are updating our README.md file to include the instruction to execute “linux_install_prerequisite” role as shown below.
admin@fedser:ahs$ cat README.md
# Instructions for execution
ansible-playbook linux_setup_httpd.yml -i inventory/hosts --tags "linux_ping" -v
ansible-playbook linux_setup_httpd.yml -i inventory/hosts --tags "linux_install_httpd" -v
ansible-playbook linux_setup_httpd.yml -i inventory/hosts --tags "linux_configure_httpd" -v
ansible-playbook linux_setup_httpd.yml -i inventory/hosts --tags "linux_expose_httpd" -v
ansible-playbook linux_setup_httpd.yml -i inventory/hosts --tags "linux_stop_httpd" -v
ansible-playbook linux_setup_httpd.yml -i inventory/hosts --tags "linux_start_httpd" -v
ansible-playbook linux_setup_httpd.yml -i inventory/hosts --tags "linux_staticdeploy_httpd" -v
ansible-playbook linux_setup_httpd.yml -i inventory/hosts --tags "linux_create_users" --extra-vars "customer=customer1" -v
ansible-playbook linux_setup_httpd.yml -i inventory/hosts --tags "linux_create_users" --extra-vars "customer=customer2" -v
ansible-playbook linux_setup_httpd.yml -i inventory/hosts --tags "linux_install_prerequisite" -v
Step6: Execute linux_install_prerequisite role
Now it’s time to execute our role “linux_install_prerequisite” to install the “mod_ldap” module as shown below.
admin@fedser:ahs$ ansible-playbook linux_setup_httpd.yml -i inventory/hosts --tags "linux_install_prerequisite" -v
Step7: Execute linux_configure_httpd role
Now let’s update our httpd configuration with the updated “customer1.conf” and “customer2.conf” by executing our role “linux_configure_httpd” as shown below.
admin@fedser:ahs$ ansible-playbook linux_setup_httpd.yml -i inventory/hosts --tags "linux_configure_httpd" -v
Step8: Restart httpd service
Let’s restart our httpd service for the changes to take effect.
admin@fedser:ahs$ ansible-playbook linux_setup_httpd.yml -i inventory/hosts --tags "linux_stop_httpd" -v
admin@fedser:ahs$ ansible-playbook linux_setup_httpd.yml -i inventory/hosts --tags "linux_start_httpd" -v
Step9: Validate LDAP secured Applications
Here for the customer1 secure.html access only ldapuser1 is authorized to access. For ldapuser2 you will get “401 Unauthorized” even though the authentication is successful.
admin@fedser:ahs$ curl -u "ldapuser1:ldappassword1" http://customer1.linuxser.stack.com/secure/secure.html
Hello Customer1. This is your secure page
admin@fedser:ahs$ curl -I -u "ldapuser2:ldappassword2" http://customer1.linuxser.stack.com/secure/secure.html
HTTP/1.1 401 Unauthorized
admin@fedser:ahs$ curl -I -u "ldapuser1:ldappassword1" http://customer2.linuxser.stack.com/secure/secure.html
HTTP/1.1 401 Unauthorized
admin@fedser:ahs$ curl -u "ldapuser2:ldappassword2" http://customer2.linuxser.stack.com/secure/secure.html
Hello Customer2. This is your secure page
Hope you enjoyed reading this article. Thank you..
Leave a Reply
You must be logged in to post a comment.