authentik icon indicating copy to clipboard operation
authentik copied to clipboard

Forward auth with HAProxy

Open schklom opened this issue 2 years ago • 14 comments

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?

schklom avatar May 26 '23 15:05 schklom

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.

vherrlein avatar Jun 02 '23 06:06 vherrlein

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.

schklom avatar Jun 03 '23 12:06 schklom

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

vherrlein avatar Jun 03 '23 13:06 vherrlein

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.

schklom avatar Jun 05 '23 01:06 schklom

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.

thimplicity avatar Jun 11 '23 02:06 thimplicity

@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.

schklom avatar Jun 11 '23 03:06 schklom

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

miccico avatar Jul 18 '23 20:07 miccico

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.

hangmanandhide avatar Aug 29 '23 00:08 hangmanandhide

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

LojDev avatar Apr 06 '24 05:04 LojDev

@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

raphaeldeveloperberlin avatar May 21 '24 08:05 raphaeldeveloperberlin

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.

LojDev avatar May 21 '24 19:05 LojDev

@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

BeryJu avatar May 23 '24 16:05 BeryJu

How to configure lua.auth-intercept in pfsense

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

SamB-GB avatar Jun 10 '24 20:06 SamB-GB

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.

JJ-Ge avatar Aug 15 '24 12:08 JJ-Ge

Hi,

Thanks a lot to @JJ-Ge and @miccico for sharing their conf. that helped me tremendously.

As of 2024.8 i was also able to make this work using HAProxy.

The main tweak was to use a secondary proxy outpost using https://docs.goauthentik.io/docs/add-secure-apps/outposts/manual-deploy-docker-compose

Happy to share the details if there's any interest for it.

Cheers

CloudGoesPwn avatar Nov 01 '24 05:11 CloudGoesPwn

Hey @CloudGoesPwn, would it be possible for you to please write down all the steps you followed? I am kind of new to this. Thank you!

anuneo avatar Nov 19 '24 06:11 anuneo

@anuneo sure thing - I've actually reviewed my conf. and finally got rid of the secondary proxy outpost.

For clarity, here's a simplified version of my setup and some assumptions:

  1. Network

    • My authentik instance is sso.mydomain.com
    • sso.mydomain.com resolves to 123.123.123.123
    • haproxy listens on 123.123.123.123:80 and 123.123.123.123:443
    • authentik is on the same host and listens on localhost:9000 and localhost:9443
    • The service (MyService) we want to setup the forward auth. for is on a different host, and has for address 192.168.1.10/24, the HAProxy / Authentik host can reach the service on 192.168.1.10:443
    • The service will be available at myservice.mydomain.com (and myservice.mydomain.com resolves to 123.123.123.123)
    • A valid certificate for *.mydomain.com
  2. Authentik

  • Using the default docker compose file from https://goauthentik.io/docker-compose.yml
  • Create a Provider (adjust as needed, unspecified parameters have been left to their default values)
    • Name: MyServiceProvider
    • Authorization flow: default-provider-authorization-implicit-consent
    • Forward auth (single application)
    • External host https://myservice.mydomain.com
  • Create an Application (adjust as needed, unspecified parameters have been left to their default values)
    • Name: MyService
    • Slug: myservice
    • Provider: MyServiceProvider
  • Review Default Proxy Outpost (authentik Embedded Outpost)
    • Selected Applications: MyService
    • Advanced Configuration:
      • Make sure that authentik_host is set accordingly, in this example:
log_level: info
docker_labels: null
authentik_host: https://sso.mydomain.com
docker_network: null
container_image: null
docker_map_ports: true
refresh_interval: minutes=5
kubernetes_replicas: 1
kubernetes_namespace: default
authentik_host_browser: ""
object_naming_template: ak-outpost-%(name)s
authentik_host_insecure: false
kubernetes_json_patches: null
kubernetes_service_type: ClusterIP
kubernetes_image_pull_secrets: []
kubernetes_ingress_class_name: null
kubernetes_disabled_components: []
kubernetes_ingress_annotations: {}
kubernetes_ingress_secret_name: authentik-outpost-tls
  1. Haproxy

