Caddy加Authelia实现2FA

之前在03服务器上搭了个wetty,用了Caddy的basic_auth实现的单次验证。感觉不够安全,需要增加2FA。
于是问了Deepseek和几个AI,很多方案都提到Authelia,而且Caddy的某个版本以上支持用forward_auth使用Authelia实现2FA。于是定下了这个方案。

在实现的过程中踩了很多坑。Deepseek和ChatGPT给的方案都没错,但是具体的配置文件却有很多问题,直接使用报错。记录了调试过程以便日后查询。
目前(2025.3)使用的是latest(4.39.1)版的authelia/authelia docker镜像

原始架构

1
用户 -> Caddy (host) -> 应用 (port 3000)

改造后架构

1
用户 -> Caddy (host) -> Docker Authelia -> 应用 (port 3000)

最终效果

访问https://wt.aaray.eu.org,自动跳转到Authelia的登录页面


登录

提示输入一次性密码(TOTP)

运行手机上的微软Authenticator程序,选择aaray.eu.org,点开会有6位密码,输入之后登录成功。
微软Authenticator无法截图。另外,还可以用Bitwarden或者类似程序代替微软的。
验证通过后回到Caddy原来的应用。

部署步骤

部署文件结构

~/docker/authelia

1
2
3
4
5
6
7
8
9
10
11
authelia/
├── config
│ ├── configuration.yml
│ ├── db.sqlite3
│ └── notification.txt
├── docker-compose.yml
├── secrets
│ ├── jwt_secret
│ └── storage_encryption_key
└── users
└── users.yml

创建 Authelia 容器

1
2
3
4
5
6
# 创建配置目录
mkdir -p ~/docker/authelia/{config,users,secrets}

# 生成密钥文件
openssl rand -base64 32 > ~/docker/authelia/secrets/jwt_secret
openssl rand -base64 32 > ~/docker/authelia/secrets/storage_encryption_key

用户数据库 authelia/users/users.yml

1
2
3
4
5
6
7
users:
admin:
displayname: "Admin"
password: "$argon2id$v=19$m=65536,t=3,p=4$BpLnfgDsc2WD8F2q$..." # 使用 docker run --rm authelia/authelia authelia crypto hash generate 生成
email: admin@yourdomain.com
groups:
- admin

我的users.yml文件。如果需要添加用户,需要在文件里面添加。目前(2025.3)版本还不支持UI添加。
但是authelia除了使用本地users.yml文件,还支持ldap。所以如果需要此功能,可以引入ldap来实现更复杂的功能。

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
users:
admin:
password: $argon2id$v=19$m=65536,t=3,p=4$qnAy4ozf/j+xu37IG***
displayname: Admin
email: a***y@2***m
groups:
- admin
given_name: ""
middle_name: ""
family_name: ""
nickname: ""
gender: ""
birthdate: ""
website: ""
profile: ""
picture: ""
zoneinfo: ""
locale: ""
phone_number: ""
phone_extension: ""
disabled: false
address: null
extra: {}
ubuntu:
password: $argon2id$v=19$m=65536,t=3,p=4$VEGQS77uz9JMZ3DoC***
displayname: Ubuntu
email: a***cn@g***m
groups:
- admin
given_name: ""
middle_name: ""
family_name: ""
nickname: ""
gender: ""
birthdate: ""
website: ""
profile: ""
picture: ""
zoneinfo: ""
locale: ""
phone_number: ""
phone_extension: ""
disabled: false
address: null
extra: {}
aaray:
password: $argon2id$v=19$m=65536,t=3,p=4$CrnfNabFimVO7D6PSa***
displayname: Ray
email: a***n@g***m
groups:
- admin
given_name: ""
middle_name: ""
family_name: ""
nickname: ""
gender: ""
birthdate: ""
website: ""
profile: ""
picture: ""
zoneinfo: ""
locale: ""
phone_number: ""
phone_extension: ""
disabled: false
address: null
extra: {}

docker-compose.yml

