关于我们

质量为本、客户为根、勇于拼搏、务实创新

< 返回新闻公共列表

2026 服务器租用自建单点登录避坑:Authelia 与 OIDC 反向代理鉴权的部署全攻略

发布时间:2026-10-21

七个系统七套密码,离职同事的账号清不干净

典型部署场景是这样的:一台租来的物理机或者一台大内存的云主机上跑着 Grafana 看板、内部 Wiki、自建 Git 仓库、网盘、工单后台、监控告警台和一个自建的运营后台,一共七个 Web 入口。每个系统各自维护一套用户表,各有一套密码规则,各有各的「忘记密码」。同事入职要开七次账号,离职要关七次——实际情况是只关了三个,剩下四个还挂在已经离职半年的人名下,密码写在某个共享文档里。等到做一次内部权限梳理,审计方点名问「这套网盘现在的实际使用者是谁」,没人答得上来。

这类问题不是密码不够复杂,而是身份没有一个统一的出口。账号的生命周期(开、改、关)散落在七个地方,只要有一处漏关,权限就一直挂着。想一次性解决,方向只有两个:要么让每个应用都去接一套标准的 OIDC 登录,要么在应用前面加一层统一的认证,把所有请求先过一道闸。前者听起来更「正规」,后者在现实里更常见——因为你手上一半的系统压根不支持 OIDC,或者支持但要改配置、要装插件、要付费版。

自建这个统一出口的成本其实很低:一台能跑容器的机器、一个域名、一份配置文件。真正难的是后面那些不写在教程首页的东西——存储怎么选、会话放哪、第二因素丢了怎么办、密钥备份到哪、上线那天怎么保证不把自己锁在门外。这篇就把这些细节摊开讲。

反向代理前置认证与应用原生 OIDC 的分界在哪

两条路线的差别,本质上就是「认证发生在哪一跳」。反向代理前置认证(Authelia 这类方案配合 Nginx 的 auth_request、Traefik 的 ForwardAuth、Caddy 的 forward_auth)把认证压在 HTTP 请求进入应用之前:请求先到反代,反代拿 Cookie 或 Authorization 头去问一次认证服务,通过了才转发给后端。应用自己完全不知道有认证这回事,它只在请求头里看到一个 Remote-User。这种方案对后端是零侵入的——Grafana 那种老版本、内部自研的后台、甚至一个纯静态页面,都能被罩住。

应用原生 OIDC/SAML 则是让每个应用自己当 Relying Party,自己去发现 IdP、换 token、校验签名、建本地会话。好处非常实在:应用拿到的是标准化的身份声明,能拿到组、角色、权限范围,做细粒度授权很自然;用户在应用内的体验也完整——退出登录、切换账号、移动端 App 拉起系统浏览器登录,这些都能正常工作,而不是被一个反代 Cookie 卡住。缺点是要求应用本身支持,而且每个应用都要配一遍 client id、secret、回调地址。

我的判断是:能原生接 OIDC 的系统就原生接,接不了的再用反代兜底,别图省事把所有东西都塞到反代后面。一刀切走反代,短期配置量小,长期会遇到两类麻烦:一是应用内部那些「当前用户」相关的功能(审计日志里的操作者、应用内的权限分组、API token)全都退化成一个 HTTP 头,粒度粗;二是移动端和 API 调用很难走 Cookie 这条链路,到头来还是得给应用单独开 OIDC。

反向代理方案还有一个必须提前认下来的硬局限:它只能拦住经过反代的 HTTP 请求。后端服务如果同时监听了一个没走反代的端口,或者运维为了调试把容器端口直接映射到了宿主机 0.0.0.0,那这条路径完全绕过了认证。非 HTTP 协议也一样——SSH、数据库直连、Redis、gRPC(如果没走 HTTP/2 网关)、消息队列,反代一个都管不着。所以「上了统一登录」不等于「访问都被管住了」,端口层面的收敛还得单独做。

Authelia 同时当 OIDC Provider 和 forward-auth 校验方

Authelia 在这个体系里其实有两个身份,而且这两个身份互不冲突、但必须分别配置。第一个身份是 OIDC Provider(OP):它自己签发 id_token,维护 client 注册、scope、授权端点、token 端点、JWKS 公钥。支持 OIDC 的应用(比如新版 Grafana、MinIO、很多自研系统)可以直接把 Authelia 当 IdP 接进去,走标准的授权码流程。

第二个身份是 forward-auth 的校验方:反代把请求头转给 Authelia 的验证端点,Authelia 只回答「放不放行」以及「放行的话这个用户是谁、属于哪些组、邮箱是什么」。这条路径不走 OIDC,就是一次带 Cookie 的内部子请求。

