Skip to content
twinkling.top服务AutheliaEN
板型any Pi 4 or 5; the login path is a hash computation and the session path is a signed cookie check
内核6.6.51+rpt-rpi-v8
空闲温度48 °C
内核架构arm64
参考机:Pi 4B 8GB / Bookworm 64-bit

部署配方 · 密钥与身份

Authelia:给值得保护的服务装一道统一的门

机架上不是每个服务都需要第二道登录界面。但那些装着东西的——密码库、文档、带着主机名和端口信息的仪表盘——需要,而且它们不该各自长出一张用户表。Authelia 待在反向代理后面,对每个请求回答一个问题,并维护唯一一份「谁能看什么」的名单。

authelia/authelia:4.38.19 · 2026-10-05 ·

Authelia rack plate: board stack and host ports
服务参数
容器镜像authelia/authelia:4.38.19
宿主端口:9091/tcp
数据目录/srv/homelab/authelia/config
内存预留120 MB
CPU 上限0.25 vCPU (cpus: "0.25")
更新节奏twice a year. Configuration keys are renamed between minor versions fairly often, so read the release notes and keep the old config in git before bumping
ARM 兼容arm64 native
端口映射与暴露面
宿主容器proto暴露用途
:90919091tcp仅局域网the portal itself and the forward-auth endpoint the proxy calls for every protected request