Get the lua files from authelia

  • auth-request.lua: https://github.com/authelia/authelia/tree/master/internal/suites/example/compose/haproxy
  • http.lua: https://github.com/authelia/authelia/tree/master/internal/suites/example/compose/haproxy

and an extra one from https://github.com/rxi/json.lua

  • json.lua

I placed these under /etc/haproxy/lua/ altough any path should work as long as the haproxy config has the same path as well in the global section.

At some point it seems that I installed the lua-socket package as well.. but i wonder if that's necessary.

Then for the haproxy.cfg itself:

#---------------------------------------------------------------------
# Global settings
#---------------------------------------------------------------------
global
    # intermediate configuration mozilla
    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:DHE-RSA-CHACHA20-POLY1305
    ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
    ssl-default-bind-options prefer-client-ciphers no-sslv3 no-tlsv10 no-tlsv11 no-tls-tickets

    # curl https://ssl-config.mozilla.org/ffdhe2048.txt > /path/to/dhparam
    ssl-dh-param-file /etc/haproxy/dhparam

    log         127.0.0.1 local2

    chroot      /var/lib/haproxy
    pidfile     /var/run/haproxy.pid
    maxconn     4000
    user        haproxy
    group       haproxy

    # Authentik Auth Lua files    
    lua-prepend-path /etc/haproxy/lua/?.lua
    lua-load /etc/haproxy/lua/auth-request.lua

#---------------------------------------------------------------------
# common defaults that all the 'listen' and 'backend' sections will
# use if not designated in their block
#---------------------------------------------------------------------
defaults
    log                     global
    option                  httplog
    option                  dontlognull
    option http-server-close
    option forwardfor       except 127.0.0.0/8
    option                  redispatch
    retries                 3
    timeout http-request    10s
    timeout queue           1m
    timeout connect         10s
    timeout client          1m
    timeout server          1m
    timeout http-keep-alive 10s
    timeout check           10s
    maxconn                 3000

#---------------------------------------------------------------------
# frontend which proxys to the backends
#---------------------------------------------------------------------

frontend http_front
    bind *:80
    mode http
    # Redirect HTTP requests to HTTPS
    redirect scheme https code 301 if !{ ssl_fc }

# Frontend for HTTPS traffic
frontend https_front
    bind *:443 ssl crt /etc/ssl/mydomain.com.pem alpn h2,http/1.1 #REPLACE
    mode http
    option httplog

    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) -i myservice.mydomain.com #REPLACE
    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_request if protected-frontends is_authentikoutpost

    # Define ACL and backend
    acl is_sso_host hdr(host) -i sso.mydomain.com #REPLACE
    use_backend sso_backend if is_sso_host

    acl is_myservice_host hdr(host) -i myservice.mydomain.com #REPLACE
    use_backend myservice_backend if is_myservice_host

    # Set Strict-Transport-Security header for all HTTPS responses
    http-response set-header Strict-Transport-Security "max-age=63072000"

#---------------------------------------------------------------------
# backends
#---------------------------------------------------------------------
backend be_auth_request
    mode http
    server proxy 127.0.0.1:8085

listen be_auth_request_proxy
    mode http
    bind :::8085 v4v6
    server sso_server localhost:9443 check ssl verify none #REPLACE

backend sso_backend
    mode http
    server sso_server localhost:9443 check ssl verify none  #REPLACE

# ----

backend myservice_backend
    mode http
    server myservice_server 192.168.1.10:443 check #REPLACE

Make sure to adjust as needed the lines with the #REPLACE comment

CloudGoesPwn avatar Nov 22 '24 04:11 CloudGoesPwn

Thank you @CloudGoesPwn for the detailed steps. I tried to follow them. As soon as I enter the following in the global settings:

lua-prepend-path /etc/haproxy/lua/?.lua
lua-load /etc/haproxy/lua/auth-request.lua

I get the following error: image

Does this have something to do with this?:

