misterli's Blog.

使用traefik为服务配置sso

字数统计: 2.6k阅读时长: 11 min
2020/09/07

使用traefik为服务配置sso

ForwardAuth

我们使用中经常会遇到一些网页没有登录身份验证,比如prometheus ui 和traefik ui 等,我们通常使用basic-auth 做登陆身份验证,这种虽然简单但是不利于实际使用中我们针对用户做限制,比如不希望某些人访问,basic-auth 无法实现类似的限制功能。

traefik有一个中间件ForwardAuth,ForwardAuth中间件可以将身份验证委派给外部服务。如果服务响应代码为2XX,则将授予访问权限并执行原始请求。否则,将返回来自身份验证服务器的响应。

AuthForward

配置如下:

1
2
3
4
5
6
7
8
9
10
11
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
name: test-auth
spec:
forwardAuth:
address: https://example.com/auth
authResponseHeaders:
- X-Forwarded-User
# trustForwardHeader: true

address选项定义外部身份验证服务器地址。

authResponseHeaders选项定义要从外部身份验证服务器复制到请求的标头列表

trustForwardHeader选项设置true表示信任所有现有的X-Forwarded-*标头

Traefik Forward Auth

这里外部服务我们使用Traefik Forward Auth,Traefik Forward Auth是一个轻量的前端身份验证服务,为traefik 提供OAuth/SSO登录和身份验证。

用法

traefik-forward-auth支持下列选项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
Usage:
traefik-forward-auth [OPTIONS]

Application Options:
--log-level=[trace|debug|info|warn|error|fatal|panic] Log level (default: warn) [$LOG_LEVEL]
--log-format=[text|json|pretty] Log format (default: text) [$LOG_FORMAT]
--auth-host= Single host to use when returning from 3rd party auth [$AUTH_HOST]
--config= Path to config file [$CONFIG]
--cookie-domain= Domain to set auth cookie on, can be set multiple times [$COOKIE_DOMAIN]
--insecure-cookie Use insecure cookies [$INSECURE_COOKIE]
--cookie-name= Cookie Name (default: _forward_auth) [$COOKIE_NAME]
--csrf-cookie-name= CSRF Cookie Name (default: _forward_auth_csrf) [$CSRF_COOKIE_NAME]
--default-action=[auth|allow] Default action (default: auth) [$DEFAULT_ACTION]
--default-provider=[google|oidc|generic-oauth] Default provider (default: google) [$DEFAULT_PROVIDER]
--domain= Only allow given email domains, can be set multiple times [$DOMAIN]
--lifetime= Lifetime in seconds (default: 43200) [$LIFETIME]
--logout-redirect= URL to redirect to following logout [$LOGOUT_REDIRECT]
--url-path= Callback URL Path (default: /_oauth) [$URL_PATH]
--secret= Secret used for signing (required) [$SECRET]
--whitelist= Only allow given email addresses, can be set multiple times [$WHITELIST]
--rule.<name>.<param>= Rule definitions, param can be: "action", "rule" or "provider"

Google Provider:
--providers.google.client-id= Client ID [$PROVIDERS_GOOGLE_CLIENT_ID]
--providers.google.client-secret= Client Secret [$PROVIDERS_GOOGLE_CLIENT_SECRET]
--providers.google.prompt= Space separated list of OpenID prompt options [$PROVIDERS_GOOGLE_PROMPT]

OIDC Provider:
--providers.oidc.issuer-url= Issuer URL [$PROVIDERS_OIDC_ISSUER_URL]
--providers.oidc.client-id= Client ID [$PROVIDERS_OIDC_CLIENT_ID]
--providers.oidc.client-secret= Client Secret [$PROVIDERS_OIDC_CLIENT_SECRET]
--providers.oidc.resource= Optional resource indicator [$PROVIDERS_OIDC_RESOURCE]

