goauthentik / authentik

The authentication glue you need.
https://goauthentik.io
Other
13.25k stars 884 forks source link

Forward auth with HAProxy #5768

Open schklom opened 1 year ago

schklom commented 1 year ago

https://goauthentik.io/docs/providers/proxy/forward_auth lists only forward-auth setups with Nginx, Caddy, Envoy, and Traefik. I use HAProxy, and am not sure how to set it up, so I cannot use Authentik yet.

Authelia has a setup with HAProxy. Could you write up something about this reverse-proxy as well?

vherrlein commented 1 year ago

Hi, Checkout that module for HAProxy which allow you to manage authentication with ldap or OIDC: https://github.com/criteo/haproxy-spoe-auth/blob/master/README.md

then you’ll have just to setup the target provider accordingly.

schklom commented 1 year ago

Not a bad idea, but it looks like this requires to run an extra service on top of HAProxy and Authentik.

Authelia simply requires to add a few Lua scripts to HAProxy, which HAProxy handles natively, and it works with OIDC pretty well.

I use HAProxy directly on PfSense, with Authelia (Authentik when I switch) on a Raspberry Pi, and would prefer to avoid involving another service.

vherrlein commented 1 year ago

Nothing in addition within Authentik, only setting up the proper Provider (OIDC or LDAP).

otherwise, if you want to use OIDC with HAProxy, look at that guide for keycloak. https://mackdanz.net/Add-OIDC-single-sign-on-to-any-website-with-HAProxy-and-Keycloak Easy to adapt for Authentik by following the setup doc here regarding to OIDC settings : https://goauthentik.io/docs/providers/oauth2/

note: could be even simpler with that lua script https://github.com/haproxytech/haproxy-lua-oauth

schklom commented 1 year ago

Nothing in addition within Authentik, only setting up the proper Provider (OIDC or LDAP).

But it is an extra service to run with Docker. I am looking for Authentik to do like it does with other reverse-proxies: by indicating how to let HAProxy delegate authentication to Authentik.

In order to do Forward-Auth with Traefik and Authentik, you don't need any other Docker container to run: https://goauthentik.io/docs/providers/proxy/server_traefik. Authelia does the same thing with HAProxy, but Authentik does not, which is my issue.

thimplicity commented 1 year ago

I would love to see that as well. I am running HAProxy as my reverse proxy and having the configuration shared like you guys do for traefik, npm, caddy would be great.

schklom commented 1 year ago

@thimplicity FYI, usually, devs prefer when people give a thumbs up or similar emoji to the first post. It makes it easier for them to see how much reaction an issue gets rather than counting all reaction messages.

miccico commented 1 year ago

Hi all!

I got forward auth working with haproxy and authentik. I took most of the source from authelia samples and modified the parameters to play well with authentiks nginx outpost.

Take lua files and general preconditions from here: https://github.com/authelia/authelia/tree/master/internal/suites/example/compose/haproxy

within your haproxy.conf frontend place:

http-request set-var(req.scheme) str(http) if !{ ssl_fc }
http-request set-var(req.questionmark) str(?) if { query -m found }

http-request set-header X-Real-IP %[src]
http-request set-header X-Forwarded-Method %[method]
http-request set-header X-Forwarded-Proto  %[var(req.scheme)]
http-request set-header X-Forwarded-Host   %[req.hdr(Host)]
http-request set-header X-Original-URL     %[url]

acl protected-frontends hdr(host) -m reg -i ^(?i)(aaaa|bbbb|cccc)\.xx\.xxxx\.xx

acl is_authentikoutpost path -m reg ^/outpost.goauthentik.io/

# websockets break if all headers are passed to be_auth_requests as the Upgrade header screws the process up - so we pass manual list
http-request lua.auth-intercept be_auth_request /outpost.goauthentik.io/auth/nginx HEAD x-original-url,x-real-ip,x-forwarded-host,x-forwarded-proto,user-agent,cookie,accept,x-forwarded-method x-authentik-username,x-authentik-uid,x-authentik-email,x-authentik-name,x-authentik-groups - if protected-frontends !is_authentikoutpost

