How to setup OpenLDAP based authentication and authorization in Apache

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..