Generic OAuth2 Provider:
--providers.generic-oauth.auth-url= Auth/Login URL [$PROVIDERS_GENERIC_OAUTH_AUTH_URL]
--providers.generic-oauth.token-url= Token URL [$PROVIDERS_GENERIC_OAUTH_TOKEN_URL]
--providers.generic-oauth.user-url= URL used to retrieve user info [$PROVIDERS_GENERIC_OAUTH_USER_URL]
--providers.generic-oauth.client-id= Client ID [$PROVIDERS_GENERIC_OAUTH_CLIENT_ID]
--providers.generic-oauth.client-secret= Client Secret [$PROVIDERS_GENERIC_OAUTH_CLIENT_SECRET]
--providers.generic-oauth.scope= Scopes (default: profile, email) [$PROVIDERS_GENERIC_OAUTH_SCOPE]
--providers.generic-oauth.token-style=[header|query] How token is presented when querying the User URL (default: header)
[$PROVIDERS_GENERIC_OAUTH_TOKEN_STYLE]
--providers.generic-oauth.resource= Optional resource indicator [$PROVIDERS_GENERIC_OAUTH_RESOURCE]

Help Options:
-h, --help Show this help message

选项下面几种方式提供,优先级从高到低

  1. 命令行参数,例如–auth-host
  2. 环境变量,用选项说明中环境变量的值设置,例如 –auth-host 可以用AUTH_HOST这个变量设置
  3. 文件,使用--config标志或$CONFIG环境变量指定文件位置,文件使用ini格式,例如 auth-host=xxxx.xxx.xx

选项详情

auth-host

​ 设置后,当用户从第三方提供商的身份验证返回时,他们将始终转发到该主机。

config

​ 用于指定配置文件的路径,可以设置多次,每个文件将按照传递的顺序进行读取。选项应以INI格式设置,例如:

1
url-path = _oauthpath

​ 如果您不在客户端和traefik之间使用HTTPS,则需要传递该insecure-cookie选项,

​ 设置时,如果用户成功完成身份验证,且需要身份验证的原始请求的主机是给定cookie域的子域,则将身份验证cookie设置为较高级别的cookie域。这意味着cookie可以允许访问多个子域,而无需重新验证。可指定多次。

​ 例如:

1
--cookie-domain = " example.com " --cookie-domain = " test.org "

例如,如果已经设置了cookie域test.com,并且app1.test.com上有一个请求,验证之后将为整个test.com域设置验证cookie。因此,如果从app2.test.com转发另一个请求进行身份验证,则将发送原始cookie,因此该请求将被允许,而无需进一步的身份验证。

default-action

指定请求不匹配任何规则时的行为。有效选项为authallow

默认值:(auth即所有请求都需要认证)

default-provider

设置用于身份验证的默认提供程序,可以在rules中覆盖它。有效选项当前为googleoidc

默认: google

domain

设置后,仅允许匹配给定域的用户访问。我们可以用来限制允许那些用户访问

例如,设置--domain=example.com --domain=test.org意味着仅允许example.com或test.org中的用户。所以thom@example.com将被允许,但thom@another.com不会。

lifetime

成功的身份验证会话应持续多长时间(以秒为单位)。

默认值:43200(12小时)

logout-redirect

设置后,用户将在注销后重定向到该URL。

url-path

自定义此服务用于在身份验证后处理回调的路径。

默认: /_oauth

secret