At some point it seems that I installed the lua-socket package as well.. but i wonder if that's necessary.

If yes, then how can I install lua-socket?

Thank you!

anuneo avatar Nov 22 '24 18:11 anuneo

Hey @anuneo ,

That actually reminds me of something... Try renaming /etc/haproxy/lua/http.lua in /etc/haproxy/lua/haproxy-lua-http.lua

CloudGoesPwn avatar Nov 22 '24 19:11 CloudGoesPwn

Hey @CloudGoesPwn, Thanks, that helped. However I got stuck at the next step. Where / how can I specify this listen block via the pfsense HAProxy GUI?

listen be_auth_request_proxy
    mode http
    bind :::8085 v4v6
    server sso_server localhost:9443 check ssl verify none #REPLACE

Thank you!

Edit: I added it to the advanced pass thru in the frontend. The redirect to authentik seems to work, but I am unable to access any services after the redirect. I get a 503 service unavailable error. Other services for which I didn't add authentik as forward proxy also cannot be accessed.

anuneo avatar Nov 23 '24 09:11 anuneo

Hello @anuneo,

Unfortunately, i will not really be able to advise regarding PFSense/OPNSense. 503 is at least something, your logs should indicate that a connection was made to the proxy.

However it seems that the request is not being processed correctly. Could you perhaps share your ACL for the backends and the backends themselves?

CloudGoesPwn avatar Nov 28 '24 19:11 CloudGoesPwn

Has anyone managed to get this to work to pfSense running HAProxy?

felmey avatar Jan 02 '25 05:01 felmey

Don't suppose anyone's figured out this yet for haproxy:

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

It's the only thing stopping me going live with this for the other half, she has a brain injury and this is all that's stopping me giving her access to make life easier for her

JJ-Ge avatar Feb 12 '25 18:02 JJ-Ge

Realizing domain level forward auth would be truly amazing. I tried setting it up for numerous hours now but I believe it's not possible using haproxy. After authenticating one gets redirected to "https://auth.domain.com/outpost.goauthentik.io/callback?X-authentik-auth-callback=true" no matter what because that's the only allowed redirect uri for the Forward Auth provider. I haven't found any way to modify the allowed redirect uri so authentik can redirect to the originally requested domain. If anyone knows a decent hack, I'd very appreciate it if you could share

Toastyyy3 avatar Mar 02 '25 00:03 Toastyyy3

Hey @felmey, @JJ-Ge and @Toastyyy3 , Sorry for the late reply. Personal life got really busy and I didn't get a chance to reply. I was able to set this up successfully and it has been working flawlessly since some months. Here are the relevant parts of the generated configuration. Hope this helps!

frontend Internal_server_access_forward_proxy
	bind			yourip:443 name yourip:443   ssl crt-list /var/etc/haproxy/Internal_server_access_forward_proxy.crt_list  
	mode			http
	log			global
	option			socket-stats
	option			http-keep-alive
	timeout client		30000
	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 end -i yoursite
	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_ipvANY /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_request_ipvANY if protected-frontends is_authentikoutpost
	
	# Define ACL and backend
	acl service1 hdr(host) -i service1.yoursite
	use_backend service1_ipvANY if service1.yoursite
	
	# Set Strict-Transport-Security header for all HTTPS responses
	http-response set-header Strict-Transport-Security "max-age=63072000"
	
	acl			protected-frontends	var(txn.txnhost) -m end -i youtsite
	acl			is_authentikoutpost	var(txn.txnpath) -m reg -i ^/outpost.goauthentik.io/
	acl			generalUsers	src -f /var/etc/haproxy/ipalias_General_users.lst
	acl			aclcrt_Internal_server_access_forward_proxy	var(txn.txnhost) -m reg -i ^([^\.]*)\.yoursite\.de(:([0-9]){1,5})?$
	http-request set-var(txn.txnhost) hdr(host)
	http-request set-var(txn.txnpath) path
	use_backend be_auth_request_ipvANY  if  generalUsers aclcrt_Internal_server_access_forward_proxy
	
