编辑
2025-10-24
容器
00

目录

1. 介绍
2. Docker最小化部署
2.1 使用Authelic的初衷
2.2 部署条件
2.3 文件结构
2.4 Docker Compos文件
2.5 Nginx配置文件
2.6 Authelia配置文件
2.7 用户数据库
2.7 私钥和其他密钥
2.8 WEBUI界面
3. 后记

authelia000.jpeg

本文详细介绍了如何使用 Docker 最小化部署 Authelia,一个开源的身份验证和授权服务器。Authelia 提供双因素认证 (2FA) 和单点登录 (SSO) 功能,作为反向代理的伴侣,为应用程序提供细粒度访问控制。与功能强大的 Keycloak 相比,Authelia 更轻量、资源占用低,非常适合内网环境和用户不多的场景。文章涵盖了 Docker Compose 文件配置、Nginx 反向代理设置、Authelia 核心配置、用户数据库管理以及密钥生成等关键步骤,旨在帮助用户快速搭建一个高效的认证系统。

1. 介绍

Authelia 是一个开源的身份验证和授权服务器,旨在通过一个网络门户为其应用程序提供双因素认证 (2FA) 和单点登录 (SSO) 功能。主要功能包括:

  • 双因素认证 (2FA) 和单点登录 (SSO):通过 2FA 确认用户身份,并通过 SSO 简化登录流程,让用户只需登录一次即可访问多个应用程序。
  • 反向代理的伴侣:Authelia 作为反向代理(如 Nginx、Traefik、Caddy、HAProxy 等)的伴侣,可以允许、拒绝或重定向请求,从而保护您的服务。它直接与反向代理连接,而不直接连接到应用程序后端。
  • 细粒度访问控制:允许管理员根据子域名、用户、用户组、请求 URI、请求方法和网络等标准定义访问规则,实现精细的访问控制。
  • 密码重置:提供通过电子邮件确认进行身份验证的密码重置功能。
  • 防暴力破解:在多次无效认证尝试后限制访问,防止暴力破解攻击。
  • 轻量高效:采用 Go 和 React 编写,具有极低的资源占用,容器大小通常小于 20MB,内存使用量低于 30MB,并且授权策略和后端任务在毫秒级完成。

2. Docker最小化部署

2.1 使用Authelic的初衷

之前在 零信任时代的身份利器:Keycloak Docker化部署全攻略 里讲了怎么部署KeyCloak。不过它实在是太重了,虽然它功能强大,对于在内网又没多少用户的情况下属于杀鸡用牛刀。毕竟我既不是搞安全的,也不是Devops,只要能实现对多个APP实现一个统一的验证和单点登录就够了。因此,对CPU和内存友好的Authelia映入我的眼帘。

authelia001.jpeg

2.2 部署条件

老样子,还是在公司内网服务器的环境部署,只有内网IP一枚,不同的APP用不同的端口来网文。首先,第一步还是要把容器跑起来,其次能通过Nginx反向代理访问WEBUI的界面。

  • 存储使用了SQLite。
  • 用户数据库使用的是本地文件。
  • 暂时先没加IDP的功能。
  • 通知没有添加SMTP,只是使用了本地文件。

未来会提供IDP的功能,使用OIDC来给自己的项目发Token。这样我的项目就不用自己写登录和鉴权的代码了。独立数据库和Redis暂时还不算上,毕竟就是一个小项目。

2.3 文件结构

在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

2.4 Docker Compos文件

  • 将Authelia加入到Nginx反向代理的Docker网络了,不需要直接将端口映射到主机服务器上了。
  • 容器默认使用9091端口。
  • 将相应的文件映射到容器外部。
yaml
services: 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

2.5 Nginx配置文件

我没有按照官网的比较复杂的配置去做,先把请求放进来。

bash
server { 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; } }

2.6 Authelia配置文件

  • 通过session cookie配置应用的重定向等设置。访问AUthelia的web界面成功后跳转到另一个8001应用。
  • 用户认证使用本地的yaml文件
  • 访问控制,对于Authelia的登陆界面直接放行,对于另一个8012的应用需要账号密码验证。
  • 存储使用SQLite。
  • 通知使用文件记录。
  • 对于重置密码,重置邮件的内容是记录在通知指定的文件里的。
bash
theme: 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

2.7 用户数据库

使用本地Yaml文件。argon2id的密码可以通过下面的命令生成,也就意味着可以先留空,可以在容器跑起来之后再运行这个命令。更新YAML文件后,需要重启容器重新加载用户数据库的内容。

docker run --rm authelia/authelia:latest authelia crypto hash generate argon2 --password 'PASSWORD'

yaml
users: admin: displayname: "Admin" password: "$argon2id$v=19$m=65536,t=3,p=4$BzODJr+sliIj25pOvXlNHQ$3GVps1iOE/JezpAmnXLK3mW1L3UU+rXvZHtLIOHGiA0" email: admin@example.com groups: - admins

2.7 私钥和其他密钥

生成私钥到文件。其他的密钥可以直接放到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

2.8 WEBUI界面

只有一个登陆界面,账号密码验证成功后,直接挑战到Session Cookie里默认的跳转界面。

authelia002.jpeg

3. 后记

本来搭建起来后,Authelia的Web界面是可以访问的。登录成功后,跳转也是成功的。但是如果再启用IDP的,并试图添加客户,配置没问题。虽然后台显示Nginx将请求发给你验证节点了,但是从浏览器的角度来看是直接可以访问了。上网也查了一些问题的可能性。

首先,Cookies是根据DNS名或IP来划分的,不支持我们这种以端口来区分服务的情况。所以没办法根据SERVER_IP

来设置不同的Cookie。

bash
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

其次,在Access Control的部分,虽然只是写正则匹配IP和端口,但是每回都是被IP直接给截胡了,所以就bypass了。

bash
access_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 许可协议。转载请注明出处!