ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Dozzle Forward Proxy 认证模式完全指南:对接 Authelia、Cloudflare Zero Trust 与 oauth2-proxy

Dozzle Forward Proxy 认证模式完全指南:对接 Authelia、Cloudflare Zero Trust 与 oauth2-proxy Dozzle Forward Proxy 认证模式完全指南对接 Authelia、Cloudflare Zero Trust 与 oauth2-proxy【免费下载链接】dozzleRealtime log viewer for containers. Supports Docker, Swarm and K8s.项目地址: https://gitcode.com/GitHub_Trending/do/dozzle本文是一份关于 Dozzle 反向代理认证模式Forward Proxy Authentication的完整实战指南。Dozzle 是一款实时容器日志查看器支持 Docker、Swarm 与 K8s其内置的forward-proxy认证提供器允许你把登录身份校验完全交给前置的反向代理或访问网关Dozzle 只负责信任代理注入的请求头。读完本文你将掌握forward-proxy的全部配置参数与默认请求头约定、基于源码理解其认证中间件的内部原理并能直接落地三种主流对接方案Authelia Traefik、Cloudflare Zero Trust以及 Pocket ID oauth2-proxy。核心概念把认证交给代理Dozzle 只读取请求头在默认的none模式下Dozzle 不要求任何认证任何能访问到端口的人都能查看日志。而forward-proxy模式把谁可以访问的决定权完全交给部署在前面的反向代理由代理或访问网关完成登录、校验身份后再把用户信息通过 HTTP 请求头注入并转发给 DozzleDozzle 通过读取这些请求头识别当前用户、执行容器过滤与角色授权。从源码看该模式的核心实现位于 internal/auth/proxy.goNewForwardProxyAuth构造一个proxyAuthContext其AuthMiddleware检查配置的用户头默认Remote-User是否存在若请求头存在则解析过滤头与角色头构造User对象并放入请求上下文WithUser后续处理链即可通过UserFromContext取得当前用户若请求头缺失则直接放行到RequireAuthentication由它返回401 Unauthorized。对应的测试 internal/web/auth_proxy_test.go 清晰验证了这一点不带任何Remote-*请求头访问/返回 401带上Remote-Email、Remote-Name、Remote-User三个请求头后返回 200。安全前提不要让客户端直接触达 Dozzle正因为 Dozzle 无条件信任请求头中的身份信息必须保证只有可信的反向代理能直接访问 Dozzle。一旦 Dozzle 端口暴露在宿主机上任何人都可以伪造Remote-User等请求头绕过认证。本文后面所有方案都会刻意使用expose仅容器网络内部可达而不是ports映射到宿主机这正是为了封死这条旁路。快速上手启用 forward-proxy 模式在 internal/support/cli/args.go 中可以看到--auth-provider环境变量DOZZLE_AUTH_PROVIDER默认值为none可选值包括none、simple、oidc与forward-proxy。将--auth-provider设为forward-proxy即启用该模式。Docker CLI 启动$ docker run -v /var/run/docker.sock:/var/run/docker.sock -v /path/to/dozzle/data:/data -p 8080:8080 amir20/dozzle --auth-provider forward-proxyDocker Compose 启动services: dozzle: image: amir20/dozzle:latest volumes: - /var/run/docker.sock:/var/run/docker.sock - /path/to/dozzle/data:/data ports: - 8080:8080 environment: DOZZLE_AUTH_PROVIDER: forward-proxy务必挂载 /data 持久化卷上面两个示例中都挂载了/data。这不是可有可无的forward-proxy 模式下Dozzle 同样会把每个用户的个性化设置写入磁盘例如固定的列、过滤条件等。如果不挂载持久化卷这些设置会在容器每次重建时丢失。默认读取的请求头在 forward-proxy 模式下Dozzle 默认期待以下请求头请求头含义说明Remote-User用户名例如johndoe是识别用户身份的必需头Remote-Email用户邮箱同时用于查找该用户的 Gravatar 头像Remote-Name显示名称例如John Doe用于界面展示Remote-Filter允许的容器过滤列表逗号分隔限制该用户能看到哪些容器Remote-Roles允许的角色列表逗号分隔控制该用户可执行的操作对应默认值同样定义在 internal/support/cli/args.go--auth-header-userDOZZLE_AUTH_HEADER_USER默认Remote-User、--auth-header-emailDOZZLE_AUTH_HEADER_EMAIL默认Remote-Email、--auth-header-nameDOZZLE_AUTH_HEADER_NAME默认Remote-Name、--auth-header-filterDOZZLE_AUTH_HEADER_FILTER默认Remote-Filter、--auth-header-rolesDOZZLE_AUTH_HEADER_ROLES默认Remote-Roles。这意味着你可以通过环境变量把任意自定义请求头映射到对应的用户属性上下文 Cloudflare 方案就是这么做的。邮箱与 Gravatar 头像在 internal/auth/proxy.go 中hashEmail将邮箱去除首尾空格、转为小写后做 MD5 哈希internal/auth/users.go 的AvatarURL再据此拼接 Gravatar 头像地址。因此如果代理注入的邮箱大小写不一致不影响头像命中MD5 前已统一小写但代理本身最好注入规范化的邮箱。过滤头限制用户可见的容器Remote-Filter的值会传给 internal/container/types.go 中的ParseContainerFilter解析成容器过滤条件。若解析失败internal/auth/proxy.go 会记录告警日志并直接返回400 Bad Request对应的单元测试见 internal/auth/proxy_test.go。代理方可以按用户维度注入不同的过滤表达式实现谁只能看哪些容器的日志。角色头细粒度权限控制Remote-Roles的值由 internal/auth/roles.go 中的ParseRole解析。角色以位掩码形式定义共五类角色名位掩码能力shell1 iota起始位移允许进入容器执行 Shellactions2允许在 Web 界面上对容器执行操作download4允许下载日志notifications8允许配置通知/告警cloud16允许使用 Dozzle Cloud 相关能力另有三个特殊值all授予全部角色、none不授予任何角色、以及带^前缀的否定语法如all,^shell表示除shell外全部授予。从 internal/auth/roles.go 的实现看ParseRole支持逗号,、竖线|分隔甚至兼容 JSON 数组字符串。关键安全语义若Remote-Roles请求头为空代码会回退为All即所有已认证用户获得全部角色——所以如果你的代理无法注入角色头必须清楚这意味着所有登录用户都是管理员。配置退出登录地址你可以通过环境变量DOZZLE_AUTH_LOGOUT_URL命令行参数--auth-logout-url指定一个退出登录后跳转的 URL用于把用户送回代理侧的会话销毁端点DOZZLE_AUTH_LOGOUT_URL: http://oauth2.example.ru/oauth2/sign_out在 internal/web/auth.go 的deleteToken中Dozzle 清除自己的会话 cookie 后会检查Authorizer是否实现了LogoutRedirector接口若是则把跳转目标以 JSON 返回给前端由前端引导浏览器跳转到代理的登出地址。方案一对接 AutheliaTraefik forward-authAuthelia 是一个开源的认证与授权服务器/门户负责身份和访问管理。它本身可以保护多个应用而 Dozzle 通过 forward-proxy 模式消费 Authelia 注入的身份头。Authelia 自身的搭建配置不在本文展开但下面的完整示例可以直接作为把 Dozzle 纳入 Authelia 保护体系的参考。docker-compose.ymlAuthelia Traefik Dozzle 三服务networks: net: driver: bridge services: authelia: image: authelia/authelia container_name: authelia volumes: - ./authelia:/config networks: - net labels: - traefik.enabletrue - traefik.http.routers.authelia.ruleHost(authelia.example.com) - traefik.http.routers.authelia.entrypointshttps - traefik.http.routers.authelia.tlstrue - traefik.http.routers.authelia.tls.optionsdefault - traefik.http.middlewares.authelia.forwardAuth.addresshttp://authelia:9091/api/authz/forward-auth - traefik.http.middlewares.authelia.forwardAuth.trustForwardHeadertrue - traefik.http.middlewares.authelia.forwardAuth.authResponseHeadersRemote-User,Remote-Groups,Remote-Name,Remote-Email expose: - 9091 restart: unless-stopped traefik: image: traefik:v3.5 container_name: traefik volumes: - ./traefik:/etc/traefik - /var/run/docker.sock:/var/run/docker.sock networks: - net labels: - traefik.enabletrue - traefik.http.routers.api.ruleHost(traefik.example.com) - traefik.http.routers.api.entrypointshttps - traefik.http.routers.api.serviceapiinternal - traefik.http.routers.api.tlstrue - traefik.http.routers.api.tls.optionsdefault - traefik.http.routers.api.middlewaresautheliadocker ports: - 80:80 - 443:443 command: - --api - --providers.dockertrue - --providers.docker.exposedByDefaultfalse - --providers.file.filename/etc/traefik/certificates.yml - --entrypoints.httptrue - --entrypoints.http.address:80 - --entrypoints.http.http.redirections.entrypoint.tohttps - --entrypoints.http.http.redirections.entrypoint.schemehttps - --entrypoints.httpstrue - --entrypoints.https.address:443 - --logtrue - --log.levelDEBUG dozzle: image: amir20/dozzle:latest networks: - net environment: DOZZLE_AUTH_PROVIDER: forward-proxy volumes: - /var/run/docker.sock:/var/run/docker.sock - dozzle:/data labels: - traefik.enabletrue - traefik.http.routers.dozzle.ruleHost(dozzle.example.com) - traefik.http.routers.dozzle.entrypointshttps - traefik.http.routers.dozzle.tlstrue - traefik.http.routers.dozzle.tls.optionsdefault - traefik.http.routers.dozzle.middlewaresautheliadocker expose: - 8080 restart: unless-stopped volumes: dozzle:注意几个关键点身份注入链路Authelia 中间件通过authResponseHeadersRemote-User,Remote-Groups,Remote-Name,Remote-Email把认证结果注入请求头Traefik 再把这些请求头转发给 DozzleDozzle 不映射宿主机端口expose: 8080保证 Dozzle 只能被 Traefik同一net网络访问SSL 必需Authelia 只工作在 HTTPS 下因此必须准备好有效的 SSL 证书示例中由/etc/traefik/certificates.yml提供证书。Authelia configuration.yml############################################################### # Authelia configuration # ############################################################### server: address: tcp://0.0.0.0:9091 log: level: info totp: issuer: authelia.com identity_validation: reset_password: jwt_secret: a_very_important_secret authentication_backend: file: path: /config/users_database.yml access_control: default_policy: deny rules: - domain: traefik.example.com policy: one_factor - domain: dozzle.example.com policy: one_factor session: secret: unsecure_session_secret cookies: - domain: example.com # 必须匹配你受保护根域 authelia_url: https://authelia.example.com default_redirection_url: https://public.example.com regulation: max_retries: 3 find_time: 120 ban_time: 300 storage: encryption_key: you_must_generate_a_random_string_of_more_than_twenty_chars_and_configure_this local: path: /config/db.sqlite3 notifier: filesystem: filename: /config/notification.txt把 Authelia 组映射为 Dozzle 角色Authelia 会在Remote-Groups请求头中发送用户的组成员信息但Dozzle 默认并不读取这个请求头默认的角色头是Remote-Roles。要把 Authelia 的组映射为 Dozzle 的角色需要两步在 Dozzle 服务上设置DOZZLE_AUTH_HEADER_ROLES: Remote-Groups让 Dozzle 改从Remote-Groups读取角色把 Authelia 中的组直接按角色命名并可以加上dozzle_前缀作为别名——例如名为dozzle_shell的组授予shell角色其他名称的组会被忽略。这与 internal/auth/roles.go 中ParseRole对dozzle_shell、dozzle_actions、dozzle_download、dozzle_notifications、dozzle_cloud等前缀别名的解析逻辑一一对应。dozzle_前缀的意义在于当你在 Authelia 中维护了大量与 Dozzle 无关的组时只有显式带dozzle_前缀或恰好与角色同名的组才会生效其余组名被安全忽略。重要安全提醒如果没有做上述组到角色的映射那么任何通过 Authelia 认证的用户都会默认获得全部角色对应源码中角色头为空则回退为All的行为。关于用户粒度过滤与角色的更多细节可参考 Dozzle 自带的简单认证simple文档 docs/guide/authentication/simple.md。方案二对接 Cloudflare Zero TrustCloudflare Zero TrustCloudflare Access是为自托管软件提供认证访问的服务。下面的配置让 Dozzle 完全依赖 Cloudflare 的隧道tunnel身份验证。services: dozzle: image: amir20/dozzle:latest environment: DOZZLE_AUTH_PROVIDER: forward-proxy DOZZLE_AUTH_HEADER_USER: Cf-Access-Authenticated-User-Email DOZZLE_AUTH_HEADER_EMAIL: Cf-Access-Authenticated-User-Email DOZZLE_AUTH_HEADER_NAME: Cf-Access-Authenticated-User-Email volumes: - /var/run/docker.sock:/var/run/docker.sock - dozzle:/data expose: - 8080 restart: unless-stopped volumes: dozzle:这份配置的本质是请求头重映射Cloudflare 注入的身份头是Cf-Access-Authenticated-User-Email它携带的是用户邮箱而 Dozzle 默认从Remote-User、Remote-Email、Remote-Name读取身份。这里通过三个环境变量把 Dozzle 的用户名、邮箱、显示名三个字段全部映射到同一个 Cloudflare 请求头于是邮箱即身份。为什么必须用expose而不是portsexpose只是声明容器内部端口不会把 8080 映射到宿主机唯一入口就是 Cloudflare 隧道。如果使用ports把端口发布到宿主机宿主机上的任何人都可以自己伪造Cf-Access-Authenticated-User-Email请求头直接访问 Dozzle从而完全绕过 Cloudflare 的认证。容器启动后你还需要在 Cloudflare Zero Trust 控制台中按官方指引配置 Application选择 Self-hosted 应用类型、绑定dozzle.example.com域名并关联到对应隧道Dozzle 侧无需再做其他改动。方案三对接 Pocket IDoauth2-proxy OIDC[!TIP] 现状提示Dozzle 目前已原生支持 OpenID Connect你可以直接把--auth-oidc-issuer指向 Pocket ID完全不需要 oauth2-proxy 这一层见 docs/guide/authentication/oauth.md 中的 OIDC 登录章节。下面这套 oauth2-proxy 方案依然保留的价值在于适合已经在运行 oauth2-proxy 的现有部署或者你希望用同一个代理同时保护除 Dozzle 之外的其他应用。如果采用 oauth2-proxy 方案你需要先部署一个容器让 OpenID Connect 认证经由你的反向代理流转再以请求头形式注入 Dozzle。第一步在 Pocket ID 中创建 OIDC 客户端在 Pocket ID 控制台新建一个 OIDC 客户端用于 Dozzle名称NameDozzle回调地址Callback URLshttps://dozzle.example.com/oauth2/callbackPKCE启用Enabled创建完成后复制Client ID与Client Secret备用。第二步修改现有 Dozzle compose在现有的 Dozzle compose 服务中加入以下环境变量并注释掉 Dozzle 的端口映射流量将从新的认证容器转发进来environment: DOZZLE_AUTH_PROVIDER: forward-proxy DOZZLE_AUTH_HEADER_USER: X-Forwarded-User DOZZLE_AUTH_HEADER_EMAIL: X-Forwarded-Email DOZZLE_AUTH_HEADER_NAME: X-Forwarded-Preferred-Username# ports: # - 8080:8080这里把 oauth2-proxy 注入的标准请求头X-Forwarded-User、X-Forwarded-Email、X-Forwarded-Preferred-Username分别映射为 Dozzle 的用户名、邮箱和显示名。此方案不需要对你的反向代理配置做任何改动。第三步添加 oauth2-proxy 服务services: # ... oauth2-proxy: image: quay.io/oauth2-proxy/oauth2-proxy:latest restart: unless-stopped container_name: dozzle-oidc command: --config /oauth2-proxy.cfg volumes: - ./oauth2-proxy.cfg:/oauth2-proxy.cfg ports: - 8080:4180第四步创建 oauth2-proxy 配置文件在 compose 文件同级目录创建oauth2-proxy.cfgclient_id xxx # 来自 Pocket ID client_secret xxx # 来自 Pocket ID cookie_secret xxx # 用 openssl rand -base64 32 | tr -- / -_ 生成 upstreams http://dozzle:8080 # 指向 Dozzle 容器的内部端口 code_challenge_method S256 # PKCE 挑战方式 plain 或 S256 cookie_expire 0 # 秒0 表示会话级 cookie cookie_name __Host-oauth2-proxy # 或 __Secure-oauth2-proxy安全性略低 cookie_secure true # 使用安全 HTTPS cookie email_domains [*] # 允许任意邮箱域认证 http_address 0.0.0.0:4180 # oauth2-proxy 监听端口 oidc_issuer_url https://id.example.com # 你的 Pocket ID 基础 URL provider_display_name Pocket ID # OIDC 登录按钮显示名 provider oidc # 使用 OpenID Connect reverse_proxy true # 处于反向代理之后 scope openid email profile groups # 透传这些 OIDC 作用域各字段按注释填写client_id/client_secret来自 Pocket IDcookie_secret用注释中的openssl命令生成一个 32 字节的随机值oidc_issuer_url填 Pocket ID 的部署地址。第五步重启 Docker Compose 栈重启后你的反向代理会先经由 oauth2-proxy 完成 OIDC 认证再把身份请求头交给 Dozzle。若登录异常优先查看 oauth2-proxy 与 Dozzle 的容器日志定位问题。三种方案对比与选型建议方案身份来源关键请求头映射适用场景Authelia TraefikAuthelia 用户库/组Remote-User/Remote-Name/Remote-EmailRemote-Groups映射角色已有或计划搭建 Authelia 统一身份门户需要同时保护多个应用Cloudflare Zero TrustCloudflare AccessCf-Access-Authenticated-User-Email映射到三个身份字段域名已接入 Cloudflare、希望零信任接入且不想维护额外代理组件Pocket ID oauth2-proxyPocket IDOIDCX-Forwarded-User/X-Forwarded-Email/X-Forwarded-Preferred-Username已在运行 oauth2-proxy或代理还需保护 Dozzle 之外的应用三者共同的核心原则不变认证永远发生在 Dozzle 之外Dozzle 只信任代理注入的身份请求头因此 Dozzle 端口绝不能直接暴露给不受信任的客户端。进阶从源码理解权限边界如果你想进一步加固权限配置建议阅读以下源码文件它们完整定义了 forward-proxy 模式的行为边界internal/auth/proxy.go请求头读取、用户构造、过滤解析与上下文注入的完整实现internal/auth/roles.go角色位掩码定义、dozzle_前缀别名、^否定语法与 JSON/逗号/竖线多种分隔解析internal/support/cli/args.go全部认证相关 CLI 参数与默认值含DOZZLE_AUTH_PROVIDER、五个请求头参数及DOZZLE_AUTH_LOGOUT_URLinternal/web/auth_proxy_test.go缺请求头 401、带请求头 200 的行为验证internal/auth/proxy_test.go非法过滤头导致 400 的验证。理解这些实现后你可以根据自己的安全需求做两件关键决策一是确认代理是否可靠地注入了Remote-Roles避免空角色头 全员管理员的默认回退二是确认 Dozzle 的监听端口是否只对代理可达expose而非ports。【免费下载链接】dozzleRealtime log viewer for containers. Supports Docker, Swarm and K8s.项目地址: https://gitcode.com/GitHub_Trending/do/dozzle创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表