Deployment guide

Traefik with Authelia SSO

Overview

Traefik with Authelia SSO is an edge appliance that terminates inbound HTTPS traffic, routes it to your backend services, and requires every visitor to sign in first. Traefik handles routing, load balancing and TLS. Authelia is the authentication and authorization server behind it. It provides a login portal, brute-force protection, optional two-factor authentication and a single session shared across every service behind the proxy.

This image wires the two together for you. Traefik holds each request at the edge and asks Authelia whether the caller has a valid session before the backend is reached. Callers without a session are redirected to the login portal. Callers with a session are proxied through, and their identity is passed upstream in request headers. That is single sign-on: one login, one session, every protected service.

What you get:

  • Traefik 3.7.10 (a single static Go binary) at /usr/local/bin/traefik
  • Authelia 4.39.20 from the official package, on loopback 127.0.0.1:9091 and never exposed directly
  • The Authelia forwardAuth middleware, already applied to the demo resource and to the Traefik dashboard
  • web (:80, permanently redirected to HTTPS), websecure (:443) and a loopback ping (127.0.0.1:8082) entrypoint
  • Local SQLite storage and in-memory sessions, so the appliance runs standalone with no database or Redis to deploy
  • Brute-force protection on the portal (3 attempts, 2 minute window, 5 minute ban)
  • Two dedicated Azure data disks, at /var/lib/traefik and /var/lib/authelia, re-provisioned with every VM
  • File-provider dynamic configuration in /etc/traefik/dynamic/ for your own routers and services
  • A pre-configured Let's Encrypt certificate resolver (HTTP challenge)
  • Unique per-VM secrets, admin password and TLS certificate generated on first boot, and a start-up guard that refuses to run with a template or published credential
  • 24/7 SecureAnyCloud support

How the single sign-on works

Every request carrying the authelia middleware flows through this loop:

browser --> Traefik :443 (Host: app.yourdomain.com) | | forwardAuth, BEFORE the backend is reached v Authelia 127.0.0.1:9091 /api/authz/forward-auth | +-- no valid session --> 302 to https://auth.yourdomain.com/?rd=<original URL> | (the login portal; the visitor signs in) | +-- valid session --> 200 + Remote-User / Remote-Groups / Remote-Name / Remote-Email --> Traefik proxies to your backend

Remove the middleware from a router and that route becomes public. Add it to a new router and that service is protected, with no changes to the service itself. That is the whole integration. It lets you publish a service that has no authentication of its own through this appliance.

Single sign-on requires a domain name

Plan this before you deploy. Authelia is domain-based. The session cookie it issues must be scoped to a domain shared by the login portal and the services behind it. Authelia rejects a bare IP address, and a browser cannot store a cookie scoped to an IP literal either. No configuration avoids this, because it is how cookies work.

The image ships with the reserved documentation domain example.com as a stand-in that you replace with your own. It uses two host names:

Host Serves

auth.yourdomain.com the Authelia login portal

app.yourdomain.com the protected demo resource and the Traefik dashboard

Until you point DNS at the VM, its public IP address shows an unauthenticated setup page that repeats these instructions. This is expected, and it is the only page reachable without signing in.

Prerequisites

Before you deploy, you need:

  • An active Azure subscription, an SSH key pair, and a VNet and subnet in the target region
  • A VM size of Standard_B2ms (2 vCPU / 8 GiB RAM) as a starting point. Authelia's argon2id password hashing is deliberately memory-hard, so do not go below 4 GiB
  • NSG inbound rules that allow 22/tcp from your management network, and 80/tcp and 443/tcp for proxied traffic. Authelia on 9091 and the demo backend on 8090 are bound to loopback, so never open them
  • A domain you control, so that you can create the two DNS records above

Step 1 - Deploy from the Azure Marketplace

In the Azure portal, choose Create a resource. Search for Traefik with Authelia SSO on Ubuntu 24.04 LTS by SecureAnyCloud and select Create. Pick your subscription, resource group and region, and choose the Standard_B2ms size. Select SSH public key authentication with the username azureuser, and allow inbound 22, 80 and 443. Then select Review + create.

Step 2 - Deploy from the Azure CLI

az vm image terms accept --urn sac1647283583153:traefik-authelia:default:latest
az vm create \
  --resource-group my-resource-group \
  --name traefik-authelia-01 \
  --image sac1647283583153:traefik-authelia:default:latest \
  --size Standard_B2ms \
  --admin-username azureuser \
  --generate-ssh-keys \
  --public-ip-sku Standard

Then open the ports that the proxy needs:

az vm open-port --resource-group my-resource-group --name traefik-authelia-01 --port 80 --priority 1010
az vm open-port --resource-group my-resource-group --name traefik-authelia-01 --port 443 --priority 1020

Step 3 - Point your DNS at the VM