http-request redirect code 302 location /outpost.goauthentik.io/start?rd=%[hdr(X-Original-URL)] if protected-frontends !{ var(txn.auth_response_successful) -m bool } { var(txn.auth_response_code) -m int 401 } !is_authentikoutpost
http-request deny if protected-frontends !{ var(txn.auth_response_successful) -m bool } { var(txn.auth_response_code) -m int 403 } !is_authentikoutpost
http-request redirect location %[var(txn.auth_response_location)] if protected-frontends !{ var(txn.auth_response_successful) -m bool } !is_authentikoutpost
use_backend be_auth if protected-frontends is_authentikoutpost

and the required backends:

backend be_auth_request
    server proxy 127.0.0.1:8085

listen be_auth_request_proxy
    bind :::8085 v4v6
    server authentikhost authentikhost.xxx.xxx.xx:8443 ssl verify none check

within authentik set up a provider configured as Forward auth (single application) and input your domain and you should be good to go. Disclaimer: I am no expert in this - if you see any obvious errors or security holes please point them out :)

best regards

Michael

hangmanandhide commented 1 year ago

Hi Michael,

I'm pretty inexperienced with HAProxy but was told to use it and now I'm in the weeds trying to get it to work with authentik. Any chance you can give me a hand with this, either with screenshots or more explicit instructions? I basically got into this mess following Laurence Systems youtube videos for HAProxy and ACME and pfsense. So I am about as ignorant as it comes with this and unfortunately i dont have the time for the forseeable future to redo everything i did with other proxies to something like npm. Ive got sftp'd into my pfsense box and made a backup of my haproxy.cfg. Could you give me something more to work with so I can get this setup? Id be really in your debt.

EDIT: I put a link to my discord profile in my github social links if you want to try and reach out.

LojDev commented 6 months ago

did anyone else ever get this working on pfsense with haproxy? tried the above by @miccico with no success

raphaeldeveloperberlin commented 5 months ago

@LojDev maybe i can help. I implemented the set-up - Ha-Proxy (Pfsense) | Traefik | Authentik.

Where did you get stuck?

I had some problem with pfsense GUI and how to set-up the "lua.auth-intercept" and rules for "ha-proxy". Following post (not related to the exact topic here) helped me to understand it. How to configure lua.auth-intercept in pfsense

To upload the file to pfsense through the GUI: Command Prompt -> Upload (pfsense upload it to /tmp/[Filename]). Then cp to destination: cp /tmp/[Filename] /path/to/destination

I used the set up @miccico, but changed the lua script because it hasn't set the response headers properly. So i used wildcards. http-request lua.auth-intercept bmAuthentik_ipvANY /outpost.goauthentik.io/auth/traefik HEAD "*" "*" "*" - if protected-frontends !is_authentikoutpost

LojDev commented 4 months ago

hey @raphaeldeveloperberlin,

When I attempted it, I couldnt get authentik to load at all after following what miccico shared.

I already had authelia setup as my frontend auth provider so i already had the json.lua, auth-request.lua and haproxy-lua-http.lua files setup in haproxy however i replaced the auth-request.lua and haproxy-lua-http.lua with the ones from the link miccico shared because I belive i saw some differences in those ones.

Then after a few days of messing around I got authentik to load by not using the is_authentikopupost acl. I created a custom acl here hdr(host) -m reg -i ^(?i)([\w\d\-]*[\.]*)*([\.]?com)((\/outpost.goauthentik.io)[\/]?)? and by using this acl got authentik to load but then I ran into another issue where whenever I would authenticate, it would not redirect to the service i was trying to access, authentik would always redirect to itself. I believe I had to slightly modify some more of the info that miccico posted but i forgot those modifications as I did not really document the changes i made apart from that custom acl i made.

And thats where I stopped and gave up and moved back to authelia. I have not tried to get authentik working with haproxy since then.

If you could post some pictures of how you have the acl's and actions setup in pfsense, that may be of some help.

BeryJu commented 4 months ago

@miccico Feel free to open a PR to add these instructions to the docs, or if you're ok with we can also add it to the docs

SamB-GB commented 4 months ago

How to configure lua.auth-intercept in pfsense