1
2
3
4
5
6
7
8
9
10
services:
authelia:
container_name: authelia
ports:
- 127.0.0.1:9091:9091
volumes:
- /home/ubuntu/docker/authelia/config:/config
- /home/ubuntu/docker/authelia/users:/etc/authelia/users
- /home/ubuntu/docker/authelia/secrets:/secrets
image: authelia/authelia:latest

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
###############################################################
# Authelia configuration #
###############################################################

server:
address: 'tcp://0.0.0.0:9091/'

log:
level: 'debug'

identity_validation:
reset_password:
jwt_secret: "@file:/secrets/jwt_secret"

totp:
issuer: aaray.eu.org

authentication_backend:
file:
path: /etc/authelia/users/users.yml #Authelia验证账户存放文件可接入ldap

access_control:
default_policy: deny
rules:
- domain: a1.aaray03.theworkpc.com
policy: two_factor #需要两个验证条件
- domain: wt.aaray03.theworkpc.com
policy: two_factor #需要两个验证条件
- domain: wt.aaray.eu.org
policy: two_factor #需要两个验证条件


session:
name: authelia_session
secret: "@file:/secrets/jwt_secret"
expiration: 3600 # 1 hour
inactivity: 300 # 5 minutes
cookies:
- domain: 'a1.aaray03.theworkpc.com' # 被保护的域名
name: 'authelia_session'
authelia_url: 'https://auth.a1.aaray03.theworkpc.com'
default_redirection_url: 'https://a1.aaray03.theworkpc.com' #默认重定向url
same_site: 'lax'
inactivity: '5 minutes'
expiration: '1 hour'
remember_me: '1 month'
- domain: 'wt.aaray03.theworkpc.com' # 被保护的域名
name: 'authelia_session'
authelia_url: 'https://auth.wt.aaray03.theworkpc.com'
default_redirection_url: 'https://wt.aaray03.theworkpc.com/wetty' #默认重定向url
same_site: 'lax'
inactivity: '5 minutes'
expiration: '1 hour'
remember_me: '1 month'
- domain: 'wt.aaray.eu.org' # 被保护的域名
name: 'authelia_session'
authelia_url: 'https://auth.wt.aaray.eu.org'
default_redirection_url: 'https://wt.aaray.eu.org/wetty' #默认重定向url
same_site: 'lax'
inactivity: '5 minutes'
expiration: '1 hour'
remember_me: '1 month'

regulation:
max_retries: 3
find_time: 120
ban_time: 300

storage:
encryption_key: 'f8299616-****-4481-****-04acb13caf8d'
local:
path: /config/db.sqlite3

notifier:
smtp:
address: 'submission://smtp.gmail.com:587'
username: 'a***n@g***m' # Password can also be set using a secret: https://www.authelia.com/configuration/methods/secrets/
password: '<google app password>'
sender: 'a***n@g***m'
timeout: '5s'
identifier: 'localhost'
subject: "[Authelia] {title}"

Caddy配置文件/etc/caddy/Caddyfile

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
...

a1.aaray03.theworkpc.com {
# 新增认证网关
forward_auth localhost:9091 {
uri /api/verify?rd=https://auth.a1.aaray03.theworkpc.com/auth
#uri /api/authz/forward-auth
copy_headers Remote-User Remote-Groups Remote-Name Remote-Email
}

# 保持原有代理配置
reverse_proxy localhost:4880 # 假设原应用运行在宿主机3000端口
}

# Authelia管理界面
auth.a1.aaray03.theworkpc.com {
reverse_proxy localhost:9091
}

# SMTP4DEV管理界面
m.aaray03.theworkpc.com {
basicauth / {
aaray $2a$14$5El1SQdjjbT5SlfSk***C
}
reverse_proxy localhost:5000
}

wt.aaray03.theworkpc.com {
redir / /wetty 301
# 新增认证网关
forward_auth localhost:9091 {
uri /api/verify?rd=https://auth.wt.aaray03.theworkpc.com/auth
copy_headers Remote-User Remote-Groups Remote-Name Remote-Email
}

# 保持原有代理配置
reverse_proxy localhost:3000 # 假设原应用运行在宿主机3000端口
}

# Authelia管理界面
auth.wt.aaray03.theworkpc.com {
reverse_proxy localhost:9091
}

