使用反向代理

了解如何将 Keycloak 与反向代理、API 网关或负载均衡器一起配置。

分布式环境通常需要使用反向代理。Keycloak 提供了多种选项来安全地与此类环境集成。

要代理的端口

默认情况下,Keycloak 在以下端口上运行

  • 8443 (当您通过 --http-enabled=true 显式启用 HTTP 时为 8080)

  • 9000

端口 8443(如果启用了 HTTP,则为 8080)用于管理 UI、账户控制台、SAML 和 OIDC 端点以及管理 REST API,如配置主机名 (v2) 指南中所述。

端口 9000 用于管理,其中包括运行状况检查和指标的端点,如配置管理接口 指南中所述。

即使您为前端/后端和管理使用不同的主机名(如为生产环境配置 Keycloak 中所述),您也只需要代理端口 8443(或 8080)。您不应代理端口 9000,因为运行状况检查和指标直接使用这些端口,并且您不希望将此信息暴露给外部调用者。

配置反向代理标头

Keycloak 将根据 proxy-headers 选项解析反向代理标头,该选项接受多个值

  • 默认情况下,如果未指定该选项,则不解析任何反向代理标头。当不使用代理或使用 https 直通时,应使用此选项。

  • forwarded 启用对 Forwarded 标头的解析,如 RFC7239 中所述。

  • xforwarded 启用对非标准 X-Forwarded-* 标头的解析,例如 X-Forwarded-ForX-Forwarded-ProtoX-Forwarded-HostX-Forwarded-Port

如果您将反向代理用于 https 直通以外的任何用途,并且未设置 proxy-headers 选项,则默认情况下,您将看到通过代理执行源检查的请求的 403 Forbidden 响应。

例如

bin/kc.[sh|bat] start --proxy-headers forwarded
如果选择了 forwardedxforwarded,请确保您的反向代理正确设置并覆盖相应的 ForwardedX-Forwarded-* 标头。要设置这些标头,请查阅您的反向代理文档。请勿将 forwardedxforwarded 与 https 直通一起使用。错误配置会将 Keycloak 暴露于安全漏洞。

请格外小心,确保您的反向代理通过 ForwardedX-Forwarded-For 标头正确设置客户端地址。如果此标头配置不正确,恶意客户端可以设置此标头,并欺骗 Keycloak 认为客户端是从与实际地址不同的 IP 地址连接的。如果您对 IP 地址进行任何拒绝或允许列表操作,则此预防措施可能更为关键。

当使用 xforwarded 设置时,X-Forwarded-Port 优先于 X-Forwarded-Host 中包含的任何端口。
如果 TLS 连接在反向代理处终止(边缘终止),则需要通过 http-enabled 设置启用 HTTP。

反向代理上不同的上下文路径

Keycloak 假定它通过与 Keycloak 配置相同的上下文路径在反向代理下公开。默认情况下,Keycloak 通过根路径 (/) 公开,这意味着它期望在反向代理上也通过 / 公开。在这些情况下,您可以为 hostname 选项使用完整 URL,例如,如果 Keycloak 通过反向代理在 /auth 上公开,则可以使用 --hostname=https://my.keycloak.org/auth

有关在不同主机名或上下文路径(包括管理 REST API 和控制台)上公开 Keycloak 的更多详细信息,请参阅 配置主机名 (v2)

或者,您也可以更改 Keycloak 自身的上下文路径,以匹配反向代理使用的上下文路径,方法是使用 http-relative-path 选项,这将更改 Keycloak 自身的上下文路径,以匹配反向代理使用的上下文路径。

启用粘性会话

典型的集群部署由负载均衡器(反向代理)和私有网络上的 2 个或更多 Keycloak 服务器组成。出于性能目的,如果负载均衡器将与特定浏览器会话相关的所有请求转发到同一 Keycloak 后端节点,可能会很有用。

原因是,Keycloak 在底层使用 Infinispan 分布式缓存来保存与当前身份验证会话和用户会话相关的数据。Infinispan 分布式缓存配置了有限数量的所有者。这意味着会话相关数据仅存储在某些集群节点中,其他节点如果想要访问该数据,则需要远程查找数据。

例如,如果 ID 为 123 的身份验证会话保存在 node1 上的 Infinispan 缓存中,然后 node2 需要查找此会话,则它需要向 node1 发送请求,以通过网络返回特定的会话实体。