Would you be able to post some pictures of your setup please @raphaeldeveloperberlin

JJ-Ge commented 2 months ago

So, I've got this semi working, pretty sure miccico's config has at least one error

use_backend be_auth (on the last line) should be:
use_backend be_auth_request 

Here's my full haproxy config:

global
        log /dev/log    local0
        log /dev/log    local1 notice
        chroot /var/lib/haproxy
        stats socket /run/haproxy/admin.sock mode 660 level admin
        stats timeout 30s
        user haproxy
        group haproxy
        daemon
        maxconn 40000
        ulimit-n 81000

        # Default SSL material locations
        ca-base /etc/ssl/certs
        crt-base /etc/ssl/private

        # See: https://ssl-config.mozilla.org/#server=haproxy&server-version=2.0.3&config=intermediate
       ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
        ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
        ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets

       # Crowdsec bouncer
       lua-prepend-path /usr/lib/crowdsec/lua/haproxy/?.lua
       lua-load /usr/lib/crowdsec/lua/haproxy/crowdsec.lua
       setenv CROWDSEC_CONFIG /etc/crowdsec/bouncers/crowdsec-haproxy-bouncer.conf

       # Authentik Auth Lua files
       lua-prepend-path /usr/local/share/lua/5.3/?.lua
       lua-load /usr/local/share/lua/5.3/auth-request.lua

defaults
        log     global
        mode    http
        option  httplog
        option  dontlognull
        option forwardfor
        timeout connect 30s
        timeout client  30s
        timeout server  5s
        errorfile 400 /etc/haproxy/errors/400.http
        errorfile 403 /etc/haproxy/errors/403.http
        errorfile 408 /etc/haproxy/errors/408.http
        errorfile 500 /etc/haproxy/errors/500.http
        errorfile 502 /etc/haproxy/errors/502.http
        errorfile 503 /etc/haproxy/errors/503.http
        errorfile 504 /etc/haproxy/errors/504.http

listen stats
    bind *:8404
    stats enable
    stats hide-version
    stats realm Haproxy\ Statistics
    stats uri /haproxy_stats
    stats auth HAProxy:Password

# Frontend to redirect HTTP to HTTPS with code 301
frontend http-redirect
    bind *:80
    http-request redirect scheme https code 301
    acl letsencrypt-acl path_beg /.well-known/acme-challenge/
    use_backend letsencrypt-backend if letsencrypt-acl

# LE Backend
backend letsencrypt-backend
    server letsencrypt 127.0.0.1:8888

# Frontend for redirecting traffic to the required frontend
frontend https-redirect
    bind *:443 ssl crt /etc/ssl/home.MYDOMAIN.com.pem
    mode tcp
    option tcplog
    tcp-request inspect-delay 5s
    tcp-request content accept if { req_ssl_hello_type 1 }
    acl internal src 0.0.0.0/24 #IP RANGE OF MY NETWORK

    use_backend internal if internal