frontend be_auth_request_proxy
	bind			0.0.0.0:8085 name 0.0.0.0:8085   
	bind			:::8085 name :::8085   
	mode			http
	log			global
	option			http-keep-alive
	timeout client		30000
	default_backend authentik_ipvANY

anuneo avatar Mar 02 '25 03:03 anuneo

@anuneo Is this Forward Auth on domain level or single application?

EDIT: Okay never mind, I totally had a misconception... You don't really need domain level forward auth with this setup as you can just add applications... Whoops...

Toastyyy3 avatar Mar 03 '25 12:03 Toastyyy3

Hey @felmey, @JJ-Ge and @Toastyyy3 , Sorry for the late reply. Personal life got really busy and I didn't get a chance to reply. I was able to set this up successfully and it has been working flawlessly since some months. Here are the relevant parts of the generated configuration. Hope this helps!

frontend Internal_server_access_forward_proxy
	bind			yourip:443 name yourip:443   ssl crt-list /var/etc/haproxy/Internal_server_access_forward_proxy.crt_list  
	mode			http
	log			global
	option			socket-stats
	option			http-keep-alive
	timeout client		30000
	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 end -i yoursite
	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_ipvANY /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_request_ipvANY if protected-frontends is_authentikoutpost
	
	# Define ACL and backend
	acl service1 hdr(host) -i service1.yoursite
	use_backend service1_ipvANY if service1.yoursite
	
	# Set Strict-Transport-Security header for all HTTPS responses
	http-response set-header Strict-Transport-Security "max-age=63072000"
	
	acl			protected-frontends	var(txn.txnhost) -m end -i youtsite
	acl			is_authentikoutpost	var(txn.txnpath) -m reg -i ^/outpost.goauthentik.io/
	acl			generalUsers	src -f /var/etc/haproxy/ipalias_General_users.lst
	acl			aclcrt_Internal_server_access_forward_proxy	var(txn.txnhost) -m reg -i ^([^\.]*)\.yoursite\.de(:([0-9]){1,5})?$
	http-request set-var(txn.txnhost) hdr(host)
	http-request set-var(txn.txnpath) path
	use_backend be_auth_request_ipvANY  if  generalUsers aclcrt_Internal_server_access_forward_proxy
	
frontend be_auth_request_proxy
	bind			0.0.0.0:8085 name 0.0.0.0:8085   
	bind			:::8085 name :::8085   
	mode			http
	log			global
	option			http-keep-alive
	timeout client		30000
	default_backend authentik_ipvANY

I've had it working for a long while, I just haven't been able to get the home assistant part working and I don't see it in your config either so either I'm missing something or you don't use the home assistant hass-auth-header and X-ak-hass-user

JJ-Ge avatar Apr 04 '25 18:04 JJ-Ge

@anuneo Can you please help me get mine working im using haproxy in pfsense and last night i got redirect working but i deleted everything and now nothing is working for me and i cannot replicate you config on my version and im not sure why.

nosar77 avatar Apr 17 '25 21:04 nosar77

@anuneo Can you please help me get mine working im using haproxy in pfsense and last night i got redirect working but i deleted everything and now nothing is working for me and i cannot replicate you config on my version and im not sure why.

Maybe you can post your config, then we can try

Toastyyy3 avatar Apr 18 '25 19:04 Toastyyy3

@anuneo Can you please help me get mine working im using haproxy in pfsense and last night i got redirect working but i deleted everything and now nothing is working for me and i cannot replicate you config on my version and im not sure why.

Maybe you can post your config, then we can try

I was going to update it, I got it working. Took much longer than it should but now I understand a lot about haproxy lol. My problem was mostly getting the configuration set up like his. but once i figured out i just needed to use custom acls and rules because HAproxy in pfsense doesn't have the options needed to replicate the config everything worked

Thanks!

I might create some documentation or a YouTube on how to get this set up because it was confusing. This seems to be the only post across the Internet getting this working.

nosar77 avatar Apr 18 '25 19:04 nosar77