This solution enables granular access control to proxied nginx sites, based on LDAP directory data.
- LDAP-based authentication
- Access control based on LDAP group membership
- Custom login page - instead of basic authentication popup
- Custom authorization error page (403)
- Configurable pass-through based on request source address (e.g. to allow unrestricted access inside internal network)
Clone this repo and build a docker image:
git clone https://github.com/kbancerz/nginx-ldap-auth.git && cd nginx-ldap-auth
docker build -t nginx-ldap-auth .
Generate a sample config file:
docker run --rm nginx-ldap-auth ./nginx-ldap-auth-server get-sample-config > config.json
Maintain the file as described in 'Configuration' section and create a docker container:
docker run --rm -p 8088:8088 -v ./config.json:/usr/src/app/config.json nginx-ldap-auth
or alternatively use docker-compose:
docker-compose up -d
This will open authentication server on port 8088.
Server requires configuration file to be maintained. It is divided into several different sections:
- host (e.g. "ldap.example.com") - LDAP server host
- port (e.g. 389) - LDAP server port
- username (e.g. "admin") - LDAP read-only account
- password (e.g. "password") - LDAP account password
- user_attr (e.g. "uid") - LDAP user attribute used as login during authentication
- user_base_dn (e.g. "ou=users,dc=example,dc=com") - base DN for user search
- group_base_dn (e.g. "ou=groups,dc=example,dc=com") - base DN for group search
- group_cache_max_age (e.g. 300) - server caches list of groups of which an account used for authentication is member of, set this to change invalidation period (in seconds)
- cookie_session (e.g. "EXAMPLE_COM_SSO") - name of session cookie
- cookie_redirect (e.g. "EXAMPLE_COM_REDIRECT") - name of redirection cookie (holds referrer URL)
- cookie_domain (e.g. ".example.com") - cookie domain - use it to change the scope of the session cookie
- cookie_secret (e.g. "4ZRL5a0emNFTMI7wn1PBe1JWXlZ59C5D") - secret, that is used to encrypt session information
- cookie_max_age (e.g. 86400) - max age of the cookie:
- if a positive number - treated as a number of seconds
- if zero or a negative number - treated as a number of full days of validity before expiring at 23:59:59 - e.g. if set to -1, then a cookie created at 15:00 will expire at midnight of the next day (one full day + 9h)
- login_template (e.g. "./pages/templates/login.html.j2") - path to login page template (Bottle template language)
- noauth_template (e.g. "./pages/templates/noauth.html.j2") - path to 403 page template (Bottle template language)
- static_root (e.g. "./pages/static") - path to static files root folder
- fallback_redirect (e.g. "https://internal.example.com") - redirection fallback address - if redirection cookie is not set (e.g. login page was accessed directly), a user will be redirected to this address after successfully logging in
- ignored_ingress (e.g. ["192.168.0.1"]) - list of ingress addresses (server side), for which authentication should not be checked (e.g. when accessed from within an internal network/VPN/etc.)
- ignored_remote (e.g. ["192.168.100."]) - list of remote hosts (client side), for which authentication should not be checked (e.g. when accessed from within an internal network/VPN/etc.)
When the server is running, nginx needs to be configured to utilize authentication service provided.
Sample configuration snippets are presented below - each can be placed as a separate config file in /etc/nginx directory and referred to with include clause.
For each server configuration clause a set of generic locations needs to be defined:
auth_locations.conf:
location /__auth {
internal;
proxy_pass_request_body off;
proxy_set_header Host $host;
proxy_set_header Content-Length "";
proxy_set_header X-LDAP-AUTH-USERS $ldap_allowed_users;
proxy_set_header X-LDAP-AUTH-GROUPS $ldap_allowed_groups;
proxy_set_header X-LDAP-AUTH-INGRESS $server_addr;
proxy_set_header X-LDAP-AUTH-REMOTE $remote_addr;
proxy_cache auth;
proxy_cache_valid 200 1m;
proxy_cache_key "$cookie_eccsso$server_addr$host";
proxy_pass http://localhost:8088/auth;
}
location @access_error401 {
add_header Set-Cookie "EXAMPLE_COM_REDIRECT=$scheme://$http_host$request_uri;Domain=.example.com;Path=/";
return 302 http://access.example.com/;
}
location @access_error403 {
add_header Set-Cookie "EXAMPLE_COM_REDIRECT=$scheme://$http_host$request_uri;Domain=.example.com;Path=/";
return 302 http://access.example.com/noauth;
}
The first location is the actual authentication server. For each incoming request nginx will access this location first, to authenticate the user. Please notice, that ldap_allowed_users and ldap_allowed_groups parameters are used here. These can be set for each resource separately. Multiple values are separated with a semicolon. Authentication flow is following:
- Check login and password
- if incorrect - return 401
- if correct - continue
- Check user name
- if ldap_allowed_users is defined and user is on the list - authorize the user, return 200
- if ldap_allowed_users is not defined or user is not on the list - continue
- Check user group membership
- if ldap_allowed_groups is defined and user is a member of any of the defined groups - authorize the user, return 200
- if ldap_allowed_groups is defined and user is not a member of any of the defined groups - unauthorized, return 403
- if ldap_allowed_groups is not defined - authorize the user, return 200
Auth server responses are cached - please check Caching section for more details.
The second location is a generic response to a 401 status returned by the auth server - this will set the redirection cookie to the referring URL and send the user to the login page.
Last location is a response to a 403 - unauthorized error - in this case user is sent to the error page on the auth server. On this page optional information can be shown, so that the user can - for example - contact administrators and request access.
Please notice, that error locations set the redirection cookie, so make sure, that the name (here EXAMPLE_COM_REDIRECT) matches the one set in the auth server configuration - cookie_redirect. The same goes for the domain (here: .example.com), make sure it matches cookie_domain parameter.
Both login and 403 error page need to be accessible to the end-users - in this example they are proxied and available at http://access.example.com.
Access control is enabled by adding configuration a snippet to the server clause:
auth_enable.conf:
auth_request /__auth;
error_page 401 = @access_error401;
error_page 403 = @access_error403;
A sample website configuration with access restriction enabled:
server {
listen 80;
server_name internal.example.com;
location / {
# enable access control for this location - notice,
# that this is set location-wide - thanks to this,
# additional locations can be set, without LDAP access
# control - useful for WebService or REST endpoints etc.
include /etc/nginx/auth_enable.conf;
root /var/www/html;
index index.html;
}
set $ldap_allowed_users "cn=User1,ou=Users,dc=example,dc=com;cn=User2,ou=Users,dc=example,dc=com";
set $ldap_allowed_groups "cn=Internal Users,ou=Groups,dc=example,dc=com";
# include the set of generic auth locations
include /etc/nginx/auth_locations.conf;
}
In this sample configuration access to this internal site will be granted only to:
- User1
- User2
- any member of the Internal Users group
To limit performance hit, authentication server responses are cached by nginx - this is set in the /__auth location definition. Caching requires setting up the file system caching location - to do this, set it in the nginx.conf file, http section by adding e.g.:
proxy_cache_path /etc/nginx/cache/auth levels=1:2 keys_zone=auth:10m max_size=1g inactive=60m use_temp_path=off;
This will allow nginx limit the requests sent to the auth server.