# Frontend for users
frontend internal
    bind abns@internal accept-proxy
    # Crowdsec bouncer
    stick-table type ip size 10k expire 30m # declare a stick table to cache captcha verifications
    http-request lua.crowdsec_allow # action to identify crowdsec remediation
    http-request track-sc0 src if { var(req.remediation) -m str "captcha-allow" } # cache captcha allow decision
    http-request redirect location %[var(req.redirect_uri)] if { var(req.remediation) -m str "captcha-allow" } # redirect to initial url
    http-request use-service lua.reply_captcha if { var(req.remediation) -m str "captcha" } # serve captcha template if remediation is captcha
    http-request use-service lua.reply_ban if { var(req.remediation) -m str "ban" } # serve ban template if remediation is ban

    #Authentik config
    acl protected-frontends hdr(host) -m reg -i ^(?i)(arrs|downloads)\.(home|media)\.home\.MYDOMAIN\.com
    acl is_authentikoutpost path -m reg ^/outpost.goauthentik.io/

    http-request set-var(req.scheme) str(http) if !{ ssl_fc }
    http-request set-var(req.questionmark) str(?) if { query -m found }

    http-request set-header X-Real-IP %[src]

    http-request set-header X-Forwarded-Method %[method]
    http-request set-header X-Forwarded-Proto  %[var(req.scheme)]
    http-request set-header X-Forwarded-Host   %[req.hdr(Host)]
    http-request set-header X-Original-URL     %[url]
    # websockets break if all headers are passed to be_auth_requests as the Upgrade header screws the process up - so we pass manual list
    http-request lua.auth-intercept be_auth_request /outpost.goauthentik.io/auth/nginx HEAD x-original-url,x-real-ip,x-forwarded-host,x-forwarded-proto,user-agent,cookie,accept,x-forwarded-method x-authentik-username,x-authentik-uid,x-authentik-email,x-authentik-name,x-authentik-groups - if protected-frontends !is_authentikoutpost

    http-request redirect code 302 location /outpost.goauthentik.io/start?rd=%[hdr(X-Original-URL)] if protected-frontends !{ var(txn.auth_response_successful) -m bool } { var(txn.auth_response_code) -m int 401 } !is_authentikoutpost
    http-request deny if protected-frontends !{ var(txn.auth_response_successful) -m bool } { var(txn.auth_response_code) -m int 403 } !is_authentikoutpost
    http-request redirect location %[var(txn.auth_response_location)] if protected-frontends !{ var(txn.auth_response_successful) -m bool } !is_authentikoutpost

    # Select backend based on services.map file or use backend no-match if not found.
    use_backend be_auth_request if protected-frontends is_authentikoutpost
    use_backend %[base,lower,map_beg(/etc/haproxy/services.map,no-match)]
    use_backend %[req.hdr(host),lower,map(/etc/haproxy/services.map,no-match)]

backend internal
    mode tcp
    server loopback-for-tls abns@internal send-proxy-v2

backend auth
    server auth home:9443 ssl verify none check

backend be_auth_request
    server proxy home:9000 check

listen be_auth_request_proxy
    bind :9000
    server be_auth_request home:9443 ssl verify none check

listen ldap_proxy
    bind :389
    server proxy home:389 check

backend ldap
    server be_auth_request home:389 check

# Normal Backends
backend no-match
    http-request deny deny_status 403

backend homedash
    server homedash home:81 check

backend hassio
    server hassio home:8123 check

backend downloads
    server downloads home:8200 check
backend prowlarr
    server prowlarr home:9696 check
backend radarr
    server radarr home:7878 check
backend sonarr
    server sonarr home:8989 check
backend arrs
    server arrs home:5055 check

# Backend for google to allow DNS resolution if using reCAPTCHA
backend captcha_verifier
    server captcha_verifier www.google.com:443 check

# Backend for crowdsec to allow DNS resolution
backend crowdsec
    server crowdsec *:8080 check

My services.map file:

home.MYDOMAIN.com homedash
#
auth.home.MYDOMAIN.com auth
auth.home.MYDOMAIN.com be_auth_request
home.home.MYDOMAIN.com hassio
#
arrs.media.home.MYDOMAIN.com arrs
#
downloads.media.home.MYDOMAIN.com downloads
arrs.media.home.MYDOMAIN.com/prowlarr prowlarr
arrs.media.home.MYDOMAIN.com/radarr radarr
arrs.media.home.MYDOMAIN.com/sonarr sonarr

A few notes:

  1. If someone chooses to emulate this config, crowdsec can either be omitted or configured using this as a guide: https://ciphermenial.github.io/posts/haproxy-crowdsec/ (just omit the Cloudflare part unless you need it). Otherwise all that needs changing is MYDOMAIN and the ip range for the acl

  2. I still haven't figured out how to translate:

proxy_set_header X-ak-hass-user $X_ak_hass_user;
auth_request_set $X_ak_hass_user $upstream_http_x_ak_hass_user;

To haproxy

  1. I have some entries for LDAP but I have yet to get it actually working (my dash is organizr configured for LDAP auth, but I always get an api error when trying to login via domain but not via local ip, I'll update this if I get it working (I really hope I do)

So anyway, this is far as I've got, if anyone has any solutions/fixes for 2 and 3 it'd be most welcomed, if not maybe this will get a few people further than they've got so far.