- 后端
- 前端
- 运维
- MCP 服务
【免费下载链接】nginx-ui
Yet another WebUI for Nginx
本文以 Nginx UI 配置文件的[auth]段为核心,系统讲解从 v2.0.0-beta.26 起引入的授权选项:IP 白名单(IPWhiteList)与登录失败封禁(BanThresholdMinutes、MaxAttempts)。结合仓库源码,你将理解这三项配置的生效链路、边界行为(本机回环豁免、代理场景、IPv6)、失败计数与解封机制,并掌握通过配置文件与NGINX_UI_AUTH_*环境变量两种方式完成安全加固的完整方案。
一、[auth]配置段概览
从 v2.0.0-beta.26 版本开始,Nginx UI 支持在配置文件的auth段设置授权选项。该段对应的 Go 结构体定义在 settings/auth.go,源码结构如下:
type Auth struct { IPWhiteList []string `json:"ip_white_list" binding:"omitempty,dive,ip|redacted" ini:",,allowshadow" protected:"true"` TrustedProxies []string `json:"trusted_proxies" binding:"omitempty,dive,ip|cidr|redacted" ini:",,allowshadow" protected:"true"` BanThresholdMinutes int `json:"ban_threshold_minutes" binding:"min=1"` MaxAttempts int `json:"max_attempts" binding:"min=1"` SecureSessionTimeoutMinutes int `json:"secure_session_timeout_minutes" binding:"min=1"` }从源码可以看出,[auth]段不仅包含文档中列出的三项,还包含TrustedProxies(受信代理列表)与SecureSessionTimeoutMinutes(安全会话超时时间,默认值 10 分钟,定义于同一文件的DefaultSecureSessionTimeoutMinutes常量)两个相邻安全选项。它们共同构成 Nginx UI 的认证入口防护体系。其中:
IPWhiteList为字符串数组,支持重复键声明(ini:",,allowshadow"表示同名键可多次出现,每次追加一个元素),这正是配置示例中多次书写IPWhiteList的语法依据;BanThresholdMinutes、MaxAttempts均要求最小值 1(binding:"min=1"),不允许配置为 0 或负数。
该配置段在 settings/settings.go 中注册为auth节(sections.Set("auth", AuthSettings)),并可通过环境变量覆盖(见后文第五节)。
二、IPWhiteList:访问来源 IP 白名单
2.1 配置语法
- 类型:
string(可重复声明,形成白名单列表) - 示例:
10.0.0.1 - 支持 IPv4 与 IPv6(如
2001:0000:130F:0000:0000:09C0:876A:130B)
[auth] IPWhiteList = 10.0.0.1 IPWhiteList = 10.0.0.2 IPWhiteList = 2001:0000:130F:0000:0000:09C0:876A:130B2.2 行为规则与回环豁免
默认情况下,如果没有设置IPWhiteList,所有 IP 地址都允许访问 Nginx UI。一旦设置了白名单:
- 只有白名单内的 IP 与
127.0.0.1可以访问 Nginx UI; - 其余来源将收到
403 Forbidden错误; - 白名单为空时视为未启用,放行所有 IP。
该逻辑实现在 internal/middleware/ip_whitelist.go,核心代码如下:
func IPWhiteList() gin.HandlerFunc { return func(c *gin.Context) { clientIP := c.ClientIP() if len(settings.AuthSettings.IPWhiteList) == 0 || clientIP == "127.0.0.1" || clientIP == "::1" { c.Next() return } if !lo.Contains(settings.AuthSettings.IPWhiteList, clientIP) { c.AbortWithStatus(http.StatusForbidden) return } c.Next() } }值得注意的细节:
- 回环豁免同时覆盖 IPv4 与 IPv6:代码同时判断了
127.0.0.1与::1。换言之,本机访问(无论走 IPv4 还是 IPv6 回环地址)始终被放行,即使它们未出现在白名单中。文档只提到127.0.0.1,源码证明::1同样享有豁免——这在启用 IPv6 的本机环境中非常重要。 - 白名单精确匹配:使用
lo.Contains进行列表精确匹配,不会做 CIDR 网段匹配。如果需要放行整个网段,必须在[auth]段逐一列出该网段内的每个 IP(或结合反向代理统一出口 IP 的策略)。若需要 CIDR 能力,可关注TrustedProxies字段(其 binding 校验为ip|cidr),它用于声明可信的反向代理来源,从而让c.ClientIP()正确解析经代理转发后的真实客户端 IP。
2.3 白名单中间件的挂载位置
IPWhiteList中间件在 router/routers.go 中被挂载到 API 路由组根节点上:
root := r.Group("/api", middleware.IPWhiteList())这意味着白名单约束作用于整个/api前缀下的全部接口——无论是登录接口还是业务接口,均受其管辖。这是访问控制的第一道闸门,在认证(登录鉴权)之前生效。
2.4 测试用例对行为边界的印证
internal/middleware/ip_whitelist_test.go 中的测试精确刻画了白名单的边界行为,值得运维人员关注:
- 代理场景:当请求来自受信代理(
trustedProxies=["127.0.0.1"])且X-Forwarded-For头携带198.51.100.20时,白名单只放行198.51.100.20,而放行203.0.113.20则返回 403 —— 说明白名单判定的是经ClientIP()解析后的"真实客户端 IP",而非 TCP 对端地址; - 伪造头防护:当不存在受信代理时,客户端直接伪造
X-Forwarded-For: 203.0.113.20也无法绕过白名单(返回 403),即伪造的转发头不会被信任; - 异常输入失败关闭:当远程地址无法解析为合法 IP 时,白名单判定为拒绝(403),遵循"失败关闭"(fail-closed)的安全原则;
- 回环兼容:
127.0.0.1与[::1]在未配置受信代理的情况下直接放行(204 No Content)。
因此,如果你在反向代理(Nginx、Caddy 等)后面部署 Nginx UI,请务必同时配置TrustedProxies指向代理服务器地址,否则ClientIP()取到的是代理地址,白名单可能无法按预期匹配真实客户端 IP。
三、登录失败封禁机制:BanThresholdMinutes 与 MaxAttempts
3.1 参数说明
| 参数 | 类型 | 默认值 | 语义 |
|---|---|---|---|
BanThresholdMinutes | int | 10 | 失败计数与封禁的有效时间窗口(分钟) |
MaxAttempts | int | 10 | 触发封禁所需的累计失败次数阈值 |
默认行为:如果用户在10 分钟内登录失败10 次,该用户来源 IP 将被禁止登录10 分钟。
源码层面的默认值定义于 settings/auth.go 的初始化逻辑:
var AuthSettings = &Auth{ BanThresholdMinutes: 10, MaxAttempts: 10, SecureSessionTimeoutMinutes: DefaultSecureSessionTimeoutMinutes, }同时在 settings/settings.go 的Init中做了一次兜底校验:若配置值小于等于 0,则回退为默认值 10:
if AuthSettings.BanThresholdMinutes <= 0 { AuthSettings.BanThresholdMinutes = 10 } if AuthSettings.MaxAttempts <= 0 { AuthSettings.MaxAttempts = 10 }注意:这里只对<= 0的情况兜底,而结构体 binding 校验要求min=1,因此合法取值区间为1及以上的正整数。若需更严苛的安全策略,可将两者调低(如BanThresholdMinutes = 5、MaxAttempts = 3),即"5 分钟内失败 3 次即封禁 5 分钟"。
3.2 计数与封禁的实现链路
封禁记录使用数据库表ban_ips(对应模型 model/ban_ip.go),通过 query/ban_ips.gen.go 生成的 GORM 查询对象操作。核心逻辑在 internal/user/login.go:
func BanIP(ip string) { b := query.BanIP banIP, err := b.Where(b.IP.Eq(ip)).First() if err != nil || banIP.ExpiredAt <= time.Now().Unix() { _ = b.Create(&model.BanIP{ IP: ip, Attempts: 1, ExpiredAt: time.Now().Unix() + int64(settings.AuthSettings.BanThresholdMinutes*60), }) return } _, _ = b.Where(b.IP.Eq(ip)).UpdateSimple(b.Attempts.Add(1)) }其工作机制可归纳为:
- 按 IP 查询封禁记录;
- 若不存在该记录,或记录已过期(
ExpiredAt <= now),则新建一条记录:Attempts = 1,过期时间 = 当前时间 +BanThresholdMinutes分钟(换算为秒); - 若记录仍有效,则将
Attempts累加 1。
在 api/user/auth.go 的Login处理器中,登录失败(密码错误、2FA 缺失或校验失败等)都会调用user.BanIP(clientIP)。而在登录入口处,会先做封禁检查:
banIP, _ := b.Where(b.IP.Eq(clientIP), b.ExpiredAt.Gte(time.Now().Unix()), b.Attempts.Gte(settings.AuthSettings.MaxAttempts), ).Count() if banIP > 0 { c.JSON(http.StatusTooManyRequests, LoginResponse{ Message: "Max attempts", Code: ErrMaxAttempts, // 4291 }) return }即:当某 IP 存在"未过期"且"失败次数 ≥ MaxAttempts"的记录时,登录请求直接返回429 Too Many Requests(业务错误码4291,常量ErrMaxAttempts)。登录成功后,该 IP 的封禁记录会被清除:
// login success, clear banned record _, _ = b.Where(b.IP.Eq(clientIP)).Delete()3.3 封禁的粒度与观察维度
- 按 IP 独立计数:封禁记录以 IP 为键,不同来源 IP 的失败计数互不影响。这一行为由 internal/user/login_ban_test.go 中的
TestBanIPKeepsDifferentClientBucketsSeparate测试直接验证:对198.51.100.10与203.0.113.20分别调用BanIP后,两条记录的Attempts各自独立(分别为 2 和 1),互不干扰。 - 窗口滑动特性:一旦超过
BanThresholdMinutes,旧记录即视为过期,下一次失败会重建记录并从 1 重新计数。因此不存在"永久封禁"——封禁是周期性的。 - 额外的人为延迟:登录失败后,服务端会追加一个 0~10 秒的随机休眠(
time.Sleep(random * time.Second),见 api/user/auth.go),进一步抬高暴力破解的时间成本。
3.4 被封禁 IP 的管理接口
api/settings/auth.go 提供了两个管理接口:
GET查询当前封禁的 IP 列表(GetBanLoginIP):先清理已过期的封禁记录,再返回满足ExpiredAt >= now && Attempts >= MaxAttempts的记录;- 删除指定被封禁的 IP(
RemoveBannedIP):管理员可手动解除误封。
即当某个合法用户因多次输错密码被临时封禁时,管理员无需等待BanThresholdMinutes到期,可以通过管理接口立即解除封禁。
四、完整配置示例与生效方式
4.1 配置文件示例
将上述配置组合到 Nginx UI 的配置文件(默认为app.ini,具体路径与加载方式见 config-app.md):
[auth] # 仅允许以下 IP 访问(可重复声明),未配置时放行所有 IP IPWhiteList = 10.0.0.1 IPWhiteList = 10.0.0.2 IPWhiteList = 2001:0000:130F:0000:0000:09C0:876A:130B # 5 分钟内累计失败 5 次,则封禁该 IP 5 分钟 BanThresholdMinutes = 5 MaxAttempts = 5 # 可选:反向代理场景下声明受信代理,保证 ClientIP() 取到真实客户端地址 TrustedProxies = 127.0.0.1配置保存后,服务重启或通过管理端保存设置时,settings.Save()会将结构体回写到 ini 文件并重新加载(settings.Reload()),实现热更新。
4.2 环境变量方式(Docker / systemd 部署)
由于 settings/settings.go 中envPrefixMap将AUTH映射到AuthSettings,且环境变量前缀为NGINX_UI_,因此 docs/zh_CN/guide/env.md 列出的对应关系如下:
| 配置项 | 环境变量 |
|---|---|
IPWhiteList | NGINX_UI_AUTH_IP_WHITE_LIST |
BanThresholdMinutes | NGINX_UI_AUTH_BAN_THRESHOLD_MINUTES |
MaxAttempts | NGINX_UI_AUTH_MAX_ATTEMPTS |
Docker 部署示例:
docker run -d \ -e NGINX_UI_AUTH_IP_WHITE_LIST=10.0.0.1 \ -e NGINX_UI_AUTH_BAN_THRESHOLD_MINUTES=5 \ -e NGINX_UI_AUTH_MAX_ATTEMPTS=5 \ -p 9000:9000 \ uozi/nginx-ui:latest提示:
IPWhiteList为数组类型,若需多个 IP,请参考 docs/zh_CN/guide/env.md 中数组类配置的写法(逗号分隔或重复声明,视版本而定)。
五、安全建议与注意事项
- 白名单与反向代理:
IPWhiteList判定的是c.ClientIP()的解析结果。在 Nginx/Caddy 反向代理之后部署时,务必配置TrustedProxies指向代理地址,否则所有请求都会显示为代理 IP,白名单要么全部放行、要么全部拒绝。这一点已由ip_whitelist_test.go中的代理与伪造头用例明确验证。 - 回环豁免是设计行为:
127.0.0.1与::1始终可访问,即使未列入白名单。这意味着"限制来源"无法阻止本机进程访问,请确保运行 Nginx UI 的主机本身是可信的。 - 封禁是 IP 粒度的临时策略:它是暴力破解的减速带,而非访问控制手段。对于高安全要求的部署,应结合强密码、2FA(见 api/user/auth.go 中的 OTP/Passkey 登录链路)与防火墙规则共同使用。
- 调参权衡:
MaxAttempts过小、BanThresholdMinutes过大,可能造成合法用户因输入错误被长时间拒之门外;反之则防护力度不足。建议按实际部署环境调整,并借助封禁列表管理接口及时处理误封。 - 配置边界:
BanThresholdMinutes与MaxAttempts必须为不小于 1 的整数,<= 0的值会在初始化时被强制回退为 10。
六、小结
[auth]段的三项核心配置为 Nginx UI 提供了两道基础防线:IPWhiteList在 API 路由最外层按来源 IP 进行访问控制(支持 IPv4/IPv6、回环豁免、失败关闭);BanThresholdMinutes与MaxAttempts则在登录入口按 IP 累计失败次数并实施周期性封禁(429 Too Many Requests),配合封禁管理接口可随时解除误封。结合TrustedProxies、环境变量注入与源码级行为边界(代理解析、IPv6 回环、独立计数桶),你可以在 Docker、systemd 与反向代理等各类部署形态下完成一次完整、可验证的登录安全加固。
如需进一步了解配置文件整体结构与环境变量全量清单,可参阅 config-app.md 与 env.md。
- 后端
- 前端
- 运维
- MCP 服务
【免费下载链接】nginx-ui
Yet another WebUI for Nginx
相关推荐
Nginx UI Auth 配置指南:IP 白名单、可信代理与登录安全策略
Nginx UI Auth 配置指南:IP 白名单、可信代理与登录安全策略 本指南系统讲解 Nginx UI 从 v2.0.0 beta.26 起引入的 aut
后端前端运维MCP 服务Nginx访问控制终极指南:快速配置IP白名单与Basic Auth认证
Nginx访问控制终极指南:快速配置IP白名单与Basic Auth认证 Nginx作为高性能的Web服务器和反向代理,其访问控制功能是保护网站安全的重要手段。
文档教程技术博客Nginx UI 認證設定完整指南:IP 白名單、失敗封鎖與安全會話時長配置解析
Nginx UI 認證設定完整指南:IP 白名單、失敗封鎖與安全會話時長配置解析 Nginx UI 自 v2.0.0 beta.26 起,可以在設定檔的 aut
后端前端运维MCP 服务
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考