本文使用「署名 4.0 国际 (CC BY 4.0)」许可协议,欢迎转载、或重新修改使用,但需要注明来源。 [署名 4.0 国际 (CC BY 4.0)](https://creativecommons.org/licenses/by/4.0/deed.zh) 本文作者: 苏洋 创建时间: 2020年12月02日 统计字数: 6675字 阅读时间: 14分钟阅读 本文链接: https://soulteary.com/2020/12/02/traefik-2-basic-authorization-verification-part-2.html ----- # Traefik 2 基础授权验证(后篇) [上篇文章](https://soulteary.com/2020/12/02/traefik-2-basic-authorization-verification-part-1.html)中,我们提到了 Traefik 的 `Forward Auth`,本篇内容我们来展开聊聊如何使用它。 ## 准备基础的 Web 服务Demo 这篇文章里,我们继续使用 `whoami` 作为 Web 服务,基础的配置文件和上一篇文章中一致,暂时不需要额外的设置: ```yaml version: '3' services: whoami: image: containous/whoami labels: - "traefik.enable=true" - "traefik.docker.network=traefik" - "traefik.http.routers.test-auth-web.middlewares=https-redirect@file" - "traefik.http.routers.test-auth-web.entrypoints=http" - "traefik.http.routers.test-auth-web.rule=Host(`whoami.lab.com`, `whoami.lab.io`)" - "traefik.http.routers.test-auth-ssl.entrypoints=https" - "traefik.http.routers.test-auth-ssl.tls=true" - "traefik.http.routers.test-auth-ssl.rule=Host(`whoami.lab.com`, `whoami.lab.io`)" networks: - traefik networks: traefik: external: true ``` ## 使用容器配置 Traefik Forward Auth 服务 [thomseddon/traefik-forward-auth](https://github.com/thomseddon/traefik-forward-auth) 这个开源项目让我们在使用 Traefik 的时候,结合 Forward Auth 中间件,可以快速实现通用 OAuth / SSO 功能: - 支持多种验证“服务商”:Google/ 通用OAuth / 通用OIDC - 支持自定义请求服务器和指定路径,方便与现有系统集成 - 支持基础的用户限制、授权来源限制、支持设置跨域 Cookie 简单来说,只要你的系统对外暴露服务是通过 Traefik,那么可以非常轻松愉快的使用这个模式为应用添加一层通用的前置 SSO 。可以达到效果类似我们在外部公网访问公司内网服务时候,会出现的一个登陆框,只有登陆成功后,才会展示我们想要看的内容。 使用这个方案的好处是,我们只需要结合一些简单的胶水代码,就可以做到背后的应用无修改接入或者几乎无修改接入,即使应用本身不支持 OAuth / SSO 方式接入,或者说我们无法直接修改的商业付费软件。 ```yaml version: '3' services: traefik-forward-auth: image: thomseddon/traefik-forward-auth:v2.2.0 restart: always hostname: traefik-auth.lab.io environment: - LOG_LEVEL=trace - DEFAULT_PROVIDER=generic-oauth - PROVIDERS_GENERIC_OAUTH_AUTH_URL=https://sso.lab.io/dialog/authorize - PROVIDERS_GENERIC_OAUTH_TOKEN_URL=http://sso-web/oauth/token - PROVIDERS_GENERIC_OAUTH_USER_URL=http://sso-web/api/userinfo - PROVIDERS_GENERIC_OAUTH_USER_URL=http://sso-web/api/traefik-auth-user - PROVIDERS_GENERIC_OAUTH_CLIENT_ID=abc123 - PROVIDERS_GENERIC_OAUTH_CLIENT_SECRET=ssh-secret - PROVIDERS_GENERIC_OAUTH_SCOPE=* - PROVIDERS_GENERIC_OAUTH_TOKEN_STYLE=header - SECRET=something-random - INSECURE_COOKIE=true labels: - "traefik.enable=true" - "traefik.docker.network=traefik" - "traefik.http.routers.traefik-auth-web.entrypoints=http" - "traefik.http.routers.traefik-auth-web.rule=Host(`traefik-auth.lab.com`, `traefik-auth.lab.io`)" - "traefik.http.routers.traefik-auth-ssl.entrypoints=https" - "traefik.http.routers.traefik-auth-ssl.rule=Host(`traefik-auth.lab.com`, `traefik-auth.lab.io`)" - "traefik.http.routers.traefik-auth-ssl.tls=true" - "traefik.http.middlewares.traefik-forward-auth.forwardauth.address=http://traefik-forward-auth:4181" - "traefik.http.middlewares.traefik-forward-auth.forwardauth.authResponseHeaders=X-Forwarded-User" - "traefik.http.services.traefik-forward-auth.loadbalancer.server.port=4181" networks: - traefik networks: traefik: external: true ``` 使用这个项目因为配置项比较多,而显得比较复杂,实际上并非如此,我们一点一点来理解它。 ### 配置应用参数 我们在环境变量中定义了许多内容,这些内容的解释可以参考[官方文档](https://github.com/thomseddon/traefik-forward-auth#overview),这里我选择了 OAuth 作为授权服务配置,为了演示方便,我将他们运行在相同主机的相同容器网卡中,`PROVIDERS_GENERIC_OAUTH_AUTH_URL` 是用于用户在浏览器前端访问的地址,用于“确认授权”行为,所以需要配置对外访问的网络域名,除此之外,`PROVIDERS_GENERIC_OAUTH_TOKEN_URL`、`PROVIDERS_GENERIC_OAUTH_USER_URL`、`PROVIDERS_GENERIC_OAUTH_USER_URL` 可以都走容器内部通信,更加高效。 如果我们的 SSO 服务可以进行独立部署,那么这里四个 URL 变量需要都配置成可访问的域名地址,如果使用 https 协议,涉及自签名证书,需要重新构建容器。下一篇内容,我们再仔细展开 SSO 服务,这里稍作了解,有个印象就行啦。 ```yaml environment: - LOG_LEVEL=trace - DEFAULT_PROVIDER=generic-oauth - PROVIDERS_GENERIC_OAUTH_AUTH_URL=https://sso.lab.io/dialog/authorize - PROVIDERS_GENERIC_OAUTH_TOKEN_URL=http://sso-web/oauth/token - PROVIDERS_GENERIC_OAUTH_USER_URL=http://sso-web/api/userinfo - PROVIDERS_GENERIC_OAUTH_USER_URL=http://sso-web/api/traefik-auth-user - PROVIDERS_GENERIC_OAUTH_CLIENT_ID=abc123 - PROVIDERS_GENERIC_OAUTH_CLIENT_SECRET=secret - PROVIDERS_GENERIC_OAUTH_SCOPE=* - PROVIDERS_GENERIC_OAUTH_TOKEN_STYLE=header - SECRET=something-random - INSECURE_COOKIE=true ``` 接着我们来进行服务路由的配置。 ### 配置应用服务路由 配置服务路由比较简单,可以根据需求和喜好,设置是否“执行 HTTP 自动转发 HTTPS”等逻辑,设置方法[上一篇文章](https://soulteary.com/2020/12/02/traefik-2-basic-authorization-verification-part-1.html)中有描述,就不再赘述: ```yaml labels: - "traefik.enable=true" - "traefik.docker.network=traefik" - "traefik.http.routers.traefik-auth-web.entrypoints=http" - "traefik.http.routers.traefik-auth-web.rule=Host(`traefik-auth.lab.com`, `traefik-auth.lab.io`)" - "traefik.http.routers.traefik-auth-ssl.entrypoints=https" - "traefik.http.routers.traefik-auth-ssl.tls=true" - "traefik.http.routers.traefik-auth-ssl.rule=Host(`traefik-auth.lab.com`, `traefik-auth.lab.io`)" ... ``` 接着是配置 `forwardauth` 中间件,这里和配置应用参数情况类似,因为是同机部署演示,这里使用应用名称即可。如果是独立部署,需要替换为访问域名: ```yaml labels: ... - "traefik.http.middlewares.traefik-forward-auth.forwardauth.address=http://traefik-forward-auth:4181" - "traefik.http.middlewares.traefik-forward-auth.forwardauth.authResponseHeaders=X-Forwarded-User" - "traefik.http.services.traefik-forward-auth.loadbalancer.server.port=4181" ``` 上面的中间件配置中还存在一个 `authResponseHeaders` 配置项 :用来向后续服务传递通过鉴权的用户信息,可以根据自己的需求进行修改或者移除。 最后,使用 `docker-compose -d` 启动应用,等待应用启动完毕,就可以准备应用接入啦。 ## 完成应用配置 我们将文章开头的 Web 服务 Demo 配置中添加一条简单的配置规则,让刚刚配置的 `traefik-forward-auth` 加入到应用服务路由中: ```yaml version: '3' services: whoami: image: containous/whoami labels: - "traefik.enable=true" - "traefik.docker.network=traefik" ... - "traefik.http.routers.test-auth-ssl.middlewares=traefik-forward-auth@docker" ... networks: - traefik networks: traefik: external: true ``` 将内容单独保存一个新的 `docker-compose.yml` ,再次继续使用 `docker-compose up -d` 启动服务,接着进行服务效果验证。 ## 验证 Forward Auth SSO 效果 打开浏览器,输入 `whoami.lab.io` ,可以看到首先是被重定向到了 `https` 协议,然后再次被重定向到了 `sso.lab.io/...` 的 SSO 鉴权地址,提示我们输入账号密码。 ![请求被重定向到了账号鉴权的地址](https://attachment.soulteary.com/2020/12/02/auth-input.png) 我们使用 `curl` 再来模拟一次服务端请求: ```TeXT curl https://whoami.lab.io -v * Trying 127.0.0.1... * TCP_NODELAY set * Connected to whoami.lab.io (127.0.0.1) port 443 (#0) ... > GET / HTTP/2 > Host: whoami.lab.io > User-Agent: curl/7.64.1 > Accept: */* > * Connection state changed (MAX_CONCURRENT_STREAMS == 250)! < HTTP/2 307 < content-type: text/html; charset=utf-8 < date: Wed, 02 Dec 2020 13:45:35 GMT < location: https://sso.lab.io/dialog/authorize?client_id=abc123&redirect_uri=https%3A%2F%2Fwhoami.lab.io%2F_oauth&response_type=code&scope=%2A&state=396bd5c20d6bcfdffc2426bddf619707%3Ageneric-oauth%3Ahttps%3A%2F%2Fwhoami.lab.io%2F < set-cookie: _forward_auth_csrf=396bd5c20d6bcfdffc2426bddf619707; Path=/; Domain=whoami.lab.io; Expires=Thu, 03 Dec 2020 01:45:35 GMT; HttpOnly < content-length: 271 < Temporary Redirect. * Connection #0 to host whoami.lab.io left intact * Closing connection 0 ``` 可以看到配置依然是生效的,服务端返回了 307 重定向,我们发送到 `whoami.lab.io` 的请求被转向到了 `sso.lab.io` ,符合我们的预期。 接着在浏览器中输入账号密码,点击提交,可以看到被重定向到了页面授权确认页面。 ![提示需要用户确认授权](https://attachment.soulteary.com/2020/12/02/auth-dialog.png) 点击允许,进行授权,等待授权完毕,我们就可以正式访问到应用的页面了。当然,也有一些应用会精简掉用户确认的对话框,让验证的整个流程更加的顺滑: ![授权完毕,正常访问背后的应用](https://attachment.soulteary.com/2020/12/02/app-after-auth.png) 可以看到,应用请求头 `X-Forwarded-User` 和 Cookie 中可以看到通过授权的用户信息,可以进行进一步处理,或者鉴权规则的完善。 ## 最后 写到这里,Traefik 基础鉴权验证的内容就完毕了,但是 SSO / OAuth 相关的内容才刚刚开始。 --EOF