两套配置的关键差别在于:OIDC 侧要配 issuer、client、密钥对、签名算法、有效期,而 forward-auth 侧要配的是会话 Cookie 的 domain、默认重定向地址、以及放行后要往后端注入哪些头。很多人的误区是配好了 OIDC 就以为反代也能用,结果反代一直 401,原因就是两侧的配置项是分开的——一边没配,另一边不会自动生效。实际部署时建议先把 forward-auth 跑通、把老系统罩住,再回头开 OIDC,因为前者的验证更快(浏览器刷一下就知道),后者涉及回调地址和 client 密钥,排查链路更长。

一次请求在反代侧走完的路:从子请求到 Remote-User

把流程拆开看,一次带登录态的请求大概是这样走的。浏览器发起 GET /dashboard,请求打到反代;反代不直接转发,而是先向 Authelia 的验证端点发一个内部子请求,把原始请求的方法、URI、Host、Cookie、Authorization 头原样带过去(这一步在 Nginx 里是 auth_request,在 Traefik 里是 ForwardAuth 中间件,在 Caddy 里是 forward_auth)。

Authelia 收到后做三件事:解析会话 Cookie 拿到会话标识 → 到会话存储里查这个会话是否有效、是否过期、是否满足目标 URL 的访问控制策略 → 根据结果返回 200 或 401/302。返回 200 时,它会附带几个头:Remote-User(用户名)、Remote-Groups(所属组,多个用逗号分隔)、Remote-Name(显示名)、Remote-Email(邮箱)。反代收到 200 后,把这几个头注入到转发给后端的请求里,同时把请求送过去。

后端应用看到的就是一个「已经认证过」的请求——前提是它被配置成信任这个头。Grafana 有专门的 auth.proxy 模式,很多自研系统和 Wiki 类程序也有类似的「信任反向代理头」开关。这一步是整条链路上唯一需要改后端的地方,改动量极小,这也是这套方案最大的吸引力。

要留意的细节有三个:子请求必须带上原始请求的 Host 和完整路径,否则 Authelia 拿不到目标 URL,访问控制规则就没法按域名和路径匹配;重定向回跳地址要配对,否则登录完成后会跳到错误的域名或者跳回登录页形成死循环;注入头一定要在反代侧先清空客户端传入的同名头,否则外部请求可以自己带一个 Remote-User: admin 进来,如果反代只做「追加」而不做「覆盖」,就有了伪造身份的口子。

直接暴露应用端口,等于把认证拆了

这是自建统一登录最常见的翻车点,没有之一。认证逻辑全在反代上,应用自己不校验任何东西,只认头。那么只要有一条路径能让请求绕过反代直达应用,认证就整体失效——而且失效得非常安静,日志里看不出异常,因为应用本来就不做校验。

常见成因有四类。容器部署时顺手写了 ports: "8080:8080",Docker 会直接把端口绑到宿主机的 0.0.0.0,公网防火墙一开就裸奔;调试时为图方便临时放行了端口,事后忘了收回;应用自己还开了一个管理端口或者健康检查端口,只给主端口套了反代;防火墙只在机房入口做了一层,机器内部的容器网络之间没有隔离,同机其他服务可以直接 localhost 访问。