用于签署Cookie身份验证,应该是随机的(例如openssl rand -hex 16

whitelist

设置后,仅允许指定的用户。

例如,设置--whitelist=thom@example.com --whitelist=alice@example.com将意味着仅允许这两个确切的用户。所以thom@example.com将被允许,但john@example.com不会。

match-whitelist-or-domain

当启用时,如果用户匹配whitelistdomain参数,则允许他们使用。

注意事项

如果同时设置whitelistdomain,则默认仅whitelist被使用,并且domain将被忽略。如果需要同时设置whitelistdomain则应该设置match-whitelist-or-domain参数

操作模式

Overlay 模式

Overlay 是默认的操作模式。默认情况下使用/_oauth路径,可以使用url-path选项自定义。

用户流程为:

  1. 请求 www.myapp.com/home
  2. 用户已重定向到Google登录
  3. Google登录后,用户被重定向到 www.myapp.com/_oauth
  4. 令牌,用户和CSRF cookie均已验证(此请求已被拦截,并且从未传递给您的应用程序)
  5. 用户被重定向到 www.myapp.com/home
  6. 请求被允许

由于中的主机名redirect_uri是根据原始请求动态生成的,因此必须在Google OAuth控制台中允许每个主机名,例如上面的流程需要添加www.myapp.com/_oauth,如果有很多子域名使用这种比较麻烦,推荐使用auth-host

验证主机(auth-host)模式

这是一种可选的操作模式,在处理大量子域时非常有用,可以通过使用--auth-host选项激活

举例来说,如果你有几个应用:app1.test.comapp2.test.comappN.test.com,增加每一个域,以谷歌的控制台可以变得费力。要使用身份验证主机,请通过将Cookie域test.com设置为auth-host来允许域级别的Cookie,然后将设置为:auth.test.com

用户流将为:

  1. 请求 app10.test.com/home/page
  2. 用户已重定向到Google登录
  3. Google登录后,用户被重定向到 auth.test.com/_oauth
  4. 令牌,用户和CSRF cookie已通过验证,auth cookie已设置为 test.com
  5. 用户被重定向到 app10.test.com/home/page
  6. 请求被允许

使用此设置,只需要配置auth.test.com在Google控制台中允许。

auth-host要使用,必须满足两个条件:

  1. 请求匹配 cookie-domain
  2. auth-hostcookie-domain相同的子域

身份验证提供者

重定向的url

设置任何身份验证提供程序时,提供程序应配置有效/授权的“重定向URI” 重定向url 应该为要进行身份验证的主机名并加上url-path 定义的路径,例如(https://app.example.com/_oauth).

默认情况下,不使用验证主机模式时,需要设置每个主机(例如https://app1.example.com/_oauthhttps://app2.example.com/_oauth)如果正在使用验证主机模式,这将只是你的auth-host(如https://auth.example.com/_oauth

提供者

google

从开发者控制台获取客户端凭据:https : //console.developers.google.com 创建新项目,在搜索栏中选择“credentials”。填写“ OAuth Consent Screen”标签。点击“Create Credentials”>“ OAuth client ID”。选择“Web Application,填写您的应用程序的名称,跳过“Authorized JavaScript origins”,并填写“Authorized redirect URIs”

使用google提供程序需要设置providers.google.client-idproviders.google.client-secret配置选项。

oidc

使用oidc证明者并设置:

配置选项
providers.oidc.issuer-url oidc issuer-url 地址
providers.oidc.client-id oidc client id
`providers.oidc.client-secret oidc 客户端的secret

k8s中使用

这里使用gitlab 作为oidc提供者,我们在gitlab上创建一个应用

image-20200907213851369

创建一个

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#
# Traefik Forward Auth Deployment
#
apiVersion: apps/v1
kind: Deployment
metadata:
name: traefik-forward-auth
labels:
app: traefik-forward-auth
namespace: kube-system
spec:
replicas: 1
selector:
matchLabels:
app: traefik-forward-auth
strategy:
type: Recreate
template:
metadata:
labels:
app: traefik-forward-auth
spec:
serviceAccountName: traefik-ingress-controller
terminationGracePeriodSeconds: 60
containers:
- image: thomseddon/traefik-forward-auth:2
name: traefik-forward-auth
ports:
- containerPort: 4181
protocol: TCP
env:
- name: CONFIG
value: "/config"
- name: DOMAIN
value: "liangla.mobi"
# INSECURE_COOKIE is required if not using a https entrypoint
# - name: INSECURE_COOKIE
# value: "true"
# Remove COOKIE_DOMAIN if not using auth host mode
- name: COOKIE_DOMAIN
value: "baixue.fun"
- name: AUTH_HOST
value: "auth.baixue.fun"
- name: LOG_LEVEL
value: "trace"
- name: DEFAULT_PROVIDER
value: "oidc"
- name: PROVIDERS_OIDC_ISSUER_URL
value: "http://gitlab.baixue.fun"
- name: PROVIDERS_OIDC_CLIENT_ID
value: "94b56383b6bcxxxxxxxxxxxxxxxxxxxxxxxxx7430678d45c0e1003bbdcf3989f34ac86"
- name: PROVIDERS_OIDC_CLIENT_SECRET
value: "f0261fa40d97ffxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx71a33eda3ec375a4c3e694bb8e"
- name: SECRET
value: "2d7194490b3eeb001b5cb9ac8f07d462"

service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#
# Auth Service
#
apiVersion: v1
kind: Service
metadata:
name: traefik-forward-auth
labels:
app: traefik
namespace: kube-system
spec:
type: ClusterIP
selector:
app: traefik-forward-auth
ports:
- name: auth-http
port: 4181
targetPort: 4181

ingress

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: traefik-forward-auth
labels:
app: traefik
namespace: kube-system
spec:
entryPoints:
- websecure
routes:
- match: Host(`auth.baixue.fun`)
kind: Rule
services:
- name: traefik-forward-auth
port: 4181
middlewares:
- name: traefik-forward-auth
tls:
certResolver: myresolver

中间件

1
2
3
4
5
6
7
8
9
10
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
name: traefik-forward-auth
namespace: kube-system
spec:
forwardAuth:
address: http://traefik-forward-auth.kube-system.svc.cluster.local:4181
authResponseHeaders:
- X-Forwarded-User

我们创建一个demo 用于验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
apiVersion: apps/v1
kind: Deployment
metadata:
name: whoami
labels:
app: whoami
spec:
replicas: 1
selector:
matchLabels:
app: whoami
template:
metadata:
labels:
app: whoami
spec:
containers:
- name: whoami
image: containous/whoami
---
#
# Service
#
apiVersion: v1
kind: Service
metadata:
name: whoami
labels:
app: whoami
spec:
ports:
- name: http
port: 80
selector:
app: whoami

---
#
# IngressRoute
#
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: whoami
labels:
app: whoami
spec:
entryPoints:
- websecure
routes:
- match: Host(`jaeger.baixue.fun`)
kind: Rule
services:
- name: whoami
port: 80
middlewares:
- name: traefik-forward-auth
namespace: kube-system
tls:
certResolver: myresolver

我们访问https:/jaeger.baixue.fun 会自动跳转到gitlab上去验证

image-20200907214851552

image-20200907214908849

这里我们用lishuai 这个gitlab用户登录的,这个用户gitlab上的邮箱为liangla@lishuai.mobi符合我们domain的设置

image-20200907214725742

我们下面用gitlab 中的root 用户登录,这个用户的邮箱为912988434@qq.com,不符合domain,所以会被拒绝

image-20200907215143254

访问被限制

image-20200907215152905

查看header如下

image-20200907215247555

CATALOG
  1. 1. 使用traefik为服务配置sso
    1. 1.1. ForwardAuth
    2. 1.2. Traefik Forward Auth
      1. 1.2.1. 用法
      2. 1.2.2. 选项详情
        1. 1.2.2.1. auth-host
        2. 1.2.2.2. config
        3. 1.2.2.3. insecure-cookie
        4. 1.2.2.4. cookie-domain
        5. 1.2.2.5. default-action
        6. 1.2.2.6. default-provider
        7. 1.2.2.7. domain
        8. 1.2.2.8. lifetime
        9. 1.2.2.9. logout-redirect
        10. 1.2.2.10. url-path
        11. 1.2.2.11. secret
        12. 1.2.2.12. whitelist
        13. 1.2.2.13. match-whitelist-or-domain
      3. 1.2.3. 注意事项
      4. 1.2.4. 操作模式
        1. 1.2.4.1. Overlay 模式
        2. 1.2.4.2. 验证主机(auth-host)模式
    3. 1.3. 身份验证提供者
      1. 1.3.0.1. 重定向的url
      2. 1.3.0.2. 提供者
        1. 1.3.0.2.1. google
      3. 1.3.0.3. oidc
  2. 1.4. k8s中使用