如果特定的会话实体始终在本地可用,则会很有利,这可以通过粘性会话来实现。具有公共前端负载均衡器和两个后端 Keycloak 节点的集群环境中的工作流程可能是这样的

  • 用户发送初始请求以查看 Keycloak 登录屏幕

  • 此请求由前端负载均衡器处理,负载均衡器将其转发到某个随机节点(例如 node1)。严格来说,节点不需要是随机的,但可以根据其他一些标准(客户端 IP 地址等)选择。这一切都取决于底层负载均衡器(反向代理)的实现和配置。

  • Keycloak 创建具有随机 ID(例如 123)的身份验证会话,并将其保存到 Infinispan 缓存。

  • Infinispan 分布式缓存根据会话 ID 的哈希值分配会话的主所有者。有关更多详细信息,请参阅 Infinispan 文档。假设 Infinispan 将 node2 分配为该会话的所有者。

  • Keycloak 创建 cookie AUTH_SESSION_ID,格式如 <会话 ID>.<所有者节点 ID>。在我们的示例中,它将是 123.node2。

  • 响应将返回给用户,其中包含 Keycloak 登录屏幕和浏览器中的 AUTH_SESSION_ID cookie

从此时起,如果负载均衡器将所有后续请求转发到 node2,则会很有利,因为 node2 是 ID 为 123 的身份验证会话的所有者,因此 Infinispan 可以在本地查找此会话。身份验证完成后,身份验证会话将转换为用户会话,用户会话也将保存在 node2 上,因为它具有相同的 ID 123。

粘性会话对于集群设置不是强制性的,但是出于上述原因,它对性能有好处。您需要配置负载均衡器以粘附在 AUTH_SESSION_ID cookie 上。进行此更改的适当过程取决于您的负载均衡器。

如果您的代理支持会话亲和性,而无需处理来自后端节点的 cookie,则应将 spi-sticky-session-encoder-infinispan-should-attach-route 选项设置为 false,以避免将节点附加到 cookie,而只需依赖反向代理的功能。

bin/kc.[sh|bat] start --spi-sticky-session-encoder-infinispan-should-attach-route=false

默认情况下,spi-sticky-session-encoder-infinispan-should-attach-route 选项值为 true,以便将节点名称附加到 cookie,以向反向代理指示应将后续请求发送到的节点。

公开路径建议

当使用反向代理时,Keycloak 只需要公开某些路径。下表显示了建议公开的路径。

Keycloak 路径 反向代理路径 已公开 原因

/

-

当公开所有路径时,不必要地公开了管理路径。

/admin/

-

公开管理路径会导致不必要的攻击向量。

/realms/

/realms/

此路径是正确工作所必需的,例如,对于 OIDC 端点。

/resources/

/resources/

此路径是正确提供资产所必需的。它可以从 CDN 而不是 Keycloak 路径提供。

/metrics

-

公开指标会导致不必要的攻击向量。

/health

-

公开运行状况检查会导致不必要的攻击向量。

我们假设您在反向代理/网关的公共 API 的根路径 / 上运行 Keycloak。如果不是,请在路径前添加您想要的路径。

受信任的代理

为确保仅使用来自您信任的代理的代理标头,请将 proxy-trusted-addresses 选项设置为逗号分隔的 IP 地址(IPv4 或 IPv6)或无类别域间路由 (CIDR) 表示法列表。

例如

bin/kc.[sh|bat] start --proxy-headers forwarded --proxy-trusted-addresses=192.168.0.32,127.0.0.0/8

PROXY 协议

proxy-protocol-enabled 选项控制服务器是否应在为来自代理后面的请求提供服务时使用 HA PROXY 协议。设置为 true 时,返回的远程地址将是来自实际连接客户端的地址。当使用 proxy-headers 选项时,该值不能为 true

当在兼容的 https 直通代理后面运行时,这很有用,因为请求标头无法被操纵。

例如

bin/kc.[sh|bat] start --proxy-protocol-enabled true

启用客户端证书查找

当代理配置为 TLS 终止代理时,客户端证书信息可以通过特定的 HTTP 请求标头转发到服务器,然后用于验证客户端身份。您可以根据您使用的代理配置服务器将如何检索客户端证书信息。

