
本文详细介绍了如何使用 Docker 最小化部署 Authelia,一个开源的身份验证和授权服务器。Authelia 提供双因素认证 (2FA) 和单点登录 (SSO) 功能,作为反向代理的伴侣,为应用程序提供细粒度访问控制。与功能强大的 Keycloak 相比,Authelia 更轻量、资源占用低,非常适合内网环境和用户不多的场景。文章涵盖了 Docker Compose 文件配置、Nginx 反向代理设置、Authelia 核心配置、用户数据库管理以及密钥生成等关键步骤,旨在帮助用户快速搭建一个高效的认证系统。
Authelia 是一个开源的身份验证和授权服务器,旨在通过一个网络门户为其应用程序提供双因素认证 (2FA) 和单点登录 (SSO) 功能。主要功能包括:
之前在 零信任时代的身份利器:Keycloak Docker化部署全攻略 里讲了怎么部署KeyCloak。不过它实在是太重了,虽然它功能强大,对于在内网又没多少用户的情况下属于杀鸡用牛刀。毕竟我既不是搞安全的,也不是Devops,只要能实现对多个APP实现一个统一的验证和单点登录就够了。因此,对CPU和内存友好的Authelia映入我的眼帘。

老样子,还是在公司内网服务器的环境部署,只有内网IP一枚,不同的APP用不同的端口来网文。首先,第一步还是要把容器跑起来,其次能通过Nginx反向代理访问WEBUI的界面。
未来会提供IDP的功能,使用OIDC来给自己的项目发Token。这样我的项目就不用自己写登录和鉴权的代码了。独立数据库和Redis暂时还不算上,毕竟就是一个小项目。
在Docker Compose文件下有一个Authelia目录,包括了配置文件,用户数据库,私钥和各种加密密钥,数据库文件,通知文件。
bash. ├── authelia │ ├── configuration.yml │ ├── data │ │ ├── db.sqlite3 │ │ └── notification.txt │ ├── keys │ │ └── oidc_private_key.pem │ ├── secrets │ │ ├── jwt_secret │ │ ├── oidc_hmac_secret │ │ ├── session_secret │ │ └── storage_encryption_key │ └── users_database.yml └── docker-compose.yml
yamlservices:
authelia:
image: authelia/authelia:latest
container_name: authelia
restart: unless-stopped
expose:
- "9091"
volumes:
- ./authelia/configuration.yml:/config/configuration.yml
- ./authelia/users_database.yml:/config/users_database.yml
- ./authelia/secrets:/config/secrets
- ./authelia/keys:/config/keys
- ./authelia/data:/data
environment:
- TZ=Asia/Shanghai
networks:
- nginx-reverse-proxy
networks:
nginx-reverse-proxy:
external: true
我没有按照官网的比较复杂的配置去做,先把请求放进来。
bashserver {
listen 80;
server_name SERVER_IP;
# Redirect all HTTP requests to HTTPS
return 301 https://$host:$request_uri;
}
server {
listen 443 ssl;
server_name SERVER_IP;
ssl_certificate /etc/nginx/certs/dlc.SERVER_IP.crt;
ssl_certificate_key /etc/nginx/certs/dlc.SERVER_IP.key;
# SSL/TLS Security Settings
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers HIGH:!aNULL:!MD5;
# Enable HSTS
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
location / {
proxy_pass http://authelia:9091/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
bashtheme: light
server:
address: tcp://0.0.0.0:9091
log:
level: info
session:
cookies:
- name: authelia_session
domain: SERVER_IP
authelia_url: https://SERVER_IP
default_redirection_url: https://SERVER_IP:8001
same_site: lax
secret: 669ebae753159618538b44f3466e3eecaeccf76a590d01005e67e8858672c1d0
expiration: 10h
inactivity: 4h
identity_validation:
reset_password:
jwt_secret: cd22924062b229d53b6ec25139080328a6368ce58588c0459c7e89761634bf6f
jwt_lifespan: 5 minutes
jwt_algorithm: HS256
authentication_backend:
file:
path: /config/users_database.yml
password:
algorithm: argon2id
access_control:
default_policy: deny
rules:
- domain_regex: '^SERVER_IP$'
policy: bypass
- domain_regex: '^SERVER_IP:8012$'
policy: one_factor
storage:
encryption_key: e006be0e2687d620f760c1016e532cd5344798fcbd30da706382d814c5017f09
local:
path: /data/db.sqlite3
notifier:
filesystem:
filename: /data/notification.txt
使用本地Yaml文件。argon2id的密码可以通过下面的命令生成,也就意味着可以先留空,可以在容器跑起来之后再运行这个命令。更新YAML文件后,需要重启容器重新加载用户数据库的内容。
docker run --rm authelia/authelia:latest authelia crypto hash generate argon2 --password 'PASSWORD'
yamlusers:
admin:
displayname: "Admin"
password: "$argon2id$v=19$m=65536,t=3,p=4$BzODJr+sliIj25pOvXlNHQ$3GVps1iOE/JezpAmnXLK3mW1L3UU+rXvZHtLIOHGiA0"
email: admin@example.com
groups:
- admins
生成私钥到文件。其他的密钥可以直接放到Authelia的配置文件里。
bash# 生成OIDC用的私钥
openssl genrsa -out oidc_private_key.pem 2048
# 生成其他的签名密钥
openssl rand -hex 32 > jwt_secret
openssl rand -hex 32 > session_secret
openssl rand -hex 32 > oidc_hmac_secret
openssl rand -hex 32 > storage_encryption_key
只有一个登陆界面,账号密码验证成功后,直接挑战到Session Cookie里默认的跳转界面。

本来搭建起来后,Authelia的Web界面是可以访问的。登录成功后,跳转也是成功的。但是如果再启用IDP的,并试图添加客户,配置没问题。虽然后台显示Nginx将请求发给你验证节点了,但是从浏览器的角度来看是直接可以访问了。上网也查了一些问题的可能性。
首先,Cookies是根据DNS名或IP来划分的,不支持我们这种以端口来区分服务的情况。所以没办法根据SERVER_IP
来设置不同的Cookie。bashsession: cookies: - name: authelia_session domain: SERVER_IP authelia_url: https://SERVER_IP default_redirection_url: https://SERVER_IP:8001 same_site: lax secret: 669ebae753159618538b44f3466e3eecaeccf76a590d01005e67e8858672c1d0 expiration: 10h inactivity: 4h
其次,在Access Control的部分,虽然只是写正则匹配IP和端口,但是每回都是被IP直接给截胡了,所以就bypass了。
bashaccess_control:
default_policy: deny
rules:
- domain: '^SERVER_IP:PORT$'
policy: one_factor
- domain_regex: '^SERVER_IP$'
policy: bypass
尝试更改Nginx配置文件,更改从Nginx传给Authelia的Header里的Host为假的域名。但是依然没有成功。
bash# Authelia配置
access_control:
default_policy: deny
rules:
- domain: 'excalidraw.local'
policy: one_factor
# Nginx配置
server {
listen 8001 ssl;
server_name excalidraw.local;
ssl_certificate /etc/nginx/certs/dlc.SERVER_IP.crt;
ssl_certificate_key /etc/nginx/certs/dlc.SERVER_IP.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers HIGH:!aNULL:!MD5;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# ---- Protected location ----
location / {
auth_request /authelia-auth;
auth_request_set $target_url $scheme://$http_host$request_uri;
auth_request_set $user $upstream_http_remote_user;
# Handle Authelia denied responses
error_page 401 =302 https://SERVER_IP/?rd=$target_url;
error_page 403 =302 https://SERVER_IP/?rd=$target_url;
proxy_pass http://excalidraw:80;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host excalidraw.local;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# Websocket endpoint (if Excalidraw uses it)
location /ws/ {
proxy_pass http://excalidraw_room:80;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}
# ---- Authelia verification endpoint ----
location /authelia-auth {
internal;
proxy_pass http://authelia:9091/api/verify;
proxy_set_header Content-Length "";
proxy_pass_request_body off;
proxy_set_header Host excalidraw.local;
proxy_set_header X-Original-URL $scheme://$host$request_uri;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
最后我只好作罢了,在没有域名区分服务的情况下,属实是比较难。我相信方法是一定有的,只是我没有扣到底。
本文作者:潘晓可
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!