Create two A records for the domain you control, both pointing at the VM's public IP address:

auth.yourdomain.com. A YOUR_VM_PUBLIC_IP app.yourdomain.com. A YOUR_VM_PUBLIC_IP

Step 4 - Connect to your VM

ssh azureuser@YOUR_VM_PUBLIC_IP

Step 5 - Confirm the services are running

First boot generates this VM's secrets and starts both daemons. Confirm that they are running:

systemctl is-active traefik authelia sac-landing

All three report active. Check the versions:

/usr/local/bin/traefik version | head -3 authelia --version

Check Traefik's health endpoint. It is unauthenticated, listens on loopback and answers immediately:

curl -sf -o /dev/null -w 'traefik ping: %{http_code}\n' http://127.0.0.1:8082/ping

Step 6 - Retrieve your per-VM credentials

This image ships with no known password. First boot generates the administrator account, the three Authelia secrets and the default TLS certificate on this VM. Print them:

sudo cat /root/traefik-authelia-credentials.txt

Record the AUTHELIA_ADMIN_PASSWORD value in a safe place. It is unique to this VM and cannot be recovered from the image.

Step 7 - Set your own domain

Two files carry the shipped example domain. In /etc/authelia/configuration.yml, set the session cookie domain and the portal URL:

session: cookies: - name: 'authelia_session' domain: 'yourdomain.com' authelia_url: 'https://auth.yourdomain.com/'

In /etc/traefik/dynamic/routers.yml, replace both host rules:

routers: authelia-portal: rule: "Host(`auth.yourdomain.com`)" traefik-dashboard: rule: "Host(`app.yourdomain.com`) && (PathPrefix(`/dashboard`) || PathPrefix(`/api`))" protected-app: rule: "Host(`app.yourdomain.com`)"

Then restart both services:

sudo systemctl restart authelia traefik

Authelia refuses to start on an invalid configuration instead of starting insecurely. If it does not come back, read the reason with sudo journalctl -u authelia -n 30 and sudo tail /var/lib/authelia/authelia.log.

Step 8 - Watch the forward-auth loop

This step shows the integration working. Request the protected resource without a session. Traefik sends you to the portal instead of the backend:

curl -sS --resolve app.example.com:443:127.0.0.1 -k -o /dev/null \
  -w 'unauthenticated: %{http_code} -> %{redirect_url}\n' https://app.example.com/

You get 302 and a redirect to the Authelia portal, carrying your original URL in ?rd=. Now try a deliberately wrong password. The portal refuses it:

curl -sS --resolve auth.example.com:443:127.0.0.1 -k \
  -X POST -H 'Content-Type: application/json' \
  -d '{"username":"admin","password":"not-the-password","keepMeLoggedIn":false}' \
  -w '\nHTTP %{http_code}\n' https://auth.example.com/api/firstfactor

That returns {"status":"KO"} and HTTP 401. Now sign in with the correct password and keep the session cookie:

curl -sS --resolve auth.example.com:443:127.0.0.1 -k -c /tmp/authelia-jar \
  -X POST -H 'Content-Type: application/json' \
  -d '{"username":"admin","password":"<AUTHELIA_ADMIN_PASSWORD>","keepMeLoggedIn":false}' \
  -w '\nHTTP %{http_code}\n' https://auth.example.com/api/firstfactor

That returns {"status":"OK"} and HTTP 200. Request the same protected URL again, this time with the session:

curl -sS --resolve app.example.com:443:127.0.0.1 -k -b /tmp/authelia-jar \
  -o /dev/null -w 'authenticated: %{http_code}\n' https://app.example.com/

The response is 200. The URL and the appliance are the same, and the only difference is a valid Authelia session.

The --resolve flags above make the request use the shipped host names from the VM itself, so this works before your DNS has propagated. Once your own records are live, drop the flags and use your real host names from a browser.

Step 9 - Sign in from a browser

Browse to https://app.yourdomain.com/. You are redirected to the Authelia portal:

Sign in as admin with the password from Step 6. Authelia establishes the session and sends you back to the URL you first requested:

The default certificate is self-signed and unique to this VM, so your browser shows a warning on the first visit. The warning stops when you switch to Let's Encrypt, as described below.

Step 10 - Open the Traefik dashboard

The Traefik dashboard is not published unauthenticated. api.insecure is false, and the dashboard router carries the same authelia middleware as every other route. Because you now hold a session, the dashboard opens at https://app.yourdomain.com/dashboard/:

Open HTTP Middlewares to confirm that the integration is live. authelia@file is listed with type forwardauth and a green status. This means Traefik loaded it and consults Authelia on every protected request:

You can confirm the same thing from the shell:

sudo grep -A3 'forwardAuth' /etc/traefik/dynamic/authelia.yml

Step 11 - Protect your own service

