Pfsense HAproxy + authelia

For quite some time I am looking for a way to make my personal servers open to the internet. But I do want them to be secure and prevent hacks. After a quick google search I found Authelia. Unfortunately the setup for Authelia isn’t as straight forward for pfsense as for other proxy managers. With the help of Github and a few Youtubers I made a manual for you to follow in a way I wanted to have.

The manual:

Prerequisites:

  • Up and running PFsense
  • Installed HAproxy-devel (for lua load

Get started:

  1.  Download the following files and save as:
  2. Open filezilla or equivalent and upload the files above to: /usr/local/share/lua/5.3 
  3. Go to services > haproxy > files
  4. Create three entries with the following content:
  5. in HAproxy got to: settings > Global Advanced pass thru paste the following:
lua-prepend-path /usr/local/share/lua/5.3/haproxy-lua-http.lua

Create Backend:

Authelia backend:

Service backend:

In my case this will be photoprism, you will need the following entries to be made (note that this needs to be done for every protected backend).

Access Control list:

NameExpressionValue
remote_user_existCustom aclvar(req.auth_response_header.remote_user) -m found
remote_groups_existCustom aclvar(req.auth_response_header.remote_groups) -m found
remote_name_existCustom aclvar(req.auth_response_header.remote_name) -m found
remote_email_existCustom aclvar(req.auth_response_header.remote_email) -m found

Actions:

ActionParameterCondition acl name
http-request header set
See Belowremote_user_exist

NameRemote-User

fmt%[var(req.auth_response_header.remote_user)]
http-request header set
remote_groups_exist

nameRemote-Groups

fmt%[var(req.auth_response_header.remote_groups)]
http-request header set
remote_name_exist

nameRemote-Name

fmt%[var(req.auth_response_header.remote_name)]
http-request header set
remote_email_exist

nameRemote-Email

fmt%[var(req.auth_response_header.remote_email)]

Frontend:

Within the frontend scroll down to: Default backend, access control lists and actions.

Access control list:

NameExpressionValue
autheliaHost matches:auth.mydomain.me
protected-frontendsHost matches:myservice.mydomain.me

Note that “protected-frontends” is important

ActionParametersCondition acl names
Use BackendSee belowauthelia
backend: be_authelia
CustomSee below{ ssl_fc }
customaction: http-request set-var(req.scheme) str(https)
CustomSee below!{ ssl_fc }
customaction: http-request set-var(req.scheme) str(http)
CustomSee below{ query -m found }
customaction: http-request set-var(req.questionmark) str(?)
http-request header setSee belowprotected-frontends
name: X-Real-IP , fmt: %[src]
http-request header setSee belowprotected-frontends
name: X-Forwarded-Method , fmt: %[var(req.method)]
http-request header setSee belowprotected-frontends
name: X-Forwarded-Proto , fmt: %[var(req.scheme)]
http-request header setSee belowprotected-frontends
name: X-Forwarded-Host , fmt: %[req.hdr(Host)]
http-request header setSee belowprotected-frontends
name: X-Forwarded-Uri , fmt: %[path]%[var(req.questionmark)]%[query]
CustomSee belowprotected-frontends
customaction: http-request lua.auth-request be_authelia_ipvANY /api/verify
http-request redirectSee belowprotected-frontends !{ var(txn.auth_response_successful) -m bool }
rule: location https://auth.mydomain.me/?rd=%[var(req.scheme)]://%[base]%[var(req.questionmark)]%[query]
Use BackendSee belowprotected-frontends
backend: photoprism

Picture in addition to text.

Please note:

customaction: http-request lua.auth-request be_authelia_ipvANY /api/verify

be_authelia_ipvANY is important! be_authelia is the backend we’ve created earlier; ipvANY needs to be added (pfsense does so in haproxy.cfg)

Another route:

In some cases, like mine it might not work as described above however; there is another way. To achieve this you need to tweak a few things as following:

For the frontend ACL:

Name: protected-frontends
Expression: Custom ACL:
Value: hdr(host) -m reg -i ^(?i)(prism|nvr|storj1|spotweb)\.mydomain\.me

Name: host-prism
Expression: Host matches:
Value: prism.mydomain.me
Frontend – ACL

For the frontend action:

Action: Use Backend
Parameters: See below 
Condition ACL names: host-prism
Actions: <your backend>
frontend – action

Above is just an example on how to approach. You need to change accordingly.

Authelia:

For this guide it’s important that you already have an up and running docker with docker compose in order to start. If you don’t have docker up and running, I’ve got you covered. Just folllow this guide

Docker compose – Authelia

version: '3.3'
    
services:
  authelia:
    image: authelia/authelia
    container_name: authelia
    volumes:
      - /myvolume:/config #change this to a shared folder on your system. DO NOT use a "/myvolume"
    ports:
      - 9091:9091
    environment:
      - TZ=Europe/Amsterdam

Start the created container, it will stop, this is normal.

Make configuration:

  1. Navigate by ssh to /myvolume/ and edit configuration.yml.
# yamllint disable rule:comments-indentation
---
###############################################################################
#                           Authelia Configuration                            #
###############################################################################

theme: auto #matches yoursystem theme
jwt_secret: 1234567890abcdefghifjkl #any text or number you want to add here to create jwt Token

default_redirection_url: https://google.com/ #where to redirect for a non-existent URL

server:
  host: 0.0.0.0
  port: 9091
  path: ""
  read_buffer_size: 4096
  write_buffer_size: 4096
  enable_pprof: false
  enable_expvars: false
  disable_healthcheck: false
  tls:
    key: ""
    certificate: ""

log:
  level: debug

totp:
  issuer: yourdomain.com #your authelia top-level domain
  period: 30
  skew: 1

authentication_backend:
  disable_reset_password: false
  refresh_interval: 5m
  file:
    path: /config/users_database.yml #this is where your authorized users are stored
    password:
      algorithm: argon2id
      iterations: 1
      key_length: 32
      salt_length: 16
      memory: 1024
      parallelism: 8

access_control:
  default_policy: deny
  rules:
    ## bypass rule
    - domain: 
        - "auth.mydomain.me" #This should be your authentication URL
      policy: bypass
    - domain: "mydomain.me" #example domain to protect
      policy: one_factor
    - domain: "sub1.mydomain.me" #example subdomain to protect
      policy: one_factor #could also be two_factor


session:
  name: authelia_session
  secret: unsecure_session_secret #any text or number you want to add here to create jwt Token
  expiration: 3600  # 1 hour
  inactivity: 300  # 5 minutes
  domain: mydomain.me  # Should match whatever your root protected domain is

regulation:
  max_retries: 3
  find_time: 10m
  ban_time: 12h

storage:
  local:
    path: /config/db.sqlite3 
  encryption_key: GENERATE_STRING_MORETHAN20_CHARS
  
notifier:
  filesystem:
    filename: /config/notification.txt

2. Next, create and edit a file called: users_database.yml.

users:
  user1: #username for user 1. change to whatever you'd like
    displayname: "User Name 1" #whatever you want the display name to be
    password: "$argon2i$v=19$m=1024,t=1,p=8$eTQ3MXdqOGFiaDZoMUtMVw$OeHWQSg9zGKslOepe5t4D1T9BZJjHA1Z+doxZrZYDgI" #generated at https://argon2.online/
    email: youremail@gmail.com #whatever your email address is
    groups: #enter the groups you want the user to be part of below
      - admins
      - dev
  user2: #username for user 2. change to whatever you'd like. Or delete this section if you only have 1 user
    displayname: "User Name 2" #whatever you want the display name to be
    password: "$argon2i$v=19$m=1024,t=1,p=8$eTQ3MXdqOGFiaDZoMUtMVw$OeHWQSg9zGKslOepe5t4D1T9BZJjHA1Z+doxZrZYDgI" #generated at https://argon2.online/
    email: youremail2@gmail.com #whatever your email address is
    groups: #enter the groups you want the user to be part of below
      - dev

Thank you for reading, I hope this guide was sufficient. whenever something isn’t clear, please let me know. I will try to explain

Dennis

Related Post

Comments (19)

  1. Hello,
    I manage to get one protected back end to work.
    It’s set to two_factor but I don’t get the option activate it.

    Daniel
    August 3, 2022
    1. I gotten t o the part to enable the device but the URL I get is a HTTP and my browser/HAproxy change it to HTTPS and I get a white page.

      Daniel
      August 3, 2022
      1. I have the same issue. did you manage to find a solution?

        Murat
        April 16, 2023
      2. We have found a solution together via a private convo. Not sure what is was though.

        April 16, 2023
        1. if you ever remember what the solution was, it would be great if you could share it please!

          Murat
          April 16, 2023
          1. I’ve switched email servers and those emails are a goner. But maybe there is a cached archive on my laptop. I will reach out 👌

            April 16, 2023
  2. Hi,
    Thanks a lot for your hard work and for sharing it here. Just have one question. Will the setup survive when I have to upgrade pfSense to a newer version in the future?

    bthoven
    December 31, 2022
    1. Hello, thank you for taking the time to read my article.
      Yes this will survive upgrades 🙂

      January 1, 2023
  3. …note: please delete the above 2 posts and use this one instead….
    Thanks again for the very comprehensive guide. I managed to set it up and seems to work okay.
    One change I need to make it work on the pfSense HAProxy side is the “lua-prepend-path /usr/local/share/lua/5.3/haproxy-lua-http.lua”
    It caused error when I applied this change to HAProxy, complaining something like it can’t find the auth-request lua. I have to change it from luar-prepend-path” to “lua-load”, and it works now.
    “lua-load /usr/local/share/lua/5.3/auth-request.lua”

    bthoven
    January 2, 2023
    1. I’m glad everything works out for you now.
      Not sure why it isn’t working with the lua-prepend-path within custom options at global advanced pass thru. 🤔

      January 4, 2023
      1. Thank you for detailed information, I got it worked, 👍 , I have issue that I have around 20 apps running on server ,as per this configuration I need add 11 action commands for each subdomain, and you have given 2 option @’Another route’ also but it’s not working for me.

        March 16, 2023
        1. I’m glad my guide helps many people. It’s a very tedious job and requires a lot of administration. Unfortunately you need to choose which of the two methods to use.

          When using the main one, you’ll have to add a separate entry for each “protected frontend”. The alternative route is described and use myself needa only one protected frontend action. Can you show what you got amd isn’t working?

          March 16, 2023
  4. Thank you for replay,

    As per Method 2 ,
    I have configured Pfsense haproxy for authelia and configuration saved but when i tried access protected frontend it’s not works for authentication, ex: like it’s not redirecting authelia page, it directly goes application page, this means for these application URL authentication not works, thank you once again

    March 17, 2023
    1. Did you check the order of the rules? These are important. You need to add the Configuration to your authelia config as well. It’s hard to say where your issue lays at this time

      March 17, 2023
      1. if method 1 works means method 2 also needs to work but in my case method 2 not works
        Even i configured backend with above shown commands also and for frontend show below
        ACL
        protected-frontends Custom acl: no hdr(host) -m reg -i ^(?i)(bio|Rdp|trilium|apex)\.xxxxxxxxx\.com
        host-trilium Host matches: no no notes.xxxxxxxx.com
        host-rdp Host matches: no no rdp.xxxxxxxx.com
        host-apex Host matches: no no apex.xxxxxxxx.com
        host-bio Host matches: no no bio.xxxxxxxx.com
        Action
        Use Backend See below host-trilium
        backend: trilium_notes
        Use Backend See below host-rdp
        backend: rdpserver
        Use Backend See below host-apex
        backend: Apexserver
        Use Backend See below host-bio
        backend: bioserver
        Authelia
        access_control:
        default_policy: deny
        rules:
        ## bypass rule
        – domain:
        – “auth.xxxxxxxx.com” #This should be your authentication URL
        policy: bypass
        – domain: “gua.xxxxxxxx.com” #example domain to protect
        “rdp.xxxxxxxx.com”
        “Apex.xxxxxxxx.com”
        “notes.xxxxxxxx.com”
        policy: two_factor
        – domain: “bio.xxxxxxxx.com” #example subdomain to protect
        policy: one_factor #could also be two_factor

        March 17, 2023
        1. Any Solution regarding 2nd method

          March 21, 2023
          1. Sorry, I don’t have had the time to look at it again

            March 21, 2023
        2. Are the X headers correctly set-up ?
          Those are really important and easy to get wrong

          March 31, 2023
  5. if method 1 works then method 2 need to work but i am unable to compile 2nd method,
    ACL
    protected-frontends Custom acl: no hdr(host) -m reg -i ^(?i)(bio|Rdp|trilium|apex)\.xxxxxxxxx\.com
    host-trilium Host matches: no no notes.xxxxxxxx.com
    host-rdp Host matches: no no rdp.xxxxxxxx.com
    host-apex Host matches: no no apex.xxxxxxxx.com
    host-bio Host matches: no no bio.xxxxxxxx.com
    Action
    Use Backend See below host-trilium
    backend: trilium_notes
    Use Backend See below host-rdp
    backend: rdpserver
    Use Backend See below host-apex
    backend: Apexserver
    Use Backend See below host-bio
    backend: bioserver
    Authelia configuration
    access_control:
    default_policy: deny
    rules:
    ## bypass rule
    – domain:
    – “auth.xxxxxxxx.com” #This should be your authentication URL
    policy: bypass
    – domain: “gua.xxxxxxxx.com” #example domain to protect
    “rdp.xxxxxxxx.com”
    “Apex.xxxxxxxx.com”
    “notes.xxxxxxxx.com”
    policy: two_factor
    – domain: “bio.xxxxxxxx.com” #example subdomain to protect
    policy: one_factor #could also be two_factor

    March 17, 2023

Leave A Comment

Name

Website

Comment