通过代理标头查找 X.509 身份验证的客户端证书被认为是安全敏感的。如果配置错误,伪造的客户端证书标头可用于身份验证。需要采取额外的预防措施,以确保通过代理标头传递的客户端证书信息是可信的。

  • 仔细检查您的用例是否需要重新加密或边缘 TLS 终止,这意味着使用代理标头进行客户端证书查找。当需要 X.509 身份验证时,建议使用 TLS 直通作为更安全的选择,因为它不需要通过代理标头传递证书。从代理标头查找客户端证书仅适用于重新加密和边缘 TLS 终止。

  • 如果直通不是一种选择,请实施以下安全措施

    • 配置您的网络,使 Keycloak 隔离,并且只能接受来自代理的连接。

    • 确保代理覆盖在 spi-x509cert-lookup-<provider>-ssl-client-cert 选项中配置的标头。

    • 特别注意 spi-x509cert-lookup-<provider>-trust-proxy-verification 设置。仅当您可以信任您的代理来验证客户端证书时,才确保启用它。在代理未验证客户端证书链的情况下设置 spi-x509cert-lookup-<provider>-trust-proxy-verification=true 会将 Keycloak 暴露于安全漏洞,因为伪造的客户端证书可用于身份验证。

服务器支持一些最常见的 TLS 终止代理,例如

代理 提供程序

Apache HTTP 服务器

apache

HAProxy

haproxy

NGINX

nginx

要配置如何从请求中检索客户端证书,您需要

启用相应的代理提供程序
bin/kc.[sh|bat] build --spi-x509cert-lookup-provider=<provider>
配置 HTTP 标头
bin/kc.[sh|bat] start --spi-x509cert-lookup-<provider>-ssl-client-cert=SSL_CLIENT_CERT --spi-x509cert-lookup-<provider>-ssl-cert-chain-prefix=CERT_CHAIN --spi-x509cert-lookup-<provider>-certificate-chain-length=10

配置 HTTP 标头时,您需要确保您使用的值与代理使用客户端证书信息转发的标头的名称相对应。

用于配置提供程序的可用选项包括

选项 描述

ssl-client-cert

持有客户端证书的标头的名称

ssl-cert-chain-prefix

持有链中其他证书的标头的前缀,并用于根据链的长度检索单个证书。例如,值 CERT_CHAIN 将告诉服务器从标头 CERT_CHAIN_0CERT_CHAIN_9 加载其他证书(如果 certificate-chain-length 设置为 10)。

certificate-chain-length

证书链的最大长度。

trust-proxy-verification

启用信任 NGINX 代理证书验证,而不是将证书转发到 Keycloak 并在 Keycloak 中验证它。

cert-is-url-encoded

转发的证书是否进行 URL 编码。在 NGINX 中,这对应于 $ssl_client_cert$ssl_client_escaped_cert 变量。这也可用于 Traefik PassTlsClientCert 中间件,因为它发送未编码的客户端证书。

配置 NGINX 提供程序

NGINX SSL/TLS 模块不公开客户端证书链。Keycloak 的 NGINX 证书查找提供程序通过使用 Keycloak 信任存储重建它。

如果您正在使用此提供程序,请参阅 配置受信任的证书,了解如何配置 Keycloak 信任存储。

相关选项

hostname

服务器公开的地址。

可以是完整 URL,也可以只是主机名。当仅提供主机名时,从请求中解析方案、端口和上下文路径。

CLI: --hostname
Env: KC_HOSTNAME

仅当启用 hostname:v2 功能时可用

hostname-admin

用于访问管理控制台的地址。

如果您使用与 hostname 选项中指定的地址不同的地址上的反向代理公开管理控制台,请使用此选项。

CLI: --hostname-admin
Env: KC_HOSTNAME_ADMIN

仅当启用 hostname:v2 功能时可用

http-relative-path

设置相对于 / 的路径以提供资源。

路径必须以 / 开头。

CLI: --http-relative-path
Env: KC_HTTP_RELATIVE_PATH

/ (默认)

proxy-headers

服务器应接受的代理标头。

错误配置可能会使服务器暴露于安全漏洞。优先于已弃用的 proxy 选项。

CLI: --proxy-headers
Env: KC_PROXY_HEADERS

forwarded, xforwarded

proxy-protocol-enabled

服务器是否应在为来自代理后面的请求提供服务时使用 HA PROXY 协议。

设置为 true 时,返回的远程地址将是来自实际连接客户端的地址。当使用 proxy-headers 时无法启用。

CLI: --proxy-protocol-enabled
Env: KC_PROXY_PROTOCOL_ENABLED

true, false (默认)

proxy-trusted-addresses

受信任代理地址的逗号分隔列表。

如果设置,则将忽略来自其他地址的代理标头。默认情况下,所有地址都受信任。受信任的代理地址指定为 IP 地址(IPv4 或 IPv6)或无类别域间路由 (CIDR) 表示法。仅当设置 proxy-headers 时可用。

CLI: --proxy-trusted-addresses
Env: KC_PROXY_TRUSTED_ADDRESSES

在此页上