A 2 AM Wake-up Call
At exactly 2 AM, my phone buzzed incessantly on the desk. Alerts from Grafana were flooding in: tens of thousands of suspicious requests were hitting the admin page of an internal dashboard. Checking the logs, I saw an overseas IP tirelessly brute-forcing the admin account at a rate of over 50,000 requests per hour.
Despite having a complex password, watching an attacker try every key to my front door was anything but comfortable. After auditing security for over 10 server systems, I realized a fatal flaw: fragmentation. Every application like Nextcloud, Portainer, or Grafana used its own set of credentials. Some supported 2FA, some didn’t. If just one weak link is bypassed, the entire infrastructure is exposed to hackers.
Where is the Problem?
The issue isn’t just about how many characters are in your password. When you self-host multiple services, managing access quickly becomes a nightmare:
- Disjointed management: Each app has its own login style, making it hard to control who is accessing what.
- Offboarding risks: If a team member leaves, you have to log into every app to delete their account. Forgetting one spot leaves a deadly vulnerability.
- Legacy app vulnerabilities: Many older open-source tools don’t support two-factor authentication, making them prime targets for credential stuffing attacks.
Options Often Ruled Out Immediately
Before deciding on Authelia, I considered several options, but each had its own set of inconveniences:
- Using a VPN (WireGuard/OpenVPN): Very secure but extremely inconvenient. Every time you need to quickly check a dashboard on your phone, you have to toggle the VPN and wait for a connection.
- Nginx Basic Auth: Too primitive. It lacks a password reset interface, has no 2FA, and is incredibly difficult to manage as the team grows.
- Cloudflare Access: Quite good, but you’re dependent on their cloud. For sensitive data that needs to stay entirely on-premise, this isn’t the preferred choice.
Authelia – A Powerful SSO and 2FA Gatekeeper
Authelia acts as a centralized authentication gateway in front of your applications. It provides Single Sign-On (SSO) and multi-factor authentication (TOTP, Duo, U2F). Simply put: instead of letting users hit the app directly, Nginx redirects them to Authelia to verify fingerprints, passwords, and OTP codes. Only if valid does the door open.
Step 1: Environment Preparation
I assume you already have Docker installed. The directory structure I usually deploy is as follows:
/opt/authelia/
├── docker-compose.yml
└── config/
├── configuration.yml
└── users.yml
Step 2: Launching with Docker Compose
This file will run Authelia along with a Redis database to store sessions. Using Redis ensures a smoother experience, as users won’t have to log in repeatedly when you restart the container.
version: '3.8'
services:
authelia:
container_name: authelia
image: authelia/authelia:latest
volumes:
- ./config:/config
networks:
- proxy
environment:
- TZ=Asia/Ho_Chi_Minh
restart: unless-stopped
redis:
container_name: authelia-redis
image: redis:alpine
networks:
- proxy
restart: unless-stopped
networks:
proxy:
external: true
Step 3: Configuring the System’s Soul – configuration.yml
This is where all the operational rules are set. You need to generate random secret strings using the command openssl rand -hex 64.
server:
host: 0.0.0.0
port: 9091
authentication_backend:
file:
path: /config/users.yml
access_control:
default_policy: deny
rules:
- domain: "*.yourdomain.com"
policy: two_factor
session:
name: authelia_session
domain: yourdomain.com
expiration: 3600
inactivity: 900
redis:
host: authelia-redis
port: 6379
notifier:
filesystem:
filename: /config/notification.txt
Step 4: Setting Up Users
In the users.yml file, passwords must be encrypted. You can use an online tool or run the command docker run --rm authelia/authelia:latest authelia hash-password "your_password" to get the Argon2id hash string.
users:
admin:
displayname: "Administrator"
password: "$argon2id$v=19$m=65536,t=3,p=4$your_hashed_password"
email: [email protected]
groups:
- admins
Step 5: Connecting the Reverse Proxy (Nginx)
If you are using Nginx Proxy Manager, paste this code into the Advanced Custom Configuration section of the application you want to protect:
auth_request /authelia;
auth_request_set $target_url $scheme://$http_host$request_uri;
auth_request_set $user $upstream_http_remote_user;
proxy_set_header Remote-User $user;
# Redirect to login page if not authenticated
error_page 401 =302 https://auth.yourdomain.com/?rd=$target_url;
Real-world Experience: Don’t Get Locked Out
After several hard-earned lessons during real-world deployments, I’ve identified 3 critical points:
- Time Synchronization: OTP codes (TOTP) are time-based. If the server and phone clocks are off by more than 30 seconds, you’ll be denied access even with the correct code. Install
chronyfor continuous time sync. - HTTPS is Mandatory: Authelia and modern browsers won’t allow 2FA over insecure HTTP connections.
- Cookie Domain: Ensure the session domain is set to
yourdomain.com(root domain). This way, when you log in atauth.yourdomain.com, other subdomains likegrafana.yourdomain.comwill recognize the session.
Since implementing Authelia, I’ve been sleeping much better. An attacker might guess a password, but they can’t get the OTP code from my pocket. User management is now consolidated into a single file—secure and professional.
