Support Traefik forwardAuth
Summary
Traefik is a popular reverse proxy especially in the containers world. It supports a forwardAuth mecanism which is quite similar to the one used for nginx. Its documentation is available here : https://doc.traefik.io/traefik/middlewares/http/forwardauth/
I was able to use the LL::NG's handler with Traefik's forwardAuth middleware on a PoC installation
Running the handler with uWSGI exposing an HTTP socket binding on 127.0.0.1:8183. My full uwsgi command is
cd /usr/share/lemonldap-ng/llng-server
/sbin/uwsgi \
--plugin psgi \
--psgi llng-server.psgi \
--master \
--workers 2 \
--max-worker-lifetime 86400 \
--max-requests 10000 \
--disable-logging \
--harakiri 30 \
--buffer-size 65535 \
--limit-post 0 \
--die-on-term \
--http-socket 127.0.0.1:8183
And then configuring the middleware in traefik with this conf fragment (note : in my case, Traefik and LL::NG's handlers are running in the same Nomad group, which would be equivalent to the same pod in K8s world, so they share the same network namespace, and the handler is available on 127.0.0.1 from traefik's POV)
http:
middlewares:
lemonldap:
forwardAuth:
address: http://127.0.0.1:8183
authResponseHeadersRegex: '^.*$'
To protect an app with Lemonldap::NG, I just have to add something like this in my tags/labels (here it's a Nomad job file, but you get the idea)
tags = [
"traefik.enable=true",
"traefik.http.routers.whoami.rule=Path(`/whoami`)",
"traefik.http.routers.whoami.entrypoints=https",
"traefik.http.routers.whoami.middlewares=lemonldap@file"
]
As far as I've tested (only little tests for now, and only the handler), a few things differ between Nginx and Traefik auth forward mecanism
- Nginx expects the original Host header to be transmited in the auth forward request as Host, while Traefik set it in the X-Forwarded-Host header
- Same thing for the REQUEST_URI, which is transmited by Traefik in the X-Forwarded-URI header
- Last, Nginx doesn't like 302/303 responses, and so LL::NG's handler intercept those codes and replace them with a 401 (in Lemonldap/NG/Handler/Server/Nginx.pm line 39). This is not the case with Traefik, which does require the original 302/303 response code to correctly redirect the user on the portal.
All in all, the only changes I've made to be able to use the handler with Traefik are :
sed -i -e 's/HTTP_HOST/HTTP_X_FORWARDED_HOST/g' \
-e 's/REQUEST_URI/HTTP_X_FORWARDED_URI/g' \
/usr/share/perl5/vendor_perl/Lemonldap/NG/Handler/Main/Run.pm
# Yes, this is silly, I'm just using it as a one liner in a container entrypoint wrapper so I could run some tests
sed -i -e 's/401/302/g' /usr/share/perl5/vendor_perl/Lemonldap/NG/Handler/Server/Nginx.pm
Design proposition
Not sure if Traefik should be on it's own handler. As it's very close to the nginx's one, it seems they could be shared in a single handler (in which case there should be some settings available for LL::NG to read Host and URI from the correct place, and don't replace 302/303 with 401 code.
In anycase, adding support for Traefik would be a huge plus for LL::NG :-)