Add a router for your service and give it the authelia middleware. Nothing else is required, and your service does not need to know about Authelia:

# /etc/traefik/dynamic/my-app.yml http: routers: my-app: rule: "Host(`intranet.yourdomain.com`)" entryPoints: [websecure] service: "my-app" middlewares: [authelia] tls: {} services: my-app: loadBalancer: servers: - url: "http://127.0.0.1:3000"

Traefik watches /etc/traefik/dynamic/ and picks up the file with no restart. Add an A record for the new host name and set the session cookie domain to a parent of it. The same single login then covers both services.

Your application can read the signed-in user from the headers Authelia adds: Remote-User, Remote-Groups, Remote-Name and Remote-Email. Many applications can use these headers directly for trusted-header authentication, which removes their own login screen.

Step 12 - Access control rules

The shipped policy requires a successful login for everything. To vary that per host, path or group, add rules to /etc/authelia/configuration.yml. The first matching rule wins:

access_control: default_policy: 'deny' rules: - domain: 'public.yourdomain.com' policy: 'bypass' - domain: 'app.yourdomain.com' policy: 'one_factor' - domain: 'admin.yourdomain.com' policy: 'two_factor' subject: ['group:admins']

Once your rules are written, change default_policy to deny. A host you forget to list is then refused instead of being asked to log in.

Step 13 - Add two-factor authentication

Authelia supports time-based one-time passwords out of the box. Set a rule's policy to two_factor and restart Authelia. Then sign in and follow the registration prompt. The image uses the filesystem notifier, so the registration link is written to a file instead of being emailed:

sudo test -f /var/lib/authelia/notification.txt && echo "notifier file present" || echo "no notifications yet"

For production, switch notifier to SMTP so that password resets and 2FA registrations reach users by email.

Managing users

Users live in /etc/authelia/users_database.yml. Generate a hash and add an entry. Never write a plaintext password, because the start-up guard refuses to start the appliance if you do:

sudo authelia crypto hash generate argon2 --password 'a-strong-password' --no-confirm

Add the resulting digest as the user's password value:

users: alice: disabled: false displayname: 'Alice Smith' password: 'PASTE_THE_DIGEST_HERE' email: 'alice@yourdomain.com' groups: - admins

Authelia reloads the file automatically, so no restart is needed to add a user.

The start-up security guard

Both services run traefik-authelia-guard.sh before they start, as a deliberate fail-closed gate. It refuses to start the appliance in any of these cases:

  • First boot has not completed
  • Any of the three Authelia secrets is still a build-time template value or one of Authelia's published example values
  • The users database is the build-time one or contains Authelia's published demo account
  • A password is not an argon2id hash
  • The TLS key is missing or world-readable
  • api.insecure has been turned on
  • The forward-auth middleware has been removed

You can run the guard yourself at any time:

sudo /usr/local/sbin/traefik-authelia-guard.sh

If the guard refuses, it prints the exact reason, and the appliance stays down instead of serving traffic in an insecure state.

Enabling Let's Encrypt certificates

The certificate resolver is pre-configured and needs only your email address. Set the address in /etc/traefik/traefik.yml:

certificatesResolvers: letsencrypt: acme: email: 'you@yourdomain.com' storage: '/var/lib/traefik/acme.json' httpChallenge: entryPoint: web

Then add the resolver to each router's TLS block in /etc/traefik/dynamic/routers.yml:

tls: certResolver: letsencrypt

Both host names must resolve publicly to this VM, and port 80 must be reachable because the HTTP challenge is served there. Certificates are stored on the /var/lib/traefik data disk, so they survive a resize or a reboot.

Getting the real client IP behind a load balancer

If you place an Azure Load Balancer, Application Gateway or Front Door in front of this VM, add its ranges to forwardedHeaders.trustedIPs for both entrypoints in /etc/traefik/traefik.yml. Without this setting, Authelia's brute-force protection sees the load balancer's address instead of the real visitor, and its rate limiting is ineffective.

Maintenance

Both data disks are separate from the OS disk, so the SQLite database, certificates and logs survive a VM resize. Back up /var/lib/authelia/db.sqlite3, /etc/authelia/users_database.yml and /var/lib/traefik/acme.json. Logs rotate daily and are kept for 14 days in /var/lib/traefik/ and /var/lib/authelia/. Unattended security upgrades are enabled, as on the stock Ubuntu image.

To review what the appliance is doing, run:

sudo journalctl -u authelia -n 20 --no-pager

Support

SecureAnyCloud publishes and supports this image. For help, contact sales@secureanycloud.com. Traefik is distributed under the MIT license and Authelia under the Apache 2.0 license. SecureAnyCloud is not affiliated with either project.

Deploy on Azure

Launch Traefik with Authelia SSO on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.

View this image

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on Azure