Simple LDAP Authentication for NGINX
Written by Dominik Pantůček on 2024-08-15
Apache Web Server users can setup HTTP authentication against LDAP by installing and configuring appropritate module. For NGINX users the life can get more complicated when they need such feature. However, there is an easy solution.
Although NGINX does not have the LDAP authentication module like nginx does not have LDAP auth module like Apache does, there is an easy way of achieving the same functionality. As NGINX can delegate authorization requests that may include user authentication to other services, all that is needed is a simple HTTP responder, that can parse the required HTTP request headers and provide appropriate HTTP response headers.
It is even possible to implement such service as very simple standalone TCP server
written in POSIX.1 shell. Of course,
such solution needs at least ldapsearch
from the ldap-utils
package of OpenLDAP. An example of a script
processing single auth request can look like this:
#!/bin/sh
AUTHORIZATION=""
while read line ; do
cline=$(echo "$line" | sed 's/[\r\n\x0a]//g')
key=${cline%%: *}
value=${cline#*: }
if [ "$key" = "Authorization" ] ; then
AUTHORIZATION=${value}
fi
if [ -z "$cline" ] ; then
break
fi
done
atype=${AUTHORIZATION%% *}
avalue=${AUTHORIZATION#* }
dvalue=$(echo "$avalue" | base64 -d)
username=${dvalue%%:*}
password=${dvalue#*:}
BASEDN="ou=Users,dc=example,dc=com"
if ldapsearch -H ldap://ldap.example.com:389 -b $BASEDN -D "uid=$username,$BASEDN" -w "$password" "uid=$username" >/dev/null 2>&1 ; then
echo $username Auth OK >&2
printf "HTTP/1.0 200 OK\r\n"
else
echo $username Auth NOT OK >&2
printf "HTTP/1.0 401 Unauthorized\r\n"
printf "WWW-Authenticate: Basic realm=Example"
fi
printf "Content-Type: text/plain\r\n"
printf "\r\n"
printf "Hello World!\r\n"
For clarity the printf
program from GNU Coreutils is used.
This script expects the HTTP request headers on its standard input and produces the HTTP response (headers and perhaps a body) as its standard output. The only question that remains is - how to turn this into a standalone service?
Let's assume the aforementioned script is available at
/usr/local/bin/ldap-auth-cgi.sh
.
With nc
(netcat) version that can fork and execute scripts while
keeping the original listen socket, well, listening, it is just a single line of
another shell script:
#!/bin/sh
ncat -l 127.0.0.1 8888 -e /usr/local/bin/ldap-auth-cgi.sh -k
This was tested with OpenBSD's version of nc.
The script may be stored for example in /usr/loca/bin/ldap-auth-http.sh
and run as SystemD service. With Puppet, such
service is really easy to configure:
'ldap-auth-http':
exec: '/usr/local/bin/ldap-auth-http.sh'
user: 'nobody'
And with this service up and running, configuring NGINX to delegate the authorization requests to perform authentication is (again with Puppet) very easy:
locations:
'ldap-auth':
location: '/auth'
location_cfg_append:
proxy_pass: 'http://127.0.0.1:8888/'
proxy_pass_request_body: 'off'
location_cfg_append:
auth_request: '/auth'
Yes, virtual location /auth
proxying the requests to the tiny auth
service and then just delegating all auth_request
s to this virtual
location. That is all what is needed and as they say: security requires simplicity -
otherwise it is hard to tell what even the attack surface area may be.
Although today it was a simple one, hopefully someone will find this information useful when they need to quickly secure access to NGINX websites. See you next time!