wt.aaray.eu.org {
redir / /wetty 301
# 新增认证网关
forward_auth localhost:9091 {
uri /api/verify?rd=https://auth.wt.aaray.eu.org/auth
copy_headers Remote-User Remote-Groups Remote-Name Remote-Email
}

# 保持原有代理配置
reverse_proxy localhost:3000 # 假设原应用运行在宿主机3000端口
}

# Authelia管理界面
auth.wt.aaray.eu.org {
reverse_proxy localhost:9091
}

部署与验证

启动服务

1
2
3
4
#启动
docker-compose up –d
#停止
docker-compose down

测试流程

参见最终效果

访问 https://yourdomain.com
自动跳转至 https://auth.yourdomain.com
首次登录需:
输入用户名密码
配置 TOTP(使用 微软/谷歌 Authenticator 等应用扫码)
后续登录需密码 + 动态码

进阶配置

使用 Redis 提升性能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# docker-compose.yml 添加
services:
redis:
image: redis:alpine
volumes:
- redis_data:/data
networks:
- auth_net

volumes:
redis_data:

# 修改 Authelia 配置
session:
redis:
host: redis
port: 6379

邮件自动注册

1
2
3
4
5
6
7
8
9
10
# configuration.yml 添加
identity_providers:
oidc:
hmac_secret: "@file:/etc/authelia/secrets/hmac_secret"
clients:
- id: myapp
description: "My Application"
secret: "supersecret"
redirect_uris:
- https://app.example.com/oauth2/callback

故障排查命令

查看容器日志:

1
2
docker-compose logs -f caddy
docker exec -it authelia authelia --config /etc/authelia/configuration.yml validate

进入容器调试:

1
2
docker exec -it caddy sh
docker exec -it authelia /bin/bash

网络连通性测试:

1
docker run --rm --network auth_net curlimages/curl curl -v http://authelia:9091/api/health

安全加固建议

容器隔离

1
2
3
4
5
services:
authelia:
security_opt:
- no-new-privileges:true
read_only: true

密钥管理:

1
2
# 使用 Docker Secrets
echo "smtp_password" | docker secret create authelia_smtp_pass -

维护命令速查

灾难恢复

1
2
# 定期备份
0 3 * * * tar czf /backups/authelia-$(date +\%Y\%m\%d).tgz ~/authelia

Cloudflare配置

1
2
CNAME/wt/wt.aaray03.theworkpc.com/Proxied/Auto
A/auth.wt.aaray.eu.org/129.154.215.168/DNS only/Auto

第一行是增加CNAME代理wt.aaray.eu.org指向wt.aaray03.theworkpc.com以提高访问速度
第二行之所以auth.wt.aaray.eu.org加的是A记录不是CNAME,因为Cloudflare超过2级别域名,证书不免费。所以放弃代理功能,直接指向服务器IP。证书由Caddy搞定。

Authelia配置遇到的问题

用文件代替SMTP的通知

1
2
3
notifier:
filesystem:
filename: /config/notification.txt #发送邮件存放地点

在配置好SMTP之前,可以用文件,原本需要发的邮件内容会写入notification.txt中,比如绑定/解绑设备等操作。

139邮箱没搞定

139邮箱没配置成功,

临时SMTP服务器smtp4dev

用smtp4dev搭建了一个smtp服务器,不能真转发邮件。邮件都保存在smtp4dev中,有web ui可以查看。

1
2
3
4
5
6
7
8
services:
smtp4dev:
stdin_open: true
tty: true
ports:
- 5000:80
- 2525:25
image: rnwood/smtp4dev

no route to host

两个独立的docker container之间通信需要打开防火墙

1
sudo iptables -I INPUT -p tcp -m tcp --dport 2525 -j ACCEPT

用smtp4dev时,authelia的configuration.yml notifier配置如下

1
2
3
4
5
6
7
8
notifier:
smtp:
address: 'smtp://172.17.0.1:2525'
timeout: '5s'
sender: "Ray <q***i@139.com>"
identifier: 'localhost'
subject: "[Authelia] {title}"
disable_starttls: true

用Gmail做SMTP服务器

需要获得app password,而打开app password的前提是开启多重验证。详细信息看这里