LemonLDAP::NG in Docker
Presentation
Docker allows do run application into containers.
You can find a Docker image for LemonLDAP::NG in this repository: https://hub.docker.com/r/lemonldapng/lemonldap-ng
See also this github project: https://github.com/LemonLDAPNG/lemonldap-ng-docker
Note
The docker images are compatible with podman. The images can also be built with podman if needed.
Usage
Prerequisites:
Add the URLs below to /etc/hosts on your host server where the docker container will be deployed
echo "127.0.0.1 auth.example.com manager.example.com reload.example.com test1.example.com test2.example.com" | sudo tee -a /etc/hosts
Simple Deployment
With sudo the container can be deployed directly to the host port 80 (option -p)
sudo docker run -d -p 80:80 --name lemonldap-ng lemonldapng/lemonldap-ng
With a rootless docker instance mapping the container to the host port 8080 on the host is neccessary
docker run -d -p 8080:8080 -e PORT=8080 --name lemonldap-ng lemonldapng/lemonldap-ng
Attention
With more complex deployments such as with SELinux the internal container port can be modified with the environment variable specified above with PORTS
Then connect to http://auth.example.com with your browser and log in with dwho/dwho.
Advanced configuration
For first time deployments you will need access to the lemonldap static (lemonldap-ng.ini) and dynamic configuration (lmConf-X.json) we will need to configure the volume mounts.
The following folders will container the theme components such as logos, templates and more:
/usr/share/lemonldap-ng/portal/htdocs/static/CustomTheme
/usr/share/lemonldap-ng/portal/htdocs/static/CustomTheme/css
/usr/share/lemonldap-ng/portal/htdocs/static/CustomTheme/js
/usr/share/lemonldap-ng/portal/htdocs/static/CustomTheme/images
/usr/share/lemonldap-ng/portal/templates/CustomTheme
Note
Loading the custom theme elements requires the configuration of portalSkin variable. In docker this variable must also be configured with the name of the folder being used. In our example case we would configure the variable with “CustomTheme” Keeping the folder name consistant between “templates” and “htdocs/static” is important.
The plugins will be stored in these folders:
/usr/share/perl5/Lemonldap/NG/Portal/Plugins/CustomPlugin
/usr/share/perl5/Lemonldap/NG/Portal/Register/CustomRegister
/usr/share/perl5/Lemonldap/NG/Portal/UserDB/CustomUserdb
/usr/share/perl5/Lemonldap/NG/Portal/Auth/CustomAuth
/usr/share/perl5/Lemonldap/NG/Portal/Captcha/CustomCaptcha
/usr/share/perl5/Lemonldap/NG/Portal/MenuTab/CustomMenuTab
Note
The plugins will be stored in a foler appended with “Custom” therefore all plugins much include the full path to the plugin in the header of the perl file. Example: For plugins stored in /usr/share/perl5/Lemonldap/NG/Portal/Plugins/CustomPlugin/LDAPSearch.pm the header would be package Lemonldap::NG::Portal::Plugin::CustomPlugin::LDAPSeach
The following folders for the configuration and session data can also be mounted and exposed:
/etc/lemonldap-ng
/var/lib/lemonldap-ng/conf
/var/lib/lemonldap-ng/sessions
/var/lib/lemonldap-ng/psessions
/etc/nginx/sites-enabled
Finally a cron job to purge sessions must also be configured with crontab:
# Lemonldap::NG::Handler Session Purge
1 * * * * docker exec -it lemonldap bash -c "[ -x /usr/share/lemonldap-ng/bin/purgeLocalCache ] && if [ ! -d /run/systemd/system ]; then /usr/share/lemonldap-ng/bin/purgeLocalCache; fi"
# Lemonldap::NG::Portal Session Purge
7 * * * * docker exec -it lemonldap bash -c "[ -x /usr/share/lemonldap-ng/bin/purgeCentralCache ] && if [ ! -d /run/systemd/system ]; then /usr/share/lemonldap-ng/bin/purgeCentralCache; fi"
Note
If the container is deployed with rootless docker make sure to execute the crontab without sude. Running sudo crontab will execute the cronjobs on the sudo docker instance deployment.
We can use the docker run command as follows which include all the possible customisations feasible with the image:
docker run -d \
--name lemonldap-ng
-e SSODOMAIN=example.com \
-e PORTAL_HOSTNAME=auth.example.com \
-e MANAGER_HOSTNAME=manager.example.com \
-e HANDLER_HOSTNAME=reload.example.com \
-e TEST1_HOSTNAME=test1.example.com \
-e TEST2_HOSTNAME=test2.example.com \
-e PRESERVEFILES=/etc/lemonldap-ng /var/lib/lemonldap-ng/conf /var/lib/lemonldap-ng/sessions /var/lib/lemonldap-ng/psessions \
-e LOGLEVEL=debug \
-e FASTCGI_LISTEN_PORT=9000 \
-e PORT=8080 \
-e IPV4_ONLY=true \
-p 8080:8080 \
-p 9000:9000 \
-v ./llng/etc:/etc/lemonldap-ng \
-v ./llng/var-conf:/var/lib/lemonldap-ng/conf \
-v ./llng/var-sessions:/var/lib/lemonldap-ng/sessions \
-v ./llng/var-psessions:/var/lib/lemonldap-ng/psessions \
-v ./llng/theme:/usr/share/lemonldap-ng/portal/htdocs/static/CustomTheme \
-v ./llng/template:/usr/share/lemonldap-ng/portal/templates/CustomTheme \
-v ./llng/plugins:/usr/share/perl5/Lemonldap/NG/Portal/Plugins/CustomPlugin \
-v ./llng/register:/usr/share/perl5/Lemonldap/NG/Portal/Register/CustomRegister \
-v ./llng/userdb:/usr/share/perl5/Lemonldap/NG/Portal/UserDB/CustomUserdb \
-v ./llng/auth:/usr/share/perl5/Lemonldap/NG/Portal/Auth/CustomAuth \
-v ./llng/captcha:/usr/share/perl5/Lemonldap/NG/Portal/Captcha/CustomCaptcha \
-v ./llng/menutab:/usr/share/perl5/Lemonldap/NG/Portal/MenuTab/CustomMenuTab \
-v ./llng/nginx:/etc/nginx/sites-enabled \
lemonldapng/lemonldap-ng:latest
Adding the :Z is required for SELinux:
docker run -d \
--name lemonldap-ng
-e SSODOMAIN=example.com \
-e PORTAL_HOSTNAME=auth.example.com \
-e MANAGER_HOSTNAME=manager.example.com \
-e HANDLER_HOSTNAME=reload.example.com \
-e TEST1_HOSTNAME=test1.example.com \
-e TEST2_HOSTNAME=test2.example.com \
-e PRESERVEFILES=/etc/lemonldap-ng /var/lib/lemonldap-ng/conf /var/lib/lemonldap-ng/sessions /var/lib/lemonldap-ng/psessions \
-e LOGLEVEL=debug \
-e FASTCGI_LISTEN_PORT=9000 \
-e PORT=8080 \
-e IPV4_ONLY=true \
-p 8080:8080 \
-p 9000:9000 \
-v ./llng/etc:/etc/lemonldap-ng:Z \
-v ./llng/var-conf:/var/lib/lemonldap-ng/conf:Z \
-v ./llng/var-sessions:/var/lib/lemonldap-ng/sessions:Z \
-v ./llng/var-psessions:/var/lib/lemonldap-ng/psessions:Z \
-v ./llng/theme:/usr/share/lemonldap-ng/portal/htdocs/static/CustomTheme:Z \
-v ./llng/template:/usr/share/lemonldap-ng/portal/templates/CustomTheme:Z \
-v ./llng/plugins:/usr/share/perl5/Lemonldap/NG/Portal/Plugins/CustomPlugin:Z \
-v ./llng/register:/usr/share/perl5/Lemonldap/NG/Portal/Register/CustomRegister:Z \
-v ./llng/userdb:/usr/share/perl5/Lemonldap/NG/Portal/UserDB/CustomUserdb:Z \
-v ./llng/auth:/usr/share/perl5/Lemonldap/NG/Portal/Auth/CustomAuth:Z \
-v ./llng/captcha:/usr/share/perl5/Lemonldap/NG/Portal/Captcha/CustomCaptcha:Z \
-v ./llng/menutab:/usr/share/perl5/Lemonldap/NG/Portal/MenuTab/CustomMenuTab:Z \
-v ./llng/nginx:/etc/nginx/sites-enabled:Z \
lemonldapng/lemonldap-ng:latest
The above command is a rootless docker deployment, using sudo, removing the PORT environment variable and binding to port 80 should be sufficient to directly expose the container.
In addition, the FASTCGI_LISTEN_PORT executes the fastcgi-server exposing port 9000 in the container. Removing this variable from the container deployment causes fastcgi-server to deploy with socket file inside the container. Regardless of the fastcgi-server deployment mechanism nginx is able to connect to fastcgi-server.
We provide docker-compose.yml example files in the github repository.
https://github.com/LemonLDAPNG/lemonldap-ng-docker/blob/master/docker-compose-selinux.yaml
https://github.com/LemonLDAPNG/lemonldap-ng-docker/blob/master/docker-compose.yaml
docker compose -f docker-compose-selinux.yaml up -d
docker compose up -d
Utilities
The docker image provides the same utilities that is provided in a native installation of lemonldap.
docker exec -it lemonldap-ng /usr/share/lemonldap-ng/bin/lemonldap-ng-cli
docker exec -it lemonldap-ng /usr/share/lemonldap-ng/bin/lmConfigEditor
Import and export configuration
Restoring existing configuration can be done by either placing the lmConf-X.json in the folder mounted to /var/lib/lemonldap-ng/ or executing the following commands:
docker cp lmConf-X.json lemonldap-ng:/var/lib/lemonldap-ng/lmConfig.json
docker exec -it lemonldap-ng bash
/usr/share/lemonldap-ng/bing/lemonldap-ng-cli restore - < /var/lib/lemonldap-ng/lmConfig.json
rm /var/lib/lemonldap-ng/lmConfig.json
Exporting configuration is simple when copied directly from the mounted folder /var/lib/lemonldap-ng/ or following these commands:
docker exec -it lemonldap-ng bash
/usr/share/lemonldap-ng/bing/lemonldap-ng-cli backup > /var/lib/lemonldap-ng/lmConfig.json
docker cp lemonldap-ng:/var/lib/lemonldap-ng/lmConfig.json lmConfig.json
Reverse Proxy
Apache2 or NGinx can both be configured to bind to port 80 and proxy pass to another port binding of lemonldap.
HTTP
<VirtualHost *:80>
ProxyPreserveHost On
ProxyRequests Off
CustomLog /var/log/httpd/llng.log combined
ServerName example.com
ServerAlias auth.example.com manager.example.com reload.example.com test1.example.com test2.example.com
ProxyPass / http://127.0.0.1:8080/
ProxyPassReverse / http://127.0.0.1:8080/
</VirtualHost>
HTTPS
<VirtualHost *:80>
ServerName example.com
ServerAlias auth.example.com manager.example.com reload.example.com test1.example.com test2.example.com
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
</VirtualHost>
<VirtualHost *:443>
ServerName example.com
CustomLog /var/log/httpd/llng.log combined
SSLEngine on
SSLCertificateChainFile "/path/to/example.com.cacert.cert"
SSLCertificateFile "/path/to/example.com.cert"
SSLCertificateKeyFile "/path/to/example.com.key"
ProxyPreserveHost On
ProxyPass / http://127.0.0.1:8080/
ProxyPassReverse / http://127.0.0.1:8080/
</VirtualHost>
Protecting applications
Lemonldap::NG provides capabilities to [protect applications](https://lemonldap-ng.org/documentation/latest/configvhost.html?highlight=nginx%20reverse#reverse-proxy-1) with the handler. However the nginx configuration needs to be modified slightly to work in a containerised deployment. The creation of a [virtual host](https://lemonldap-ng.org/documentation/latest/configvhost.html?highlight=virtual%20host#lemonldap-ng-configuration) in Lemonldap::NG manager is also required for it to accepted by lemonldap for authentication and redirection of the protected application.
The following nginx configuration can be added to the ./llng/nginx folder mounted as a volume to docker container. All configurations will be picked up by nginx after restarting the container as the folder is mounted to /etc/nginx/sites-enabled/.
server {
listen 8080;
server_name application.example.com;
# Internal authentication request
location = /lmauth {
internal;
include /etc/nginx/fastcgi_params;
fastcgi_pass /path/to/llng-fastcgi-server.sock;
# Drop post data
fastcgi_pass_request_body off;
fastcgi_param CONTENT_LENGTH "";
# Keep original hostname
fastcgi_param HOST $http_host;
# Keep original request (LLNG server will receive /lmauth)
fastcgi_param X_ORIGINAL_URI $original_uri;
}
# Client requests
location / {
auth_request /lmauth;
set $original_uri $uri$is_args$args;
auth_request_set $lmremote_user $upstream_http_lm_remote_user;
auth_request_set $lmlocation $upstream_http_location;
error_page 401 $lmlocation;
proxy_pass http://application-proxy.example.com/;
include /etc/nginx/proxy_params;
##################################
# PASSING HEADERS TO APPLICATION #
##################################
# IF LUA IS SUPPORTED
#include /path/to/nginx-lua-headers.conf;
# ELSE
# Set manually your headers
#auth_request_set $authuser $upstream_http_auth_user;
#proxy_set_header HTTP_AUTH_USER $authuser;
}
}
If the application being protected by Lemonldap::NG and also requires AUTH-USER or other HTTP headers sent to the application during proxy_pass directive make sure use proxy_set_header in the nginx configuration above.
When the docker deployment directly exploses Lemonldap::NG with nginx the following configuration is required:
include /etc/nginx/proxy_params;
When a reverse proxy is used to redirect to the containerised instance of Lemonldap:NG additional configuration is required.
The following Nginx configurations needs to added and modified:
#include /etc/nginx/proxy_params
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_pass_request_headers on;
Meanwhile the following Apache2 (httpd) configurations need to be created:
<VirtualHost *:80>
ServerName application.example.com
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
</VirtualHost>
<VirtualHost *:443>
ServerName application.example.com
CustomLog /var/log/httpd/application.log combined
SSLEngine on
SSLCertificateChainFile "/path/to/application.example.com.cacert.cert"
SSLCertificateFile "/path/to/application.example.com.cert"
SSLCertificateKeyFile "/path/to/application.example.com.key"
ProxyPreserveHost On
ProxyPass / http://localhost:8080/
ProxyPassReverse / http://application.example.com:8080/
</VirtualHost>
Note
Use of directory volumes mounts or docker volumes ensure safe storage of persistent configurations and important files.