判断方法很简单:从一台完全在外的机器,直接 curl 应用的原始端口(比如 http://服务器IP:3000),看能不能拿到登录后的页面或者直接拿到数据接口返回。更彻底的做法是定期做一次端口扫描,把监听在 0.0.0.0 上的端口清单拉出来,逐个核对是否都经过反代。

正确的修法是把收敛做在两层:容器端口只映射到 127.0.0.1(写成 127.0.0.1:3000:3000),让应用从物理上只能被本机的反代访问;防火墙默认拒绝所有入站,只放行 80/443 和必要的管理端口,并且管理端口限制来源 IP。这样即便有人误配了映射,也还有第二层兜着。

用户存储三选一:YAML 文件、LDAP/AD、SQL

用户放哪,决定了这套系统能撑多大、改一次要多久、以及能不能被别人接管。Authelia 支持三种后端,适配的形态完全不同。

本地 YAML 文件(通常是 users_database.yml)适合三五个人的小团队。一个人一段配置:用户名、显示名、邮箱、组、以及用 argon2id 或 bcrypt 哈希过的口令。好处是可读、可进 Git、可 diff,出问题时直接看文件就知道有哪些人。上限也很明显:改一次就要让 Authelia 重载配置(文件后端通常不支持热加载,需要重启或发送重载信号),用户改密码必须有人去服务器上改文件再算哈希,几十个人之后这个文件就会变成没人敢动的变更点。

LDAP / Active Directory 适合公司里已经有域控的情况。配置的关键是四个参数:bind DN(Authelia 用来查目录的账号,权限只要能读就够,别给写)、搜索基(用户和组各自一个 base DN)、用户过滤器和组过滤器(用属性匹配而不是用正则捞全量)、组成员判定方式(用 memberOf 属性反查,还是从组的 member 属性正查)。传输必须走 LDAPS,证书校验不要图省事关掉——关掉之后中间人可以直接拿到 bind 口令。AD 环境还要注意属性名差异,比如 sAMAccountName 和 userPrincipalName 的取舍,登录名到底用哪个要一开始就定死,后期改等于所有人的用户名都变。

SQL 后端 分两种用法。SQLite 是单实例部署的默认选择,零依赖、一个文件、备份就是拷文件,缺点是写操作会锁库、多实例没法共享,只能跑一份。PostgreSQL 适合多实例共享状态的场景,配合 Redis 存会话,可以起两个 Authelia 做冗余。代价是你要多维护一个数据库:备份策略、连接数、版本升级、磁盘增长都得管。

一万网络深耕 IDC 19 年(成立于 2007 年),在华南、华东、华北及中国香港、海外多地整合自营节点与 BGP 多线资源,提供服务器租用、机柜托管与带宽接入的一体化交付;具体机型、带宽与报价以官网实时信息或咨询确认为准。如果这套身份系统要长期跑,把它单独放在一台小规格实例上、数据库单独放,比和七个应用挤在一台机器上要稳得多。

对比维度 本地 YAML 文件 LDAP / Active Directory SQL(SQLite / PostgreSQL)
适合规模 三五人到十人以内的小团队,成员变动极少 已有域控的组织,几十到上千人都可承载,复用既有账号生命周期 SQLite 适合单实例十余人;PostgreSQL 适合需要多实例共享的几十人以上
改动如何生效 改文件后需重启或触发重载,无法在线自助改密 域控侧改完即时生效,Authelia 侧通常有缓存需等过期 写入即生效,可配合自助改密与密码重置流程
运维成本 最低,无额外组件,但每次改动都要登服务器 最高,要维护 bind 账号、LDAPS 证书、搜索基与过滤器 中等,PostgreSQL 需额外维护数据库备份与版本升级
可用性与多实例 多实例需靠文件同步,容易出现不一致 依赖域控自身的高可用,域控挂了全体登不上 SQLite 不支持多实例;PostgreSQL 可多实例共享
迁移难度 导出容易,但哈希格式与目标后端不一定兼容 迁出最麻烦,组结构和属性映射要重新设计 中等,表结构清晰,导出导入相对直接
典型坑 人一多文件没人敢改;忘记重载导致改了不生效 证书校验被关掉;时钟偏差导致绑定异常;过滤器写太宽捞出无关账号 SQLite 写锁;PostgreSQL 连接池打满后登录集体变慢

这张表怎么看:先看「改动如何生效」这一行,它决定了用户能不能自助改密码——不能自助就意味着所有密码变更都会变成运维工单。再看「可用性与多实例」,它决定了你能不能做双实例冗余。规模小的团队不要因为「以后可能会扩」就一上來搭 LDAP,运维成本会先压死你;反过来,已经有域控的团队也别为了省事另建一套 YAML 用户库,那等于重新制造了一个清不干净的账号池。

TOTP、WebAuthn 与推送三类第二因素的取舍

第二因素不是「开了就安全了」,不同方式的抗攻击能力和运维负担差得很远。TOTP(手机上的动态口令应用)成本最低、兼容性最好,任何人都能用,缺点是抗钓鱼能力弱——用户在假页面上把动态码填进去,攻击者立刻就能用它登录一次。它能挡住撞库和口令泄露,挡不住实时中间人。

WebAuthn / 安全密钥(FIDO2) 的抗钓鱼能力是本质性的:凭据和来源域名绑定,假域名拿不到合法断言。硬件 Key 适合管理员和高权限账号,平台型(笔记本的指纹、Windows Hello、手机 biometric)适合普通成员,日常体验比掏手机输六位数还快。要注意的是凭据是和设备绑定的,一个人至少要注册两个凭据(比如一个硬件 Key 放抽屉、一个平台凭据日常用),否则设备丢了就进不去。

推送类(Duo 那一类)体验上最接近「点一下确认」,还能带设备合规性判断,但它引入了一个外部依赖:厂商服务不可达时,登录链路会受影响,而且通常要额外账号和费用。自建场景下我一般建议 TOTP 打底、给管理员强制 WebAuthn,推送类留给已经有企业账号体系的团队。

真正决定成败的不是选哪种,而是恢复流程提前设计。要提前定好三件事:一是恢复码——每个用户在注册第二因素时生成一组一次性码,强制下载或打印,存到密码管理器里,并明确「用完要重新生成」;二是手机丢了怎么办——是管理员后台重置第二因素(要求本人走一个线下确认渠道:当面、电话回拨、或者由另一位管理员会签),还是靠备用凭据自愈;三是恢复通道本身不能只有一个管理员,否则那个人休假就全线瘫痪。这个流程要写进文档并演练一次,不然真出事时一定手忙脚乱。

还有个容易吵起来的点:「记住此设备」到底开多久。Authelia 里有设备记住(remember me)和第二次因素跳过两类配置。开得久(比如 30 天)用户体验好,但一台丢了或被借用的设备就成了长期通行证;开得短(比如每次会话都要二次验证)安全性高,内部系统天天输动态码会让人想办法绕开。我的经验是普通成员给 7 到 30 天、管理员组一律不给记住,后台类系统按敏感程度单独收紧。

会话、Cookie 与多实例:登录为什么会随机掉

「随机掉登录」这个现象,几乎一定和多实例下的会话状态有关。Authelia 的会话可以存在内存里,也可以存到 Redis。单实例存内存没问题;一旦起两个实例做冗余,而上游负载均衡没做会话保持,用户第一次请求落在 A、第二次落在 B,B 的内存里没有这个会话,就判定未登录——表现就是刷新一下就被踢回登录页,而且看起来毫无规律。

修法只有两条:把会话放到 Redis 这类共享存储里,所有实例读写同一份;或者在上游做严格的会话保持(按源 IP 或按 Cookie 哈希)。前者更彻底,后者一旦实例重启或扩容,被分走的那批人还是会掉。选了共享存储之后还要注意 Redis 本身的持久化——Redis 一重启会话全没,等于全体强制重新登录,如果这种情况不可接受,就要给 Redis 配持久化或者接受这个代价。

Cookie 的设计也有几个必须提前想清楚的参数。会话有效期(多长时间无论如何都要重新登录)和不活动超时(多长时间没操作就失效)是两个不同的值,混为一谈会导致要么太频繁被踢、要么长期不登出。默认策略建议会话有效期按天计、不活动超时按小时计。「记住我」是一个独立的、更长的 Cookie,它的时长要和上面说的安全策略对齐。

Cookie 的 domain 决定了能不能跨子域共享:设成 .example.com,那么 a.example.com 和 b.example.com 都能读到同一个会话,七个系统只需登录一次;设成不带前导点的单域名或者按子域隔离,那用户访问每个子系统都要重新登录一遍,统一登录就名存实亡了。代价是子域上任何一个 XSS 都能读到这个 Cookie,所以共享 domain 的前提是所有子域的应用都值得信任。

还有两个属性:Secure 要求 Cookie 只在 HTTPS 下发送——全站 HTTPS 的情况下应该一律开启,否则一旦有 HTTP 入口就会明文泄露;SameSite 影响跨站请求,设成 Lax 时从外站链接跳转过来的请求不会带 Cookie(用户会看到一次未登录),设成 None 必须同时配 Secure,跨站嵌入和被嵌入的场景才带得上。如果你有系统需要被 iframe 嵌进别的页面,或者要从外部站点发起带登录态的请求,SameSite 的取值会直接决定功能能不能用,这个要逐个应用试。

口令策略与 regulation 锁定:公网 IP 侧的误伤

暴力破解防护靠的是 Authelia 的 regulation 机制:连续失败多少次之后,对某个对象锁定一段时间,并且锁定时长可以随失败次数递增。可选的限制维度一般有按用户名和按 IP 两种,这是配置里最需要想清楚的地方。

按 IP 锁定的副作用在你有公网出口时被放大。如果整个办公室、整个机房出口共用少数几个公网 IP,一个用户连错五次,同出口的所有人都会一起被挡——而且是那种「怎么输都对,也没任何提示」的挡法,排查起来非常费时。更糟的情况是攻击者故意用你的用户名乱试,等于发动了一次针对全公司的拒绝服务。

所以我的建议是:以按用户名锁定为主,IP 维度作为补充且阈值放宽;同时确保 Authelia 能拿到真实客户端 IP——反代在转发子请求时要正确传递 X-Forwarded-For,并且要有可信代理(trusted proxies)的配置,否则要么拿到的是反代自己的 IP(所有人都共用一个,一锁全锁),要么被伪造的 X-Forwarded-For 绕过计数。这个配置在多层反代(CDN → 反代 → Authelia)时尤其容易错,必须逐层核对。

还有一个隐形依赖必须点名:SMTP。密码重置、邮箱验证、某些通知都依赖邮件通道。SMTP 配错或者被上游限流,表现不是「邮件慢一点」,而是「所有人的密码重置整体瘫痪」,而且用户端往往只看到「操作失败」。自建身份系统时,邮件通道和身份服务本身是同一优先级的关键依赖——要么用稳定的外部发信服务并做好退信监控,要么明确放弃自助重置、改走管理员人工通道。同理,如果启用了 WebAuthn 的邮件验证或者通知类功能,也要把它们算进这个依赖里。

ACL 规则怎么排:匹配顺序、默认策略与正则失控

访问控制规则(ACL)是这套方案里最容易「先跑起来、后收拾不了」的部分。规则由若干匹配条件(域名、路径、用户、组、客户端网络段、请求方法、是否带认证头)和一个策略(bypass / one_factor / two_factor / deny)组成,匹配时自上而下取第一条命中,顺序写错了结果就是反的。典型错误是把一条宽松的 one_factor 规则写在前面,后面给管理后台写的 two_factor 永远不生效。

默认策略(default policy)的选择决定了出错时的方向:默认拒绝意味着新加一个子域没配规则就登不上,安全但容易误伤,上线初期会频繁收到「某某系统打不开了」;默认放行意味着新加的子域直接裸奔,省事但迟早出事。务实的做法是默认给 two_factor(要求二次验证),把确实需要放行的(静态资源、健康检查、公开的文档站)单独写 bypass 规则,并且对 bypass 规则做定期复核。

网络段规则(比如内网 IP 段直接 bypass)非常有用,它能让机房内部、办公网内的访问不必每次输动态码,也是故障时的自救通道。但它的风险在于「内网」的定义会漂移——VPN 地址池改了、办公室换了出口、云上机器换了个内网网段,都会让规则失效或者意外生效。写这类规则时最好用明确的 CIDR,并写注释说明这段地址是什么,半年后你一定需要这个注释。

还有一条经验:不要试图用一条大正则覆盖所有路径。一开始写 ^/(api|admin|static|upload)/.* 这种能省几行,半年后新系统接入、路径命名不一致,你会发现这条正则的命中范围和你想的完全不一样,而且没人敢改——因为改一行可能影响七个系统。正确做法是按域名分组、每个子域几条独立规则,重复一点无所谓,可读性和可维护性重要得多。规则数量到几十条时,考虑拆成多个配置文件或者用锚点/引用机制,别让单文件膨胀到几千行。

跨境链路与机房位置:登录跳转为什么在海外节点更容易超时

自建统一登录的机器放哪,影响的不是功能,是体验和可用性。反代前置认证会给每个请求增加一次内部子请求,这个子请求通常是本机或内网调用,延迟极低,几乎无感;但登录跳转链路是跨公网的——浏览器要跳到认证域名、输密码、过二次验证、再跳回来,中间多次 302,每一跳的延迟都会被用户直接感知。

如果应用服务器在一个地区、认证服务在另一个地区,或者用户分布在多个地区,这条链路的抖动会被放大:链路丢包时登录页加载不全、二次验证页面卡住、回跳丢失参数导致登录成功后又回到登录页。判断方法是在用户侧抓一次登录全过程的时序,看每一跳的耗时和状态码,而不是只看「能不能登上去」。

实践上的取舍是:认证服务尽量和主要用户群放在同一个地区,退一步才考虑和应用服务器同机。应用服务器在中国香港、东南亚或美洲节点的团队,最好把认证服务也放在同一区域,避免登录时跨洲往返。时钟同步同样受位置影响——TOTP 依赖时间准确,多台机器之间如果时钟漂移超过容许窗口,动态码会持续校验失败,所以所有相关机器都要配 NTP 并定期检查偏移,跨地区部署时这一点尤其容易忽略。

自建身份系统的成本账:硬件之外的工时与依赖

硬件成本可以忽略:Authelia 本身很轻,一台小规格实例、甚至和现有应用共用一台机器的容器就能跑起来;Redis 和 PostgreSQL 也都可以是小规格。真正花钱的是工时和隐性依赖。

算一下隐形成本清单:配置文件与 ACL 的初始编写和后续维护;七个后端应用逐个开启「信任反代头」的改造;第二因素注册与恢复流程的推广(每个用户都要过一遍,还得处理各种客户端兼容问题);TLS 证书与子域的管理;SMTP 通道的对接与退信监控;备份策略的实施与定期恢复演练;以及故障时的排查成本——统一登录一旦出问题,七个系统同时不可用,影响面远大于单个应用挂掉。

换句话说,这套系统把「七个分散的小风险」换成了「一个集中的大风险」。这个交换在账号治理上是划算的(清账号只要清一处),在可用性上是不划算的(一处故障全网瘫痪)。所以必须配套做冗余和旁路,而不是单点跑起来就完事。服务器租用层面,把认证服务单独放在一台机器上、与数据库分离,成本增加有限,但故障隔离的效果明显。一万网络在交付这类多节点部署时,通常建议客户按「应用节点 + 认证节点 + 数据节点」分层规划,而不是把所有角色塞进一台机器后再做拆分。

备份优先级:密钥、加密盐与 WebAuthn 凭据

备份不是「把配置目录打包」这么笼统,不同东西丢了后果完全不同,优先级要分开排。

第一优先级是密钥材料:OIDC 的签名私钥、会话加密密钥、存储加密密钥。密钥泄露意味着攻击者可以自己签发合法的 id_token,等于拿到了整个身份体系的最高权限——他不需要猜任何人的密码,直接造一个身份就能登录所有系统。这类密钥必须离线保存一份、访问权限严格管控,一旦怀疑泄露就要立即轮换(轮换后所有会话失效,用户需要重新登录,这是预期内的代价)。

第二优先级是用户库:YAML 文件、SQL 数据库导出、或者 LDAP 的备份。丢了不会导致安全事件,但会导致所有人无法登录,且重建很痛苦。

第三优先级是 WebAuthn 凭据和第二因素配置。这里有个反直觉的点:WebAuthn 凭据是不能「备份迁移」的——它和安全硬件绑定,导出来也没法在另一台设备上用。所以丢凭据不等于丢数据,等于用户需要重新注册。备份它的意义在于保留「谁注册过什么」的记录,而不是恢复使用。真正要保证的是恢复码还在、恢复流程还能走通。

还有一类常被漏掉的:加密盐(salt)和哈希参数。如果换了盐或者改了哈希算法参数,旧的口令哈希就校验不过,表现为全体用户密码失效。升级配置时不要动这两个值,除非你准备好让所有人重置密码。备份之外,最重要的是恢复演练——每季度找一台机器完整恢复一次并验证能登录,只备份不验证等于没备份。

灰度、旁路与回滚:会话失效的连锁掉登录

上线策略决定了这次改造是「一次平滑切换」还是「一次事故」。推荐的顺序是:先选一个不敏感的子域灰度(比如内部 Wiki),只把它切到反代后面,保留原来的直连入口一段时间,观察登录、跳转、退出、超时这四类行为是否正常。

然后做保留旁路。最实用的一条是「内网 IP 段直放」的 ACL 规则:从办公网或机房内网访问时 bypass 认证,直接进应用。这条规则在故障时的价值极大——认证服务挂了、配置写错了、密钥轮换了导致全体掉登录,运维还能从内网进去改配置。代价是它同时是一条安全口子,所以要限制到明确的 CIDR,并且明确记录它的存在(很多团队上线半年后忘了自己留过这条规则)。

回滚要提前想清楚一件容易被忽略的事:Authelia 的会话失效会让所有应用同时掉登录。因为七个系统共用一套会话,一旦会话存储被清空、密钥被轮换、Cookie domain 被改动,用户会在同一时刻被踢出所有系统。如果这时候用户没有备用登录方式(比如应用原来的本地账号还在),就会出现大面积无法工作。所以迁移时不要立刻删除应用原有的本地账号,先禁用、观察一段时间再清;同时把切换安排在低峰时段,并提前通知所有人「可能会出现一次重新登录」。

回滚的具体动作也要预演:反代配置退回直连需要多久、DNS 或配置是否需要回退、应用侧「信任反代头」的开关是否需要关闭。把这些写成一页纸的操作步骤,比出事时现想可靠得多。

自建统一登录最容易踩的五个坑

坑一:应用端口直接暴露在公网。为什么:认证逻辑全在反代,应用只认头,绕过反代的请求没有任何校验。怎么判断:从外网直接访问应用原始端口(http://IP:端口),能打开就是中招;定期列出监听在 0.0.0.0 的端口逐个核对。怎么避:容器端口只映射 127.0.0.1,防火墙默认拒绝入站、只放行 80/443 与限制来源的管理端口,两层同时收敛。

坑二:多实例不共享会话,登录随机掉线。为什么:会话默认在内存,上游未做会话保持时请求落到没有该会话的实例上就被判未登录。怎么判断:刷新页面时好时坏、掉线和具体时间无关、重启某个实例后一批人同时掉线。怎么避:把会话存到 Redis 并让所有实例指向同一份;同时接受 Redis 重启会导致全体重新登录这个代价,或者给 Redis 配持久化。

坑三:SMTP 不可用导致密码重置整体瘫痪。为什么:重置、验证、通知都依赖邮件通道,它不是「锦上添花」而是关键依赖。怎么判断:功能上线前手工发一封测试邮件,并模拟一次完整重置流程;上线后监控退信。怎么避:优先用稳定的外部发信服务,配置退信告警;如果不用自助重置,就明确写进文档、走管理员人工通道,别让用户点了按钮却永远收不到信。

坑四:按 IP 做失败锁定,把整个办公室一起挡在门外。为什么:共用一个公网出口时,一个人的失败计数会累加到所有人的出口 IP 上,还会被攻击者利用成拒绝服务。怎么判断:出现「所有人都登不上、换浏览器也不行」且无明确提示。怎么避:以按用户名锁定为主、IP 维度阈值放宽;同时正确配置可信代理与 X-Forwarded-For,保证拿到真实客户端 IP。

坑五:ACL 规则顺序写反或越写越臃肿。为什么:规则自上而下取第一条命中,宽松规则在前会吃掉后面的严格规则;一条大正则覆盖多系统会因系统增加而失控。怎么判断:新加一条严格规则却不生效,或者改动一条规则影响多个不相关系统。怎么避:按子域分组写独立规则,把严格规则放前面,默认策略设为要求二次验证,bypass 规则单独登记并定期复核。

关于自建统一登录的八个高频疑问

问:Authelia 开了之后,应用里原来的本地账号要不要删掉?
不要立刻删。切换初期先把本地账号禁用而不是删除,保留一两周观察期。原因是统一登录一旦出问题(会话被清空、密钥轮换、Cookie 配置改动),七个系统会同时掉登录,如果应用侧没有可用的备用入口,就会出现大面积无法工作的局面。等观察期结束、确认认证链路稳定,再逐个清理本地账号。清理时要顺便核对一遍:这个系统里有没有以本地账号身份创建的数据、定时任务、API token,删账号可能连带影响这些东西。

问:反向代理前置认证到底拦不住哪些访问?
它只在 HTTP 请求经过反代时生效,管不住三类路径:一是应用直接监听、没有走反代的端口(容器映射到 0.0.0.0、防火墙临时放行、调试端口遗留);二是非 HTTP 协议,比如 SSH、数据库直连、Redis、消息中间件、不走 HTTP 网关的 gRPC;三是拿到有效会话之后的越权行为,认证只回答「你是谁」,不回答「你能看什么」,应用内部的权限判断它替代不了。所以上统一登录的同时要做端口收敛、协议侧的访问控制,以及应用内授权,这三件事是互补的、不能互相替代。

问:七个系统共用一次登录,Cookie 域名该怎么设?
把 Cookie 的 domain 设成父域并带前导点(例如 .example.com),这样 a.example.com、b.example.com 都能读到同一个会话,用户只需登录一次;认证服务本身也要放在这个父域下的某个子域。如果设成单域名或不共享的子域,每个系统都要单独登录,统一登录就失去意义。代价是子域上任何一处 XSS 都可能读到这个 Cookie,所以前提是这些子域的应用都值得信任,并且全站启用 HTTPS 配 Secure 属性。跨站场景还要实测 SameSite 的取值,Lax 下从外站跳转过来的首次请求不带 Cookie。

问:SQLite 和 PostgreSQL 该怎么选?
单机单实例、用户数在十几人以内、不需要冗余,SQLite 就够了:零依赖、备份就是拷文件、出问题容易排查。它的短板是写操作会锁库、多实例无法共享,一旦你要起第二个 Authelia 实例就必须换。PostgreSQL 适合需要多实例共享状态、用户数较多、或者希望支持自助改密与密码重置流程的场景,代价是要多维护一个数据库:备份、连接数、版本升级、磁盘增长都要纳入运维。判断标准很简单——未来半年内有没有做双实例的计划,有就直接上 PostgreSQL,迁移一次比一开始选对更费事。

问:TOTP 动态码一直提示错误,可能是什么原因?
最常见的是时钟问题:TOTP 对时间敏感,服务器时钟漂移超过容许窗口就会持续校验失败,多机部署尤其容易中招。先检查所有相关机器的 NTP 是否正常运行、时区是否正确、与标准时间的偏差有多大。下一步检查是不是用了「记住此设备」导致实际没有走二次验证流程,或者反过来——策略要求是每次都要验证而用户以为已经记住了。还有一种情况是用户注册时扫码之后没有确认激活,或者同时注册了多个同名的条目选错了。排查时让用户在认证服务侧重新校准或重新注册一次,比反复检查服务器配置更快。

问:后端应用只认 Remote-User 头,怎么防止被伪造?
关键是让后端只接受来自反代的请求:容器端口只映射到 127.0.0.1,防火墙拒绝外部直连应用端口,必要时用本机 Unix socket 或容器内部网络通信。反代侧在转发前必须先清除客户端自带的同名头(Remote-User、Remote-Groups、Remote-Email 等),只注入自己生成的值,否则外部请求可以自己带一个头进来冒充管理员。同时要确认后端确实开启了「信任反代头」且只信任指定的上游地址。定期从外网直连应用端口做验证,是最直接的检查手段。

问:想做双实例高可用,哪些状态必须共享?
至少三样。一是会话存储,必须放到 Redis 这类共享存储里,否则上游没做会话保持时用户会在实例间被反复踢下线;二是用户存储,YAML 文件多实例会不一致,要用 SQL 或 LDAP;三是密钥与配置,两个实例必须用同一份 OIDC 签名密钥、会话加密密钥和存储加密密钥,否则 id_token 校验会互相失败。时钟也要一致(NTP),TOTP 对时间敏感。还有一点容易被忽略:第二因素相关记录、regulation 的失败计数如果存本地,也会出现「在 A 上被锁、在 B 上还能试」的绕过,这类状态也要放到共享存储。

问:密码重置邮件发不出去会发生什么,怎么预防?
表现不是「慢一点」,而是自助重置功能整体不可用,用户点了按钮却永远收不到信,而且往往没有明确报错,会被当成系统故障反复报障。更麻烦的是注册第二因素、邮箱验证这类流程也可能依赖同一条通道。预防办法:上线前手工完成一次完整的重置流程并确认收到邮件;使用稳定的外部发信服务;配置退信监控与告警;把发信通道和身份服务放在同一优先级做可用性管理。如果确实无法提供可靠的邮件通道,就明确放弃自助重置,改由管理员通过线下确认后代为重置,并把这个流程写进文档告诉用户,别留一个点了没反应的按钮。

一万网络机器上自建统一登录的落地顺序

把上面的内容收拢成一条可执行的顺序,按这个走能少踩一半的坑。第一步:盘资产——列清所有要接入的系统、它们各自有没有原生 OIDC 支持、监听在哪些端口、有没有非 HTTP 入口。能原生接 OIDC 的划到 OIDC 路线,剩下的走反向代理。

第二步:定域名与证书——给认证服务选一个父域下的子域,规划好 Cookie 的共享 domain,准备好通配符或多域名证书,全站 HTTPS。这一步决定了后面所有跳转和 Cookie 的行为,别到后面再改。

第三步:选存储并先小规模跑通——三五人的团队用 YAML 就够了;有域控就对接 LDAP;打算做多实例就直接用 PostgreSQL 配 Redis。先在测试子域上把 forward-auth 跑通,确认子请求带了正确的 Host 和路径、回跳地址正确、注入头生效。

第四步:收敛端口——把应用端口全部改到 127.0.0.1,防火墙默认拒绝入站,只放行 80/443 与限源的管理端口。这一步要在切流量之前做完,否则中间有一段裸奔期。

第五步:配置 ACL 与第二因素策略——按子域分组写规则,默认策略设为要求二次验证,单独登记 bypass 规则;管理员组强制 WebAuthn、不给记住设备,普通成员 TOTP 加恢复码。

第六步:灰度与旁路——先切一个不敏感系统,保留内网 IP 段直放作为自救通道,保留应用本地账号一两周观察期,切换安排在低峰并提前通知。

第七步:备份与演练——密钥材料离线保存并管控访问,用户库定期导出,恢复流程写成文档并每季度演练一次完整恢复。同时给所有相关机器配 NTP 并定期检查时钟偏移。

在一万网络的租用环境里做这套部署,机器地域的选择建议跟着主要用户群走:用户集中在内地就把认证节点放在内地机房,用户分布在海外或中国香港就同区域部署,避免登录跳转跨洲往返带来的抖动与超时。认证服务与数据库单独分层部署的成本增加有限,但能把「一处故障全网瘫痪」的风险压下去,这笔钱值得花。

数据来源

Authelia 官方文档(docs.authelia.com):项目定位、配置结构、authentication backend(文件 / LDAP / SQL)、session 配置、access control 规则、forward-auth 集成与 OIDC Provider 相关章节。

OpenID Connect Core 1.0 规范(OpenID Foundation):授权码流程、id_token 结构与校验、issuer 与 discovery 的语义,用于判断应用原生接入时的配置项正确性。

WebAuthn / FIDO2 规范(W3C 与 FIDO Alliance):凭据与来源域名绑定的机制、平台型与漫游型(硬件 Key)凭据的区别、抗钓鱼的原理。

RFC 6238(TOTP 一次性口令算法,IETF):时间步长与容许窗口的定义,用于理解时钟同步对动态码校验的影响。

Nginx ngx_http_auth_request_module 官方文档:auth_request 子请求的行为、401 处理与错误页配置。

Traefik ForwardAuth 中间件文档:forward 认证的请求转发方式、响应头透传与 authResponseHeaders 配置。

OAuth 2.1 安全最佳实践与 PKCE(IETF 相关草案与 OAuth 2.0 Security Best Current Practice):授权码拦截防护、重定向 URI 校验原则,用于配置 OIDC 客户端时的安全取舍。

配置思路、部署顺序与避坑判断均为典型部署场景下的工程实践归纳,不涉及任何第三方平台的未授权访问;文中价格与机型信息以官网实时信息或咨询确认为准。


上一篇:北极海缆为什么被反复提起:亚欧延迟能降多少,和你现在能买到什么

下一篇:2026 服务器租用 Helm 制品库搭建测评:ChartMuseum 版本治理与私有仓库同步的干货手册