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:
- Download the following files and save as:
- Open filezilla or equivalent and upload the files above to: /usr/local/share/lua/5.3
- Go to services > haproxy > files
- Create three entries with the following content:
- 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:
Name | Expression | Value |
remote_user_exist | Custom acl | var(req.auth_response_header.remote_user) -m found |
remote_groups_exist | Custom acl | var(req.auth_response_header.remote_groups) -m found |
remote_name_exist | Custom acl | var(req.auth_response_header.remote_name) -m found |
remote_email_exist | Custom acl | var(req.auth_response_header.remote_email) -m found |
Actions:
Action | Parameter | Condition acl name | |
http-request header set | See Below | remote_user_exist | |
Name | Remote-User | ||
fmt | %[var(req.auth_response_header.remote_user)] | ||
http-request header set | remote_groups_exist | ||
name | Remote-Groups | ||
fmt | %[var(req.auth_response_header.remote_groups)] | ||
http-request header set | remote_name_exist | ||
name | Remote-Name | ||
fmt | %[var(req.auth_response_header.remote_name)] | ||
http-request header set | remote_email_exist | ||
name | Remote-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:
Name | Expression | Value |
authelia | Host matches: | auth.mydomain.me |
protected-frontends | Host matches: | myservice.mydomain.me |
Note that “protected-frontends” is important
Action | Parameters | Condition acl names |
---|---|---|
Use Backend | See below | authelia |
backend: be_authelia | ||
Custom | See below | { ssl_fc } |
customaction: http-request set-var(req.scheme) str(https) | ||
Custom | See below | !{ ssl_fc } |
customaction: http-request set-var(req.scheme) str(http) | ||
Custom | See below | { query -m found } |
customaction: http-request set-var(req.questionmark) str(?) | ||
http-request header set | See below | protected-frontends |
name: X-Real-IP , fmt: %[src] | ||
http-request header set | See below | protected-frontends |
name: X-Forwarded-Method , fmt: %[var(req.method)] | ||
http-request header set | See below | protected-frontends |
name: X-Forwarded-Proto , fmt: %[var(req.scheme)] | ||
http-request header set | See below | protected-frontends |
name: X-Forwarded-Host , fmt: %[req.hdr(Host)] | ||
http-request header set | See below | protected-frontends |
name: X-Forwarded-Uri , fmt: %[path]%[var(req.questionmark)]%[query] | ||
Custom | See below | protected-frontends |
customaction: http-request lua.auth-request be_authelia_ipvANY /api/verify | ||
http-request redirect | See below | protected-frontends !{ var(txn.auth_response_successful) -m bool } |
rule: location https://auth.mydomain.me/?rd=%[var(req.scheme)]://%[base]%[var(req.questionmark)]%[query] | ||
Use Backend | See below | protected-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
For the frontend action:
Action: Use Backend
Parameters: See below
Condition ACL names: host-prism
Actions: <your backend>
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:
- 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
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.
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.
I have the same issue. did you manage to find a solution?
We have found a solution together via a private convo. Not sure what is was though.
if you ever remember what the solution was, it would be great if you could share it please!
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 👌
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?
Hello, thank you for taking the time to read my article.
Yes this will survive upgrades 🙂
…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”
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. 🤔
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.
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?
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
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
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
Any Solution regarding 2nd method
Sorry, I don’t have had the time to look at it again
Are the X headers correctly set-up ?
Those are really important and easy to get wrong
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
Hello,
Thanks for a great guide, it’s running fine for me.
But lately I’m trying to use path like this ones, but I can’t get the hang of how to set it up.
http://www.mydomain.foo/3dprinter
http://www.mydomain.foo/HomeAssistans
it’s easier to have 1 SSL cert to manage and some D(YN)DNS services just have few free.
If Google didn’t go about selling it’s domain services I wouldn’t have cared but now they have and I need to find another that has the same functions with DNS/SSL cert API updates.
Thanks for your comment. I don’t really understand what you mean. Did you resolve this or is it still a problem?
First off, THANK YOU for putting this together. It took me two lengthy tries but I finally got one site properly redirecting through Authelia and haproxy on my pfSense router. I do have a few questions.
1 – I’m not clear on how you route multiple sites through Authelia using method one. It looks like all the steps need to be repeated for each protected backend. What is confusing me is the “protected-frontends” which makes it seem that it is a gateway for multiple Authelia protected backends. Is this just a matter of adding the 4 ACL’s and 4 Actions to each protected backend?
2 – The second method seems more streamliined if I understand it correctly. It looks like there is one ACL to modify with all the backends protected by Authelia. The backend configuration seems simpler. Am I interpreting this correct?
I have not tried implementing method two since I have method one working for one site and it was a lot of effort to put everything in correctly and the correct order. I am very happy to see that this does work. Please accept my deep appreciation for putting this guide together. I have not come across anything else this comprehensive to help with pfSense/ha-proxy/Authelia configuration.
Hi, I’m sorry for my late reaction.. very late reaction. I’m trying to keep up with everything while I’m super busy with other stuff too. I will make a new tutorial on this subject since quite a lot has changed and I redid my setup too. This reply doesn’t really help you but if you want I can provide you with more information
Thank you so much for putting this together. It took me 3 tries to finally get method two working. Now I can relatively easily manage multiple Authelia protected site acces with ha-proxy. This guide has been an amazing help.