部署步骤

  1. 先于一切生成三个密钥

    会话、存储加密和身份校验 JWT 是三个独立的值。它们放在文件里而不是配置里,也绝不能从另一套安装里复用。

    run
    sudo mkdir -p /srv/homelab/authelia/{config,secrets}
    for s in session storage jwt; do
      openssl rand -base64 48 | tr -d '\n' | sudo tee /srv/homelab/authelia/secrets/$s >/dev/null
    done
    sudo chmod 600 /srv/homelab/authelia/secrets/*
  2. 用哈希后的密码创建用户数据库

    每个密码都要过一遍容器自带的哈希命令。displayname 和 email 字段有意义,因为代理会把它们作为请求头传给后面的应用。

    run
    docker run --rm authelia/authelia:4.38.19 authelia crypto hash generate argon2 --password 'CHANGE_ME'
    # users_database.yml:
    # users:
    #   ada:
    #     displayname: Ada
    #     password: '$argon2id$v=19$m=65536,t=3,p=4$...'
    #     email: [email protected]
    #     groups: [family, admins]
  3. 写好配置,策略默认拒绝

    从全部拒绝开始,然后一次一个地加上你真正要保护的主机。顺序很重要:第一条匹配的规则生效,所以具体的规则要放在宽泛的上面。

    run
    cat > /srv/homelab/authelia/config/configuration.yml <<'EOF'
    host: 0.0.0.0
    port: 9091
    theme: dark
    session:
      name: authelia_session
      domain: lan
      expiration: 1h
      remember_me: 30d
    storage:
      local:
        path: /config/db.sqlite3
    access_control:
      default_policy: deny
    EOF
  4. 接上代理,确认会发生跳转

    用一个没有会话的浏览器测试。预期行为是跳转到门户,并带一个 redirect 参数,登录后回到你原本请求的页面。

    run
    docker compose up -d authelia
    docker logs --tail 30 authelia
    curl -sI https://vault.lan | head -3
    # 预期 302 到 https://auth.lan/?rd=https%3A%2F%2Fvault.lan%2F
  5. 给每个账号登记双因素,并检查绕过路径

    现在就登记,而不是等有人第一次要用密码库时。同时确认监控的绕过路径在无会话时仍然可用——一个被跳转到登录页的状态检查,会在服务其实已经挂了的时候报「正常」。

    run
    curl -s -o /dev/null -w '%{http_code}\n' http://192.168.10.20:9110/api/health
    # 预期 200,且不带会话 cookie

代理和 Authelia 如何分工

对受保护主机的每个请求,代理都会向 Authelia 的 forward-auth 端点发一个子请求并复制它的答复。200 就是放行;302 指向门户,意味着浏览器去登录,回来后代理再问一次。

关键细节在于执行这件事的是 Caddy,不是应用本身。应用根本看不到未认证的请求,也就是说,一个自带弱登录的服务,依然被前面这道闸门保护着。

shell
# Caddyfile
secure.lan {
  forward_auth authelia:9091 {
    uri /api/verify?rd=https://auth.lan/
    copy_headers Remote-User Remote-Groups Remote-Name Remote-Email
  }
  reverse_proxy cloud:80
}

默认说不的规则

访问控制段从上往下评估,第一条匹配的生效。安全的写法是底部放一条兜底的拒绝,上面是明确的放行,这样新增一个主机却没有加规则时,不会意外把它公开出去。

有两类对象需要区别对待:不装敏感内容的服务用单因素,密码库和文档用双因素,只有监控系统需要无会话访问的健康检查路径才用 bypass。

shell
# /srv/homelab/authelia/config/configuration.yml
access_control:
  default_policy: deny
  rules:
    - domain: vault.lan
      policy: two_factor
    - domain: cloud.lan
      subject:
        - ["group:family"]
      policy: two_factor
    - domain: status.lan
      resources:
        - "^/api/.*$"
      policy: bypass
    - domain: status.lan
      policy: one_factor

正确地生成密码哈希

Authelia 存的是 Argon2id 哈希,而生成它的唯一靠谱办法是用容器自带的命令,因为参数必须和正在运行的版本期望的一致。在别处生成再粘回来,就是人们得到「登录永远失败、却没有任何错误提示」的原因。

密钥同理:会话、存储加密和 JWT 密钥是三个独立的随机值,放在文件里;而轮换存储加密密钥需要一次迁移步骤,而不只是重启。

shell
docker run --rm authelia/authelia:4.38.19 authelia crypto hash generate argon2 --password 'the-password'
# 把摘要粘进 users_database.yml,绝不粘密码本身

家人实际体验到的样子

在 auth.lan 登录一次,可选记住三十天,之后门户就退到幕后。家庭里对这套的抵触来自两个地方:每次有人想看照片都要双因素,以及会话在正做事的时候过期。

两者都是配置问题。七天的会话加记住我 cookie 解决第一个;第二个正是「记住我」存在的意义。会话时长该按你对家里这些设备的信任程度来定,而不是按文档的建议值。

shell
# 值得调的会话与限流设置
session:
  expiration: 1h
  inactivity: 5m
  remember_me: 30d
regulation:
  max_retries: 3
  find_time: 2m
  ban_time: 5m

compose 文件

整份文件放在 /srv/homelab/authelia/compose.yaml。标签固定版本号,不用 latest——树莓派上回滚比升级麻烦得多。

compose.yaml
services:
  authelia:
    image: authelia/authelia:4.38.19
    container_name: authelia
    restart: unless-stopped
    ports:
      - "9091:9091/tcp"
    environment:
      TZ: Asia/Shanghai
      AUTHELIA_SESSION_SECRET_FILE: /secrets/session
      AUTHELIA_STORAGE_ENCRYPTION_KEY_FILE: /secrets/storage
      AUTHELIA_IDENTITY_VALIDATION_RESET_PASSWORD_JWT_SECRET_FILE: /secrets/jwt
    volumes:
      - /srv/homelab/authelia/config:/config
      - /srv/homelab/authelia/secrets:/secrets:ro
    networks: [rack]

networks:
  rack:
    external: true

加固清单

  • 门户只在局域网内可达;代理会拒绝不是来自代理自身的 forward-auth 请求
  • 密码以 Argon2id 哈希存储,由容器自带的哈希命令生成,明文绝不进配置文件
  • 会话 cookie 用文件里的密钥签名,即使 cookie 在共享网络上被截获也无法篡改
  • 每个受保护主机一组规则,默认拒绝:没有在访问控制文件里写明的路径就不可达
  • 除一份很小的绕过清单外,一切都需要双因素;清单里每一条都要写明理由

备份方案

配置目录里有用户数据库(含密码哈希)、各种密钥和访问规则。它很小,而且在 git 里,唯一被排除的是密钥文件。丢了它意味着每个用户都要重新登记双因素——对一个四口之家来说,就是一个晚上。

上线后验证

  • 未认证访问受保护主机会跳转到门户,登录后回到原 URL
  • 密码库要求双因素,而状态页只要求密码
  • 不在正确组里的用户得到 403 而不是登录循环,说明规则确实是按组匹配的,而不只是按域名
  • 重启 Authelia 不会把任何人登出,说明会话密钥在重启之间是稳定的

踩过的坑

  • 密钥在重启之间绝不能变。重新生成会话密钥会把所有人登出;重新生成存储加密密钥会让已有的用户数据库变得不可读,那就要恢复而不是修复了。
  • 用别的 Argon2 实现、参数又不同生成的密码哈希,会验证失败且不给出有用的错误。永远用你正在运行的那个镜像版本自带的哈希命令。
  • 绕过规则里写一个宽泛的路径正则,就是认证网关「其实什么都没保护」的由来。只匹配监控系统实际使用的那个精确 API 前缀,别的都不要。
  • forward-auth URL 里的 redirect 参数是浏览器会去跟随的。Authelia 会校验它,但一个被手工改成指向意外地址的值,值得在第一周的日志里留意。

硬件与选型问答

四口之家值得上这个吗?
值得,但更多是因为它消掉的那堆重复登录,而不是它增加的安全性。四个服务各有一张账号表,就是四个要重置密码的地方;一个门户一份用户名单,就是一个。安全上的收益是真实的,但是次要的——真正的收益是它后面的服务可以完全不设登录。
Authelia 还是 Authentik 还是 Keycloak?
Authelia 最小,也最贴合 forward-auth 代理模式,代价是它不是完整的身份提供者——没有那些想要 OIDC 流程的应用能用的 OIDC。Authentik 是折中路线,更重但支持更多协议。Keycloak 是企业系统,它就属于企业。
Authelia 挂了会怎样?
代理后面每个受保护的服务都会报错,因为验证子请求失败了。这是诚实的失败模式:失败即关闭。值得为监控留一条绕过路径,这样你是从状态页而不是从家人那里知道这次故障的。