Frigate 认证机制全解JWT 会话、登录限流、代理授权与基于角色的访问控制【免费下载链接】frigateNVR with realtime local object detection for IP cameras项目地址: https://gitcode.com/GitHub_Trending/fr/frigateFrigate 作为面向 IP 摄像头的实时本地物体检测 NVR其 Web UI 与 REST API 的访问安全由一套完整的认证体系保障用户凭据以 PBKDF2-SHA256 哈希存储在 SQLite 数据库中登录后签发 JWT 令牌并写入 Cookie同时支持通过上游认证代理Authelia、Authentik、oauth2_proxy 等以请求头方式接入企业身份体系。读完本文你将掌握 Frigate 双端口认证模型、auth与proxy两套配置项的完整用法、JWT 密钥管理策略、登录暴力破解防护SlowApi 限流 可信代理 CIDR以及 admin / viewer / 自定义角色三类角色的授权边界与源码级实现依据。认证体系总览双端口模型与 JWT 会话Frigate 将用户信息存储在数据库SQLite中密码哈希采用行业标准的 PBKDF2-SHA256 算法迭代次数为 600,000 次。登录成功后系统签发一枚带过期时间的 JWT 令牌并写入 CookieCookie 会在临期时自动刷新该 JWT 也可以作为 Bearer 令牌放在Authorization请求头中使用。用户的管理入口在 UI 的Settings Users页面。从源码看这一描述与 frigate/api/auth.py 中的实现完全一致hash_password()使用hashlib.pbkdf2_hmac(sha256, ...)配合 600,000 次迭代生成哈希并保存为pbkdf2_sha256$iterations$salt$b64_hash四段式字符串算法名常量定义在 frigate/const.py 的PASSWORD_HASH_ALGORITHM。JWT 的构造见 create_encoded_jwt采用HS256签名算法claims 中包含sub用户名、role、exp过期时间戳与iat签发时间戳。Frigate 提供两个端口访问 Web UI二者在认证语义上截然不同端口说明8971经过认证的 UI 和 API。反向代理应当使用此端口。5000内部免认证 UI 和 API 访问。对该端口的访问应严格受限。其设计用途是 Docker 网络内部与 Frigate 集成、但不支持认证的服务直接调用。源码中这一双端口模型在 /auth 端点 中清晰可见请求携带的x-server-port头由 Frigate 自带的 nginx 设置无法被外部伪造若等于内部端口则直接以remote-user: anonymous、remote-role: admin通过认证——这正是“5000 端口内部匿名访问等同 admin”的实现来源。而外部请求则必须走 JWT 或代理头校验流程。Frigate 原生并不实现 OIDC、SAML 或 LDAP 协议作为一个专注于录像与物体检测的 NVR它依赖久经考验的认证代理处理这些协议并通过请求头传递已认证的用户名和角色。Onboarding首次启动与管理员账号Frigate 启动时会检测数据库中是否存在用户。若不存在且认证已启用则自动生成一个 admin 用户和密码并以醒目横幅打印在日志中。建议首次登录后在Settings Users下为 admin 账号设置新密码。从 frigate/app.py 的启动逻辑看无用户时生成的密码写入数据库哈希存储日志打印User: admin与Password: ...同时置位admin_first_time_login标志使登录页面展示首次登录帮助链接对应GET /auth/first_time_login端点frigate/api/auth.py密码重置时同样使用secrets.token_hex(16)生成新密码并打印Reset admin password set in the config横幅。重置管理员密码如果实例被锁定忘记 admin 密码可以指示 Frigate 在下次启动时重置 admin 密码并打印到日志。UI 方式进入Settings System Authentication将Reset admin password设为 on。YAML 方式auth: reset_admin_password: true该字段的定义见 frigate/config/auth.pyreset_admin_password: bool默认False语义为“启动时重置 admin 用户密码并在日志中打印新密码”。注意这是启动期一次性行为——重启完成、日志打印新密码后应尽快登录修改密码并将该配置恢复为false。密码策略构造安全密码并妥善管理它非常重要。Frigate 要求密码最小长度为 12 个字符。源码中的校验函数 validate_password_strength 直接引用了 NIST SP 800-63B 标准核心思路是“更长的密码比更短但复杂的密码更难破解”。创建用户POST /users与修改密码PUT /users/{username}/password都会先经过该校验不满足时返回 400 及明确错误信息例如 Password must be at least 12 characters long。关于密码规范的进一步参考NIST SP 800-63B 标准以及社区关于“什么样的密码才真正安全”的讨论文章原文档给出外部链接此处不再列出可搜索 NIST SP 800-63B 获取原文。登录失败限流SlowApi 与可信代理为限制暴力破解攻击的风险Frigate 为登录失败提供限流能力底层使用 SlowApi 实现。合法取值的字符串写法遵循 limits 库的 quickstart 语法分号分隔多条规则。例如配置1/second;5/minute;20/hour后当登录失败次数超过以下阈值时登录端点将被限流每秒 1 次每分 5 次每小时 20 次重启 Frigate 会重置所有限流状态SlowApi 使用内存存储进程重启即清零。若 Frigate 运行在代理之后必须配置trusted_proxies否则限流将作用于上游代理的 IP——这意味着一次暴力破解会连带限流其他设备的正常登录尝试可能把你暂时锁在实例之外。配置trusted_proxies后Frigate 依据X-Forwarded-For头解析真实客户端 IP列出你愿意信任的上游网络CIDR限流即只针对请求的真实来源 IP。源码层面get_remote_addr 实现了这套 IP 解析逻辑读取x-forwarded-for头按逗号拆分为路由链并反转最右侧为最接近客户端的跳若链路上没有代理信息回退到直接 TCP 对端地址逐个检查路由中的 IP凡命中trusted_proxies中任一 CIDR区分 IPv4/IPv6IPv4-mapped IPv6 会被展开比较的地址视为“可信跳”继续向左走返回第一个不可信的地址作为客户端 IP若整条链全部可信则回退到直接对端地址。限流器实例化在 frigate/api/auth.pylimiter Limiter(key_funcget_remote_addr)通过limiter.limit(limit_valuerateLimiter.get_limit)装饰在POST /login上限制值来自运行时可更新的rateLimiter由failed_login_rate_limit配置填充。从源码结构看同一限流器同样装饰在PUT /users/{username}/password端点上frigate/api/auth.py因此密码修改失败也受相同规则约束。反向代理场景的完整配置示例如果你的反向代理与 Frigate 运行在同一个 Docker Compose 文件中配置如下UI 方式进入Settings System Authentication设置以下字段字段说明Failed login limits登录失败限流字符串如1/second;5/minute;20/hourTrusted proxies用于信任X-Forwarded-For的上游网络 CIDR 列表如 Docker Compose 内部网络的172.18.0.0/16YAML 方式auth: failed_login_rate_limit: 1/second;5/minute;20/hour trusted_proxies: - 172.18.0.0/16 # ---- 这是 Docker Compose 内部网络的子网对应配置字段定义在 frigate/config/auth.pyfailed_login_rate_limit: str | None默认None即不限流与trusted_proxies: list[str]默认空列表。会话时长Session LengthFrigate 用户认证会话的默认时长为24 小时86400 秒。该设置决定已认证会话在需要刷新令牌之前保持有效的时间——否则用户需要重新登录。默认值在安全性与便利性之间取得平衡你可以按自身安全要求调整单位是秒。几个示例取值86400默认24 小时后认证会话过期0会话时长设为 0 时用户每次访问应用或经过极短的即时超时后都需要重新登录6048007 天内令牌未被刷新则要求重新登录。源码提示当前仓库中session_length字段带有ge60的校验约束frigate/config/auth.py即配置值需不小于 60 秒才会通过校验“0”是文档给出的语义示例实际配置时以仓库当前 schema 校验为准。UI 方式Settings System Authentication中的Session length字段单位为秒默认 8640024 小时。YAML 方式auth: session_length: 86400与session_length配套的是刷新窗口refresh_time默认 1800 秒最小 30 秒当 Cookie 中的 JWT 距离过期不足该窗口时访问/auth端点会签发明文新 JWT 并写回 Cookie把有效期重新拉满到session_lengthfrigate/api/auth.py。若刷新时发现用户密码在令牌签发之后被修改过password_changed_at时间戳比对刷新会被拒绝强制重新登录——这就是“修改密码会使旧令牌失效”的底层机制。auth: session_length: 86400 # 会话总时长秒 # refresh_time: 1800 # 临期刷新窗口秒默认 1800JWT 令牌密钥JWT Token SecretJWT 密钥必须妥善保管任何持有该密钥的人都可以生成有效的 JWT 令牌来认证访问 Frigate。它应当是长度至少 64 字符的密码学随机字符串。可用 Python secrets 模块生成python3 -c import secrets; print(secrets.token_hex(64))Frigate 按以下顺序查找 JWT 密钥实现见 get_jwt_secret名为FRIGATE_JWT_SECRET的环境变量常量JWT_SECRET_ENV_VAR定义在 frigate/const.pyCREDENTIALS_DIRECTORY环境变量指定目录下的FRIGATE_JWT_SECRET文件默认为 Docker Secrets 目录/run/secrets/源码中固定读取/run/secrets/FRIGATE_JWT_SECRETHome Assistant Add-on 选项文件/data/options.json中的jwt_secret选项配置目录下的.jwt_secret文件。若启动时四处均未找到密钥Frigate 会生成一个 128 字符的十六进制随机密钥secrets.token_hex(64)并以0600权限写入配置目录的.jwt_secret文件写入失败如只读挂载时每次启动都会重新生成——这会导致所有已签发令牌在重启后全部失效。另外源码对密钥长度有检查不足 64 字符会打印 JWT Secret is recommended to be 64 characters or more 的告警日志。更换密钥会使当前所有令牌立即失效旧密钥签发的 JWT 无法通过新密钥验签。因此生产环境中建议通过环境变量或 Docker Secret 显式提供密钥并保证配置目录/密钥文件的持久化。反向代理集成复用 Authelia / Authentik / oauth2_proxy / traefik-forward-authFrigate 可以配置为利用常见上游认证代理的能力。由于 Frigate 不原生实现 OIDC、SAML 或 LDAP这类代理负责协议层面的身份认证Frigate 则通过请求头接收“已认证用户名 角色”。如果你正在依赖上游代理的认证通常应当禁用 Frigate 自身的认证——因为 Frigate 数据库中的用户与代理认证的用户之间没有对应关系。此外如果反向代理与 Frigate 之间的通信处于不受信任的网络中应设置proxy.auth_secret并要求代理发送名为X-Proxy-Secret的请求头携带该密钥同时建议配置真实的 TLS 证书参见 TLS 配置文档防止流量被嗅探窃取密钥。要禁用 Frigate 认证并确保请求只来自你的代理UI 方式Settings System Authentication将Enable authentication设为 offSettings System Proxy将Proxy secret设为some random long string。YAML 方式auth: enabled: False proxy: auth_secret: some random long string生成随机密钥的命令与上文相同python3 -c import secrets; print(secrets.token_hex(64))源码中的校验位于 /auth 端点当proxy.auth_secret已配置而X-Proxy-Secret头与之不符时直接返回 401。auth_secret在配置模型中声明为EnvString类型frigate/config/proxy.py支持用环境变量注入敏感值避免明文出现在配置文件中。Header 映射header_map禁用 Frigate 认证后若你的代理支持传递认证用户名和/或角色头可以用header_map指定头名供 Frigate 读取。例如映射X-Forwarded-User与X-Forwarded-Groups。头名不区分大小写角色头可包含多个值Frigate 预期以逗号分隔但可用separator配置项自定义分隔符。UI 方式进入Settings System Proxy配置头映射与分隔符字段说明Separator character角色头中分隔多个角色的字符默认逗号。Authentik 使用竖线\|。Header mapping User header认证用户名所在的头名如x-forwarded-userHeader mapping Role header认证角色/组所在的头名如x-forwarded-groupsYAML 方式proxy: ... separator: | # 此值默认为逗号例如 Authentik 使用竖线。 header_map: user: x-forwarded-user role: x-forwarded-groups配置模型见 frigate/config/proxy.pyHeaderMappingConfig包含user、role头名默认均为 None与可选的role_mapseparator默认,且校验器强制其为恰好一个字符。Frigate 支持admin、viewer及自定义角色见下文。经由8971端口时Frigate 会校验这些头后续请求使用remote-user与remote-role头进行授权。默认角色default_role可以为代理认证用户指定一个默认角色映射的role头中的任何值都会覆盖该默认值。UI 方式Settings System Proxy的Default role字段——“当不存在角色头时的回退角色如viewer”。YAML 方式proxy: ... default_role: viewer配置定义frigate/config/proxy.py中default_role默认值就是viewer。源码中的角色解析函数 resolve_role 明确了完整决策链配置了header_map.role头且请求携带其值若配置了role_map把该值视为组声明按separator拆分做映射命中admin时短路返回admin未配置role_map时把该值视为角色名本身转小写后与配置角色求交集未找到有效角色时返回经验证的default_role若default_role不在有效角色集合中回退为viewer。角色映射role_map在某些环境中上游身份提供方OIDC、SAML、LDAP 等不会直接传递 Frigate 兼容的角色名而是传递一个或多个组声明。为此 Frigate 支持role_map把上游组名翻译成 Frigate 内部角色admin、viewer或自定义角色。该配置通过 YAML 配置proxy: ... header_map: user: x-forwarded-user role: x-forwarded-groups role_map: admin: - sysadmins - access-level-security viewer: - camera-viewer operator: # 自定义角色映射 - operators在这个示例中代理传递的角色头包含sysadmins或access-level-security时用户被赋予admin角色包含camera-viewer时赋予viewer角色包含operators时赋予自定义角色operator若没有任何映射命中配置了default_role则回退到它若未定义role_mapFrigate 假定角色头直接包含admin、viewer或自定义角色名。匹配语义说明admin 优先若admin映射命中Frigate 直接把会话解析为admin避免用户同时属于多个组例如同时属于admin和viewer组时被意外降级。这一“admin 短路”逻辑与 resolve_role 中if admin in matched_roles and admin in config_roles: return admin的实现一致。提示如果用户拿到的角色不符合预期可开启 debug 日志查看 Frigate 从代理收到的确切头内容logger: default: info logs: frigate.api.auth: debugresolve_role在 debug 级别会打印原始角色头值、拆分出的组列表和最终解析结果frigate/api/auth.py排障时非常有用。端口行为差异认证端口8971Header 映射完全支持。remote-role头决定用户权限admin→ 完全访问用户管理、配置修改viewer→ 只读访问自定义角色→ 仅限auth.roles[role]中定义摄像头的只读访问。请确保代理同时发送用户头和角色头否则角色强制不生效。免认证端口5000角色头被忽略。所有请求按匿名处理。remote-role会被覆盖为 admin 级访问。该设计保证受信任网络内部的免认证使用。受信任头的白名单默认情况下只有以下请求头被允许传递Remote-User Remote-Groups Remote-Email Remote-Name X-Forwarded-User X-Forwarded-Groups X-Forwarded-Email X-Forwarded-Preferred-Username X-authentik-username X-authentik-groups X-authentik-email X-authentik-name X-authentik-uid如需增加更多头选项可以用 Docker bind mount 覆盖默认文件/usr/local/nginx/conf/proxy_trusted_headers.conf默认文件格式可参考仓库中的 docker/main/rootfs/usr/local/nginx/conf/proxy_trusted_headers.conf。该文件通过 auth_location.conf 引入为每个允许的proxy_set_header传递对应的原始头到/auth子请求。从源码结构看当前仓库的默认白名单中还额外转发了X-Auth-Request-User、X-Auth-Request-Groups、X-Auth-Request-Email、X-Auth-Request-Preferred-Username四个 traefik-forward-auth 风格头文件注释也列明了其适配 Authelia、Traefik forward auth、oauth2_proxy、Authentik 四类代理。登录页重定向Frigate 会“优雅地”执行登录页重定向可适配大多数认证代理当反向代理对未授权请求401、302或307返回Location头时Frigate 前端会自动检测该头并重定向到对应 URL例如代理自己的登录页。Frigate 自身的 401 认证失败响应同样会携带location: /login头frigate/api/auth.py。自定义登出 URL如果反向代理有专用登出 URL可用logout_url配置项指定UI 中的Logout链接将指向该地址配置定义见 frigate/config/proxy.py。用户角色与访问控制Frigate 支持用户角色用于控制 UI 与 API 中特定功能的访问如用户管理、配置修改。角色保存在数据库中或经代理头下发在通过认证端口8971访问 UI/API 时被强制生效。支持的角色admin所有功能的完全访问包括用户管理与配置。viewerUI 与 API 的只读访问包括查看摄像头、review 条目与历史录像配置编辑器和设置页不可用。自定义角色任意角色名字母数字、点/下划线风格绑定特定摄像头权限用于细粒度访问控制例如 operator 只能看指定摄像头。角色配置在auth.roles中定义。配置模型 frigate/config/auth.py 的关键约束源码级事实admin与viewer是保留角色模型校验器会无条件确保二者存在且映射为空列表空列表 访问全部摄像头自定义角色不得占用这两个名字自定义角色名必须为字母数字 下划线校验器role.replace(_, ).isalnum()每个自定义角色必须至少分配一台摄像头空摄像头列表会导致配置校验失败。自定义角色与摄像头访问viewer 角色提供对所有摄像头的只读访问。自定义角色允许 admin 把只读访问限制到特定摄像头——每个角色指定一个允许摄像头名的数组。被赋予自定义角色的用户相当于受限的 viewer只能查看指定摄像头的 Live、Review/History、Explore 和 Export。后端 API 在服务端强制执行对未授权摄像头返回 403前端 UI 也相应过滤内容如摄像头下拉框只显示允许的选项。服务端强制的实现分布在 frigate/api/auth.py 的require_camera_access依赖中未通过摄像头白名单检查时抛出 403错误信息会列出该角色实际允许的摄像头对实时流路径go2rtc 的 MSE/WebRTC 代理路径则由 deny_response_for_go2rtc_stream 在/auth子请求中按src参数逐一解析流归属摄像头并拦截越权请求。另有require_full_camera_accessfrigate/api/auth.py约束那些无法按摄像头裁剪响应、必须覆盖全部摄像头的端点admin 与 viewer 恒通过自定义角色仅当其摄像头列表覆盖全部摄像头时通过。角色配置示例UI 方式进入Settings Users Roles定义自定义角色并分配各角色可访问的摄像头。YAML 方式cameras: front_door: # ... 摄像头配置 side_yard: # ... 摄像头配置 garage: # ... 摄像头配置 auth: enabled: true roles: operator: # 自定义角色 - front_door - garage # operator 可访问前门与车库 neighbor: - side_yard如果希望某用户访问全部摄像头直接使用viewer角色即可。角色管理与强制以admin用户通过端口8971登录推荐或通过端口5000免认证访问进入Settings在Users区域编辑用户的角色从 admin、viewer 或自定义角色中选择在Roles区域添加/编辑/删除自定义角色用开关选择摄像头删除角色时其用户会被自动重新分配到viewer。角色强制的规则经由认证端口8971时角色通过 JWT 令牌或代理头如remote-role校验内部免认证端口5000上角色不被强制所有请求按匿名处理获得等同admin角色的无限制访问要使用基于角色的访问控制必须经**认证端口8971**直连或通过反向代理接入 Frigate。UI 中的角色可见性经8971登录时账户菜单角落显示用户名与角色经5000使用时UI 恒定显示用户名 anonymous、角色 admin。API 层面的细节源码事实内置 admin 用户不可被删除DELETE /users/admin返回 403frigate/api/auth.py也不可修改其角色frigate/api/auth.py避免最后一个管理员被误删导致实例锁死。此外FastAPI 应用设置了全局默认 admin 依赖除豁免路径与按摄像头作用域的路径外其余端点默认要求remote-role: admin这正是 viewer / 自定义角色被限制在只读面内的总闸门各只读端点通过allow_any_authenticated()/require_role()显式放宽。相关测试可参考 test_http_auth_internal_port.py、test_proxy_auth.py、test_go2rtc_stream_auth.py 与 test_media_auth.py。API 认证实战获取并使用 Bearer Token要调用 Frigate API需要先认证。步骤如下1. 登录获取令牌向/login发起 POST 请求提交凭据curl -i -X POST https://frigate_ip:8971/api/login \ -H Content-Type: application/json \ -d {user: admin, password: your_password}如果你的 Frigate 实例使用自签名证书这些步骤中可能需要在参数列表里加-k例如curl -k -i -X POST ...。响应中会包含携带 JWT 令牌的 CookieCookie 名为frigate_tokenHttpOnly见 set_jwt_cookie。若认证已被禁用auth.enabled: false/login直接返回 404 Authentication is disabledfrigate/api/auth.py。2. 使用 Bearer Token拿到令牌后在后续请求的Authorization头中携带它curl -H Authorization: Bearer your_token https://frigate_ip:8971/api/profile/auth端点会优先从Authorization头读取 Bearer 令牌其次才读取 Cookiefrigate/api/auth.py因此脚本化集成推荐直接用 Bearer 方式。/profile会返回当前用户名、角色与该角色允许的摄像头列表frigate/api/auth.py。3. 令牌生命周期令牌有效期为配置的session_length访问/auth端点时临期 Cookie 会被自动刷新为全时长新令牌用户修改密码后签发于修改之前的令牌无法再被刷新等同失效调用/logout清除会话 Cookie该端点删除 Cookie 并 303 重定向到登录页frigate/api/auth.py。小结认证配置速查把本文涉及的auth/proxy配置项汇总全部字段与默认值可在 frigate/config/auth.py 与 frigate/config/proxy.py 中核对auth: enabled: true # 启用原生认证对接上游代理时建议关闭 reset_admin_password: false # 启动时重置 admin 密码并打印到日志 session_length: 86400 # 会话时长秒默认 24 小时 # refresh_time: 1800 # 临期刷新窗口秒默认 1800 failed_login_rate_limit: 1/second;5/minute;20/hour trusted_proxies: - 172.18.0.0/16 roles: operator: [front_door, garage] # 自定义角色 - 摄像头白名单 proxy: auth_secret: random long string # 代理须以 X-Proxy-Secret 头回传 default_role: viewer separator: , header_map: user: x-forwarded-user role: x-forwarded-groups role_map: # 可选上游组名 - Frigate 角色 admin: [sysadmins] # logout_url: https://auth.example.com/logout配置完成后登录限流、会话刷新、代理头角色解析的行为均可通过开启frigate.api.auth: debug日志逐项验证端口选择上记住一条主线——对外一律走8971认证、角色强制5000仅用于受信 Docker 网络内部的匿名集成且其访问必须被网络层严格隔离。【免费下载链接】frigateNVR with realtime local object detection for IP cameras项目地址: https://gitcode.com/GitHub_Trending/fr/frigate创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考