
前言
我个人的很多服务是暴露在公网的,虽然传输过程已经通过反代获得 HTTPS 加密,但是网页本身的安全性并不见得一定够高。像是 LibreSpeed、HomePage 这种服务,其本身设计上就没有考虑暴露在公网的情况,但我恰恰有需要他们在公网环境下工作(公网测速、公网访问)。
最开始我的选择是使用 Basic Auth,这个所有的反代工具都会提供,需要你输入密码才能访问网站,但问题就在于它“太 Basic 了”,密码设置的太复杂不方便记,用密码管理器没办法自动填充且太麻烦;密码设置的太简单容易被爆破,最后变得形同虚设。如果能从反代层面为这些网站添加一个前置验证,使用账号密码 + 2FA的方式保护网站,那么安全性与便捷性都可以得到提升。在诸多解决方案中,我选择了 Authelia。

在 Docker Compose 中使用 Authelia
这部分官方文档写的太复杂,网上的教程能参考的又太少,最后在借助 Claude 的帮助下才搞出一个能用的配置(GPT 都抓瞎,不知道为啥现在 AI 降智这么严重),下面直接给出配置:
Caddy docker-compose.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| services: caddy: image: ghcr.io/sixiaolong1117/my-caddy:latest container_name: caddy restart: always networks: - homelab cap_add: - NET_ADMIN ports: - "80:80" - "443:443" - "443:443/udp" volumes: - ./conf:/etc/caddy - ./site:/srv - caddy_data:/data - caddy_config:/config environment: - DNS=223.5.5.5,8.8.8.8 - TZ=Asia/Shanghai
volumes: caddy_data: caddy_config:
networks: homelab: external: true
|
Authelia docker-compose.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| services: authelia: image: docker.io/authelia/authelia:latest container_name: authelia volumes: - ./authelia/config:/config networks: - homelab ports: - 9091:9091 restart: unless-stopped environment: - TZ=Asia/Shanghai healthcheck: test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:9091/api/health"] interval: 30s timeout: 10s retries: 3
redis: image: redis:alpine container_name: authelia-redis volumes: - ./authelia/redis:/data networks: - homelab expose: - 6379 restart: unless-stopped environment: - TZ=Asia/Shanghai
networks: homelab: external: true
|
此处我将 Caddy 与 Authelia 分在不同的 Compose 中,是因为我的 Caddy 要为很多服务提供反代,它需要独立运行以适配我的自定义配置(上述配置中未列出)。因为分开了,为了后面方便容器内网络互访,我将他们统一添加到了提前创建好的外部网络 homelab
中。对于没有相关需求的,可以直接将二者合为一体,这样就可以免去多余的外部网络配置。
Authelia 配置文件 ./authelia/config/configuration.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
| ---
theme: auto
jwt_secret: your-super-secret-jwt-key-change-this-please
default_redirection_url: https://auth.yourdomain.com
server: host: 0.0.0.0 port: 9091 path: "" enable_pprof: false enable_expvars: false disable_healthcheck: false tls: key: "" certificate: ""
log: level: info format: text file_path: /config/authelia.log keep_stdout: true
totp: disable: false issuer: yourdomain.com algorithm: sha1 digits: 6 period: 30 skew: 1
webauthn: disable: false timeout: 60s display_name: Authelia attestation_conveyance_preference: indirect user_verification: preferred
authentication_backend: password_reset: disable: false refresh_interval: 5m file: path: /config/users_database.yml password: algorithm: argon2id iterations: 3 salt_length: 16 parallelism: 4 memory: 64
access_control: default_policy: deny rules: - domain: auth.yourdomain.com policy: bypass - domain: "*.yourdomain.com" policy: two_factor
session: name: authelia_session domain: yourdomain.com same_site: lax secret: your-super-secret-session-key-change-this-please expiration: 1h inactivity: 30m remember_me_duration: 1M redis: host: authelia-redis port: 6379
storage: encryption_key: your-super-secret-encryption-key-change-this-please local: path: /config/db.sqlite3
notifier: disable_startup_check: false filesystem: filename: /config/notification.txt
regulation: max_retries: 3 find_time: 2m ban_time: 5m
|
- 配置中的
jwt_secret
、session.secret
与 storage.encryption_key
可以通过命令:
生成随机哈希填进去。
- 配置中的所有
yourdomain.com
都要替换为实际域名。
Authelia redis 用户数据库文件 ./authelia/config/users_database.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| ---
users: admin: displayname: "管理员" 默认密码: password123 (容器内生成密码哈希,见下文) email: [email protected] groups: - admins - dev
user: displayname: "普通用户" 默认密码: password123 (容器内生成密码哈希,见下文) email: [email protected] groups: - users
|
密码可以通过容器内工具生成(注意修改 your-password-here
):
1 2
| docker run --rm authelia/authelia:latest authelia crypto hash generate argon2 --password 'your-password-here'
|
Caddy 配置
我们需要首先为 Authelia 认证门户本身进行反代:
1 2 3
| auth.yourdomain.com { reverse_proxy authelia:9091 }
|
此处可以使用 authelia:9091
的原因就在于 Caddy 与 Authelia 在同一个网络中。
其他需要借助 Authelia 门户认证的网页,可以设置一个 forward_auth
:
1 2 3 4 5 6 7 8
| homepage.yourdomain.com { forward_auth authelia:9091 { uri /api/verify?rd=https://auth.yourdomain.com copy_headers Remote-User Remote-Groups Remote-Name Remote-Email }
reverse_proxy <IP>:<Port> }
|
如果你使用非标准端口(如 44443
),则应该转到:
1
| /api/verify?rd=https://auth.yourdomain.com:44443
|
Authelia 认证门户设置
通过访问 auth.yourdomain.com
进入 Authelia 的 Web 页面,东西很少,一共就支持俩东西:TOTP 和 WebAuth,没啥可讲的。
这里主要说一下 Authelia 在设计上是需要通过发送邮箱验证码来确认用户身份的,你添加 TOTP 和 WebAuth 都会提示从邮箱接收验证码。
但是如果你只是个人使用,不想配置 SMTP(前文注释部分),而是使用了 filesystem(前文配置),Authelia 前端依旧会告诉你发送了右键,不过你可以通过 ./authelia/config/notification.txt
来获取验证码内容通过验证。
有关 Authelia 配置的补充
一些应用需要通过 APP 使用,这些 APP 的主流实现方式就是填入 URL 与账号密码,直接登陆。设置了 Authelia forward_auth
之后这些 APP 的使用会出现异常,所以我们需要在 access_control
部分添加相应的 Bypass。例如对于 Gotify 来说:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| access_control: default_policy: deny rules: - domain: auth.yourdomain.com policy: bypass
- domain: "gotify.yourdomain.com" policy: bypass resources: - '^/message([/?].*)?$' - '^/version([/?].*)?$' - '^/stream([/?].*)?$' - '^/current([/?].*)?$' - '^/client([/?].*)?$' - '^/application([/?].*)?$' - '^/user([/?].*)?$' - '^/health([/?].*)?$' - '^/plugin([/?].*)?$' - '^/image([/?].*)?$' - '^/static/.*$' - domain: "*.yourdomain.com" policy: two_factor
|
对于不同的网站来说,需要设置 Bypass 的规则也不同,有需要的建议在 AI 的帮助下结合个人实践酌情添加。不建议为有重度 APP 需求,且页面过于复杂的网站添加 Authelia 2FA。
结语
Authelia 本身的配置以及与 Caddy 结合使用都不算复杂,但是因为文档太繁琐,教程太少,中间还是踩了不少坑。
Authelia 支持 WebAuth,我一共存储了两个密钥,一个在 Bitwarden 中用于从我自己的电脑快速登陆,一个在之前文章中提到的 Pico Key 中,可以在陌生设备上快速登陆,实现了便捷性与安全性双赢。