<span class="nav-link-with-icon">OIDC <img src="https://doc.traefik.io/traefik-hub/img/ps-traefik-hub-logo-light.svg" class="menu-icon" alt="Traefik Hub API Gateway"></span>
Traefik Hub 功能
此中间件仅在 Traefik Hub 中提供。了解更多 Traefik Hub 高级功能。
OIDC 认证中间件将认证委托给外部提供商,从而保护应用。
配置示例¶
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: test-oidc
namespace: whoami
spec:
plugin:
oidc:
issuer: "https://tenant.auth0.com/realms/myrealm"
redirectUrl: "/callback"
clientId: "urn:k8s:secret:my-secret:clientId"
clientSecret: "urn:k8s:secret:my-secret:clientSecret"
session:
name: customsessioncookiename
sliding: false
refresh: false
expiry: 10
sameSite: none
httpOnly: false
secure: true
stateCookie:
name: customstatecookiename
maxAge: 10
sameSite: none
httpOnly: true
secure: true
forwardHeaders:
Group: grp
Expires-At: exp
claims: Equals(`grp`, `admin`)
csrf: {}apiVersion: v1
kind: Secret
metadata:
name: my-secret
stringData:
clientID: my-oidc-client-name
clientSecret: mysecret配置选项¶
| 字段 | 说明 | 默认值 | 必填 |
|---|---|---|---|
issuer |
定义 OpenID Connect 提供商的 URL(例如 https://accounts.google.com)。应指向提供 OpenID Connect 配置的服务器。 |
"" | 是 |
trustedIssuer |
定义另一个可信签发者 URL,除了从 OpenID Connect 提供商发现的签发者之外,也接受此 URL 进行验证。 | "" | 否 |
disableIssuerCheck |
在令牌验证期间禁用签发者验证检查。 | false | 否 |
redirectUrl |
定义授权完成后 OpenID Connect 提供商重定向回中间件时使用的 URL。(更多信息见此处) | "" | 是 |
clientId |
定义 OpenID Connect 提供商中某个账户的唯一客户端标识符;设置 clientSecret 时必须设置。(更多信息见此处) |
"" | 是 |
clientSecret |
定义 OpenID Connect 提供商中某个账户的唯一客户端密钥;设置 clientId 时必须设置。(更多信息见此处) |
"" | 是 |
claims |
定义为授权请求而需要验证的声明。claims 选项仅可用于 JWT 格式的令牌。(更多信息见此处) |
"" | 否 |
usernameClaim |
定义需要求值的声明,以填充访问日志中的 clientusername。usernameClaim 选项仅可用于 JWT 格式的令牌。 |
"" | 否 |
forwardHeaders |
定义要添加到请求中的 HTTP 头,并使用从授权服务器返回的访问令牌声明中提取的值填充。 如果 JWT 中找不到要转发的声明,相应头的值为空。 forwardHeaders 选项仅可用于 JWT 格式的令牌。 |
[] | 否 |
clientConfig.tls.ca |
PEM 编码的证书包,或引用包含此证书包的 Secret 的 URN;该证书包用于与授权服务器建立 TLS 连接。(更多信息见此处) | "" | 否 |
clientConfig.tls.cert |
PEM 编码的证书,或引用包含此证书的 Secret 的 URN;该证书用于与 Vault 服务器建立 TLS 连接。(更多信息见此处) | "" | 否 |
clientConfig.tls.key |
PEM 编码的密钥,或引用包含此密钥的 Secret 的 URN;该密钥用于与 Vault 服务器建立 TLS 连接。(更多信息见此处) | "" | 否 |
clientConfig.tls.insecureSkipVerify |
与授权服务器通信时禁用 TLS 证书验证。 可用于测试,但强烈不建议在生产环境使用。(更多信息见此处) |
"" | 否 |
clientConfig.timeoutSeconds |
定义向授权服务器发出请求后,放弃等待之前的时长。 | 5 | 否 |
clientConfig.maxRetries |
定义向授权服务器发送请求失败时的重试次数。 | 3 | 否 |
pkce |
定义 RFC 7636 中描述的代码交换证明密钥(Proof Key for Code Exchange)。 | false | 否 |
discoveryParams |
任意查询参数的映射,将在发现机制执行期间添加到 openid-configuration 的 well-known URI。 | "" | 否 |
scopes |
要请求的作用域,必须包含 openid。 |
openid | 否 |
authParams |
要传给认证提供商的任意查询参数映射。 当 AuthParams 中的 prompt 键设置为空字符串时,OAuth2 授权 URL 不会添加 prompt 参数,这意味着不会提示用户同意授权。 |
"" | 否 |
disableLogin |
禁用到认证提供商的重定向。 保护 API 时,如果不希望重定向到登录页面,此选项很有用。 |
false | 否 |
loginUrl |
定义需要时用于启动授权的 URL。 其他尚未授权的请求均返回 401 Unauthorized。留空时,所有请求都可以启动授权。 可以是路径(例如 /login)、主机加路径(example.com/login),或完整 URL(https://example.com/login)。仅支持 http 和 https 协议方案。 |
"" | 否 |
logoutUrl |
定义用于删除会话、使用户退出登录的 URL。 可以是路径(例如 /logout)、主机加路径(example.com/logout),或完整 URL(https://example.com/logout)。仅支持 http 和 https 协议方案。 |
"" | 否 |
postLoginRedirectUrl |
如果设置并与 loginUrl 配合使用,中间件将在登录成功后重定向到此 URL。可以是路径(例如 /after/login)、主机加路径(example.com/after/login),或完整 URL(https://example.com/after/login)。仅支持 http 和 https 协议方案。 |
"" | 否 |
postLogoutRedirectUrl |
如果设置并与 logoutUrl 配合使用,中间件将在退出登录后重定向到此 URL。可以是路径(例如 /after/logout)、主机加路径(example.com/after/logout),或完整 URL(https://example.com/after/logout)。仅支持 http 和 https 协议方案。 |
"" | 否 |
backchannelLogoutUrl |
定义用户退出登录时 OIDC 提供商调用的 URL(参见 https://openid.net/specs/openid-connect-rpinitiated-1_0.html#OpenID.BackChannel)。 可以是路径(例如 /backchannel-logout)、主机加路径(example.com/backchannel-logout),或完整 URL(https://example.com/backchannel-logout)。仅支持 http 和 https 协议方案。此功能目前处于实验阶段,且仅使用 Keycloak OIDC 提供商进行过测试。 |
"" | 否 |
backchannelLogoutSessionsRequired |
指定 OIDC 提供商是否在 Logout Token 中包含 sid(会话 ID)声明,用于识别用户会话(参见 https://openid.net/specs/openid-connect-backchannel-1_0.html#BCRegistration)。 省略时默认为 false。 此功能目前处于实验阶段,且仅使用 Keycloak OIDC 提供商进行过测试。 |
false | 否 |
stateCookie.name |
定义状态 Cookie 的名称。 | "MIDDLEWARE_NAME-state" |
否 |
stateCookie.path |
定义请求 URL 中必须存在的 URL 路径,只有满足时才发送 Cookie 头。%x2F('/')字符被视为目录分隔符,子目录也会匹配。例如, stateCookie.path 设置为 /docs 时,这些路径均匹配:/docs、/docs/web/、/docs/web/http。 |
"/" | 否 |
stateCookie.domain |
定义允许接收 Cookie 的主机。 如果指定此项,始终包含其子域名。 例如,设为 example.com 时,Cookie 也会发送到 api.example.com 等子域名。 |
"" | 否 |
stateCookie.maxAge |
定义状态 Cookie 多少秒后过期。 零或负数会使 Cookie 立即过期。 |
600 | 否 |
stateCookie.sameSite |
告知浏览器如何在跨站请求中处理状态 Cookie。 设为 lax 或 strict 可提供一定的跨站请求伪造(CSRF)攻击防护。更多信息见此处。 |
lax | 否 |
stateCookie.httpOnly |
禁止 JavaScript 访问 Cookie。 例如,通过 Document.cookie 属性、XMLHttpRequest API 或 Request API。这有助于缓解跨站脚本(XSS)攻击。 |
true | 否 |
stateCookie.secure |
定义是否仅在使用 https 协议方案发出请求时,才将状态 Cookie 发送给服务器。 |
false | 否 |
session.name |
会话 Cookie 的名称。 | "MIDDLEWARE_NAME-session" |
否 |
session.path |
定义请求 URL 中必须存在的 URL 路径,只有满足时才发送 Cookie 头。%x2F('/')字符被视为目录分隔符,子目录也会匹配。例如, stateCookie.path 设置为 /docs 时,这些路径均匹配:/docs、/docs/web/、/docs/web/http。 |
"/" | 否 |
session.domain |
指定允许接收 Cookie 的主机。如果指定此项,始终包含其子域名。如果指定此项,始终包含其子域名。 例如,设为 example.com 时,Cookie 也会发送到 api.example.com 等子域名。 |
"" | 否 |
session.expiry |
会话多少秒后过期。禁止使用零或负数。 | 86400 (24h) | 否 |
session.sliding |
强制中间件在每次收到已认证的请求时续期会话 Cookie。 | true | 否 |
session.refresh |
启用访问令牌到期时的刷新功能。 | true | 否 |
session.sameSite |
告知浏览器如何在跨站请求中处理会话 Cookie。 设为 lax 或 strict 可提供一定的跨站请求伪造(CSRF)攻击防护。更多信息见此处。 |
lax | 否 |
session.httpOnly |
禁止 JavaScript 访问 Cookie。 例如,通过 Document.cookie 属性、XMLHttpRequest API 或 Request API。这有助于缓解跨站脚本(XSS)攻击。 |
true | 否 |
session.secure |
定义是否仅在使用 https 协议方案发出请求时,才将会话 Cookie 发送给服务器。 |
false | 否 |
session.store.redis.endpoints |
要连接的 Redis 实例端点(例如 redis.traefik-hub.svc.cluster.local:6379)。 |
"" | 是 |
session.store.redis.username |
Traefik Hub 连接 Redis 时使用的用户名。 | "" | 否 |
session.store.redis.password |
Traefik Hub 连接 Redis 时使用的 password。 | "" | 否 |
session.store.redis.database |
Traefik Hub 用于存储信息的数据库(默认值:0)。 |
"" | 否 |
session.store.redis.cluster |
启用 Redis Cluster。 | "" | 否 |
session.store.redis.tls.caBundle |
自定义 CA 证书包。 | "" | 否 |
session.store.redis.tls.cert |
TLS 证书。 | "" | 否 |
session.store.redis.tls.key |
TLS 密钥。 | "" | 否 |
session.store.redis.tls.insecureSkipVerify |
允许跳过 TLS 验证。 | "" | 否 |
session.store.redis.sentinel.masterSet |
用于主节点选择的主节点集合名称。使用 Sentinel 时必填。 | "" | 否 |
session.store.redis.sentinel.username |
Sentinel 认证使用的用户名(可以与 username 不同)。 |
"" | 否 |
session.store.redis.sentinel.password |
Sentinel 认证使用的密码(可以与 password 不同)。 |
"" | 否 |
session.store.keyPrefix |
定义用于存储会话的条目的键前缀。 | "" | 否 |
csrf |
启用后,将名为 hub-csrf-token 的 CSRF Cookie 绑定到 OIDC 会话,以保护服务免受 CSRF 攻击。此功能基于 OWASP 基金会定义的签名双重提交 Cookie实现。 更多信息见此处。 |
"" | 否 |
csrf.secure |
定义是否仅在使用 https 协议方案发出请求时,才将 CSRF Cookie 发送给服务器。 |
false | 否 |
csrf.headerName |
定义用于发送 CSRF 令牌值的请求头名称,该令牌值来自先前收到的 CSRF Cookie。 | Hub-Csrf-Token | 否 |
redirectUrl 重定向 URL¶
在 IngressRoute 上添加特定规则¶
此 URL 告知 OpenID Connect 提供商如何返回中间件。 如果路由器规则接受某个域名下的所有路径,则无需额外操作。 如果路由器规则对允许的路径有限制,则应将此选项设置的路径包含在规则中。
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: whoami
spec:
entryPoints:
- web
- websecure
routes:
# Rules to match the loginUrl and redirectUrl can be added into
# your current router.
- match: Path(`/myapi`) || Path(`/login`) || Path(`/callback`)
kind: Rule
middlewares:
- name: test-oidc此 URL 不会传递给上游应用,而由中间件自身处理。 因此,所选 URL 不应与上游应用需要使用的任何 URL 冲突。
有时还需要在 OpenID Connect 提供商的配置中设置此 URL,例如 Google Accounts。
它可以是绝对 URL、相对于协议的 URL(继承请求协议),或相对于域名的 URL(继承请求域名和协议)。 参阅以下示例。
继承请求的协议与域名,使用 Redirecturl 的路径¶
| 请求 URL | RedirectURL | 结果 |
|---|---|---|
http://expl.co |
/cback |
http://expl.co/cback |
继承请求的协议,使用 Redirecturl 的域名与路径¶
| 请求 URL | RedirectURL | 结果 |
|---|---|---|
https://scur.co |
expl.co/cback |
https://expl.co/cback |
使用绝对重定向 URL 替换请求 URL¶
| 请求 URL | RedirectURL | 结果 |
|---|---|---|
https://scur.co |
http://expl.co/cback |
http://expl.co/cback |
支持的协议方案
仅支持 http 和 https 协议方案。
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: test-oidc
spec:
plugin:
oidc:
issuer: "https://tenant.auth0.com/realms/myrealm"
redirectUrl: "/callback"
clientId: my-oidc-client-name
clientSecret: mysecretclientId 与 clientSecret¶
在 Kubernetes Secret 中存储机密值¶
配置 clientId 和 clientSecret 时,可以引用与 Middleware 位于同一命名空间中的 Kubernetes Secret。
Kubernetes Secret 的引用采用 URN 格式:
urn:k8s:secret:[name]:[valueKey]
claims 声明¶
语法¶
claims 支持以下函数:
| 函数 | 说明 | 示例 |
|---|---|---|
| Equals | 验证 key 中的值是否等于 value。 |
Equals(`grp`, `admin`) |
| Prefix | 验证 key 中的值是否以 value 为前缀。 |
Prefix(`referrer`, `http://example.com`) |
| Contains(字符串) | 验证 key 中的值是否包含 value。 |
Contains(`referrer`, `/foo/`) |
| Contains(数组) | 验证 key 数组是否包含 value。 |
Contains(`areas`, `home`) |
| SplitContains | 验证 key 中的值按分隔符拆分后是否包含 value。 |
SplitContains(`scope`, ` `, `writer`) |
| OneOf | 验证 key 数组是否包含 values 中的任意一个值。 |
OneOf(`areas`, `office`, `lab`) |
所有函数都可以通过布尔运算符连接。支持以下运算符:
| 运算符 | 说明 | 示例 |
|---|---|---|
| && | 比较两个函数,仅当二者都求值为 true 时返回 true。 | Equals(`grp`, `admin`) && Equals(`active`, `true`) |
| || | 比较两个函数,只要任意一个求值为 true 就返回 true。 | Equals(`grp`, `admin`) || Equals(`active`, `true`) |
| ! | 函数为 true 时返回 false,否则返回 true。 | !Equals(`grp`, `testers`) |
对于以下数据结构,所有示例都会返回 true:
{
"active": true,
"grp": "admin",
"scope": "reader writer deploy",
"referrer": "http://example.com/foo/bar",
"areas": [
"office",
"home"
]
}嵌套声明¶
在键之间使用 . 可以支持嵌套声明。例如:
user.name{
"active": true,
"grp": "admin",
"scope": "reader writer deploy",
"referrer": "http://example.com/foo/bar",
"areas": [
"office",
"home"
],
"user" {
"name": "John Snow",
"status": "undead"
}
}John Snow处理包含 '.' 的键
如果 key 包含点号,可以使用 \. 转义。
处理包含 '\' 的键
如果 key 包含 \,则需要写成两个:\\。
访问令牌与 ID 令牌的声明
函数的第一个参数表示要在令牌声明中查找的键,可以通过前缀指定要检查两种令牌中的哪一种。
可用前缀为 id_token. 和 access_token.。不指定前缀时,默认检查 ID 令牌。
| 示例 | 说明 |
|---|---|
| Equals(`id_token.grp`, `admin`) | 检查 ID 令牌中 grp 声明的值是否为 admin。 |
| Prefix(`access_token.referrer`, `http://example.com`) | 检查访问令牌中 referrer 声明的值是否以 http://example.com\ 为前缀。 |
| OneOf(`areas`, `office`, `lab`) | 检查 ID 令牌中 areas 声明的值是否为 office 或 labs。 |
clientConfig 客户端配置¶
定义 API 网关连接身份提供商等第三方软件时使用的配置。
clientConfig.tls TLS 配置¶
在 Kubernetes Secret 中存储机密值¶
配置 tls.ca、tls.cert、tls.key 时,可以引用与 Middleware 位于同一命名空间中的 Kubernetes Secret。
Kubernetes Secret 的引用采用 URN 格式:
urn:k8s:secret:[name]:[valueKey]
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: test-oidc
spec:
plugin:
oidc:
clientConfig:
tls:
ca: "urn:k8s:secret:tls:ca"
cert: "urn:k8s:secret:tls:cert"
key: "urn:k8s:secret:tls:key"
insecureSkipVerify: trueapiVersion: v1
kind: Secret
metadata:
name: tls
stringData:
ca: |-
-----BEGIN CERTIFICATE-----
MIIB9TCCAWACAQAwgbgxGTAXBgNVBAoMEFF1b1ZhZGlzIExpbWl0ZWQxHDAaBgNV
BAsME0RvY3VtZW50IERlcGFydG1lbnQxOTA3BgNVBAMMMFdoeSBhcmUgeW91IGRl
Y29kaW5nIG1lPyAgVGhpcyBpcyBvbmx5IGEgdGVzdCEhITERMA8GA1UEBwwISGFt
aWx0b24xETAPBgNVBAgMCFBlbWJyb2tlMQswCQYDVQQGEwJCTTEPMA0GCSqGSIb3
DQEJARYAMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCJ9WRanG/fUvcfKiGl
EL4aRLjGt537mZ28UU9/3eiJeJznNSOuNLnF+hmabAu7H0LT4K7EdqfF+XUZW/2j
RKRYcvOUDGF9A7OjW7UfKk1In3+6QDCi7X34RE161jqoaJjrm/T18TOKcgkkhRzE
apQnIDm0Ea/HVzX/PiSOGuertwIDAQABMAsGCSqGSIb3DQEBBQOBgQBzMJdAV4QP
Awel8LzGx5uMOshezF/KfP67wJ93UW+N7zXY6AwPgoLj4Kjw+WtU684JL8Dtr9FX
ozakE+8p06BpxegR4BR3FMHf6p+0jQxUEAkAyb/mVgm66TyghDGC6/YkiKoZptXQ
98TwDIK/39WEB/V607As+KoYazQG8drorw==
-----END CERTIFICATE-----
cert: |-
-----BEGIN CERTIFICATE-----
MIIB9TCCAWACAQAwgbgxGTAXBgNVBAoMEFF1b1ZhZGlzIExpbWl0ZWQxHDAaBgNV
BAsME0RvY3VtZW50IERlcGFydG1lbnQxOTA3BgNVBAMMMFdoeSBhcmUgeW91IGRl
Y29kaW5nIG1lPyAgVGhpcyBpcyBvbmx5IGEgdGVzdCEhITERMA8GA1UEBwwISGFt
aWx0b24xETAPBgNVBAgMCFBlbWJyb2tlMQswCQYDVQQGEwJCTTEPMA0GCSqGSIb3
DQEJARYAMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCJ9WRanG/fUvcfKiGl
EL4aRLjGt537mZ28UU9/3eiJeJznNSOuNLnF+hmabAu7H0LT4K7EdqfF+XUZW/2j
RKRYcvOUDGF9A7OjW7UfKk1In3+6QDCi7X34RE161jqoaJjrm/T18TOKcgkkhRzE
apQnIDm0Ea/HVzX/PiSOGuertwIDAQABMAsGCSqGSIb3DQEBBQOBgQBzMJdAV4QP
Awel8LzGx5uMOshezF/KfP67wJ93UW+N7zXY6AwPgoLj4Kjw+WtU684JL8Dtr9FX
ozakE+8p06BpxegR4BR3FMHf6p+0jQxUEAkAyb/mVgm66TyghDGC6/YkiKoZptXQ
98TwDIK/39WEB/V607As+KoYazQG8drorw==
-----END CERTIFICATE-----
key: |-
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIC8CsJ/B115S+JtR1/l3ZQwKA3XdXt9zLqusF1VXc/KloAoGCCqGSM49
AwEHoUQDQgAEpwUmRIZHFt8CdDHYm1ikScCScd2q6QVYXxJu+G3fQZ78ScGtN7fu
KXMnQqVjXVRAr8qUY8yipVKuMCepnPXScQ==
-----END EC PRIVATE KEY-----sameSite 可接受的值¶
none:浏览器会在跨站请求和同站请求中都发送 Cookie。strict:浏览器仅在同站请求(源自设置该 Cookie 的站点的请求)中发送 Cookie。 如果请求来自与当前位置 URL 不同的 URL,则不会包含任何标记为strict的 Cookie。lax:同站 Cookie 不会随跨站子请求(例如加载图片或框架的调用)发送,但用户从外部站点导航到该 URL 时会发送,例如点击链接。
session.store 会话存储¶
OpenID Connect 认证中间件可以使用持久化 KV 存储保存 HTTP 会话数据,
而不是将所有状态都保存在 Cookie 中。
这能避免 Cookie 变得过大而引发延迟问题。
参阅 Redis 选项来配置 Redis 连接。
Redis 服务器的连接参数配置在 Middleware 部署中。
支持以下 Redis 模式:
- 单实例模式
- Redis Cluster 集群模式
- Redis Sentinel 哨兵模式
提示
如果使用 Redis 单实例模式或 Redis Sentinel,可以配置 database 字段。
如果使用 Redis Cluster,此值不会生效(仅可使用数据库 0)。
此时将显示一条警告,并忽略该值。
更多 Redis 信息,建议参阅 Redis 官方文档。
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: test-oidc
spec:
plugin:
oidc:
issuer: "https://tenant.auth0.com/realms/myrealm"
redirectUrl: "/callback"
clientId: my-oidc-client-name
clientSecret: mysecret
session:
store:
redis:
endpoints:
- redis-master.traefik-hub.svc.cluster.local:6379
password: "urn:k8s:secret:oidc:redisPass"apiVersion: v1
kind: Secret
metadata:
name: oidc
stringData:
redisPass: mysecret12345678csrf 跨站请求伪造防护¶
CSRF 内部行为¶
OIDC 会话过期时,对应的 CSRF Cookie 会被删除。 这意味着,每当会话刷新或重建时,都会生成新的 CSRF 令牌并发送给客户端。
当请求使用非安全方法时(参见 RFC7231#section-4.2.1), 必须通过 headerName 选项配置的请求头,将 CSRF 令牌值(从 Cookie 提取)发送给服务器。
在生产环境使用 Traefik OSS?
如果你在工作中使用 Traefik,可以考虑为 Traefik OSS 增加企业级 API 网关能力或商业支持。
为 Traefik OSS 增加 API 网关能力快捷且平滑,无需推倒重来,现有配置也会完整保留。请通过这个简短视频了解实际效果。