From 71192ca98830d43aa0c5fade14c152ee612fcc22 Mon Sep 17 00:00:00 2001 From: Pedro Ruivo Date: Tue, 21 Apr 2026 20:19:59 +0100 Subject: [PATCH] HAProxy tls-passthrough blueprint Closes #48000 Signed-off-by: Pedro Ruivo <1492066+pruivo@users.noreply.github.com> Signed-off-by: Alexander Schwartz Co-authored-by: Pedro Ruivo <1492066+pruivo@users.noreply.github.com> Co-authored-by: Alexander Schwartz --- docs/guides/server/haproxy-passthrough.adoc | 121 +++++++++++ docs/guides/server/pinned-guides | 1 + docs/guides/server/reverseproxy.adoc | 218 ++++++++++++-------- 3 files changed, 255 insertions(+), 85 deletions(-) create mode 100644 docs/guides/server/haproxy-passthrough.adoc diff --git a/docs/guides/server/haproxy-passthrough.adoc b/docs/guides/server/haproxy-passthrough.adoc new file mode 100644 index 00000000000..beb267ebdab --- /dev/null +++ b/docs/guides/server/haproxy-passthrough.adoc @@ -0,0 +1,121 @@ +<#import "/templates/guide.adoc" as tmpl> +<#import "/templates/kc.adoc" as kc> +<#import "/templates/options.adoc" as opts> +<#import "/templates/links.adoc" as links> + +<@tmpl.guide title="HAProxy with TLS passthrough" +summary="Configure HAProxy as a TLS passthrough load balancer for {project_name}." +includedOptions="proxy-protocol-enabled metrics-enabled health-enabled" +tileVisible="false"> + +This {section} describes how to configure link:https://www.haproxy.com/[HAProxy] as a TLS passthrough load balancer in front of {project_name}. + +In TLS passthrough mode, HAProxy forwards the raw TLS connection directly to {project_name} without terminating it. +HAProxy operates at the TCP layer (Layer 4) and has no visibility into the HTTP content. +The TLS handshake occurs between the client and {project_name}, preserving end-to-end encryption. + +For a general overview of TLS passthrough and how it compares to re-encrypt, see the <@links.server id="reverseproxy"/> {section}. + +A ready-to-run example with Docker Compose is available in the link:https://github.com/keycloak/keycloak-quickstarts/tree/main/proxy/haproxy/passthrough[quickstart repository]. + +== HAProxy configuration + +The following `haproxy.cfg` shows a configuration for TLS passthrough with two {project_name} backend servers. + +[source,haproxy] +---- +global + log stdout format raw local0 + +defaults + log global + mode http + option httplog + option dontlognull + timeout connect 5s + timeout client 50s + timeout server 50s + retries 3 + +frontend https_front + bind *:8443 # <1> + mode tcp # <2> + option tcplog + default_backend keycloak_back + +backend keycloak_back + mode tcp # <3> + balance roundrobin # <4> + option httpchk GET /health/ready # <5> + http-check expect status 200 + server keycloak1 keycloak1:8443 send-proxy-v2 check port 9000 check-ssl verify none inter 5s fall 3 rise 2 # <6> + server keycloak2 keycloak2:8443 send-proxy-v2 check port 9000 check-ssl verify none inter 5s fall 3 rise 2 +---- + +<1> The port HAProxy listens on for incoming TLS connections. +Clients connect to this port, and HAProxy forwards the raw TCP stream to the backend. +<2> Enables link:https://docs.haproxy.org/3.2/configuration.html#4-mode[TCP mode] on the frontend. +This tells HAProxy to operate at Layer 4, forwarding raw bytes without decrypting TLS. +HAProxy never sees the plaintext HTTP traffic in this mode. +<3> The backend must also use TCP mode to match the frontend. +<4> Distributes connections across backend servers using link:https://docs.haproxy.org/3.2/configuration.html#4-balance[round-robin] load balancing. +<5> Configures link:https://docs.haproxy.org/3.2/configuration.html#4.2-option%20httpchk[HTTP health checks] against {project_name}'s readiness endpoint. +Even though the data traffic uses TCP mode, HAProxy can perform HTTP health checks on a separate port. +The `http-check expect status 200` directive tells HAProxy to consider the server healthy only when it receives an HTTP 200 response. +For details on the {project_name} health check endpoint, see the <@links.server id="management-interface"/> {section}. +<6> Defines a backend {project_name} server. +The parameters on this line control PROXY protocol, health checks, and failure detection: + +The `server` directive parameters are explained below: + +`send-proxy-v2`:: +Enables the link:https://docs.haproxy.org/3.2/configuration.html#5.2-send-proxy-v2[PROXY protocol v2]. +HAProxy prepends the original client IP address to the TCP connection so that {project_name} sees the real source IP instead of HAProxy's address. +This requires {project_name} to be configured with `--proxy-protocol-enabled=true` (see <>). +Version 1 (`send-proxy`) is also supported. + +`check port 9000 check-ssl verify none`:: +Directs health checks to the link:https://docs.haproxy.org/3.2/configuration.html#5.2-check[management port] (`9000`) over HTTPS. +The `verify none` option skips TLS certificate verification for the health check connection, which is acceptable because the health check is an internal communication between HAProxy and {project_name}. +The management port is separate from the main application port (`8443`), as described in the <@links.server id="management-interface"/> {section}. + +`inter 5s fall 3 rise 2`:: +Configures the link:https://docs.haproxy.org/3.2/configuration.html#5.2-inter[health check frequency]: +poll every 5 seconds, mark a server as down after 3 consecutive failures, and mark it as up again after 2 consecutive successes. +These values affect how quickly HAProxy detects that a {project_name} instance is shutting down (see <>). + +[[keycloak-configuration]] +== {project_name} configuration + +With TLS passthrough, {project_name} requires the following configuration: + +<@kc.start parameters="--proxy-protocol-enabled true --health-enabled true --metrics-enabled true"/> + +`--proxy-protocol-enabled true`:: +Enables the PROXY protocol so that {project_name} can read the real client IP address from the PROXY protocol header added by HAProxy. +Do not set `--proxy-headers` when using TLS passthrough because HAProxy cannot inject HTTP headers into the encrypted traffic. +The `--proxy-protocol-enabled` option and the `--proxy-headers` option are mutually exclusive. + +`--health-enabled true`:: +Enables the health check endpoints on the management port (`9000`). +Without this, the `/health/ready` endpoint that HAProxy polls will not be available, and HAProxy will mark all {project_name} instances as down. + +`--metrics-enabled true`:: +Enables the metrics endpoints on the management port. +Enabling metrics also enables an additional status check for the database, so it is recommended to enable metrics. + +[[graceful-shutdown-considerations]] +== Graceful shutdown considerations + +With TLS passthrough, HAProxy cannot signal a connection close at the HTTP level. +The health check timing directly determines how long it takes HAProxy to detect that a {project_name} instance is shutting down and stop routing new connections to it. + +With the health check settings from the configuration above (`inter 5s fall 3`), it takes up to 15 seconds (3 failures x 5-second interval) for HAProxy to mark a {project_name} instance as down. +During this period, {project_name} must remain running to serve in-flight requests. +Therefore, configure the `--shutdown-delay` to be at least as long as the detection time: + +<@kc.start parameters="--proxy-protocol-enabled true --health-enabled true --metrics-enabled true --shutdown-delay=30s"/> + +For a detailed explanation of shutdown phases and how to tune the delay and timeout values, see the Graceful HTTP shutdown section in the <@links.server id="reverseproxy"/> {section}. + + diff --git a/docs/guides/server/pinned-guides b/docs/guides/server/pinned-guides index e73ee5e856e..ee70da1cb0e 100644 --- a/docs/guides/server/pinned-guides +++ b/docs/guides/server/pinned-guides @@ -7,6 +7,7 @@ containers enabletls hostname reverseproxy +haproxy-passthrough db caching outgoinghttp diff --git a/docs/guides/server/reverseproxy.adoc b/docs/guides/server/reverseproxy.adoc index f5960fb04db..0916411565b 100644 --- a/docs/guides/server/reverseproxy.adoc +++ b/docs/guides/server/reverseproxy.adoc @@ -13,15 +13,20 @@ Distributed environments frequently require the use of a reverse proxy. {project == TLS termination modes -When deploying {project_name} behind a reverse proxy, there are two common approaches for handling TLS connections between clients, the proxy, and {project_name}: re-encrypt and passthrough. +When deploying {project_name} behind a reverse proxy, there are three common approaches for handling TLS connections between clients, the proxy, and {project_name}: re-encrypt, edge termination and passthrough. === Re-encrypt For re-encrypt, the reverse proxy terminates the client TLS connection and establishes a TLS connection to {project_name}. The proxy can inspect and modify HTTP headers (such as `Forwarded` or `X-Forwarded-*`) before re-encrypting the traffic to the backend. -The proxy and {project_name} use independent TLS certificates: The proxy presents its own certificate to clients, while {project_name} uses a separate certificate for backend communication. +The proxy and {project_name} use independent TLS certificates: the proxy presents its own certificate to clients, while {project_name} uses a separate certificate for backend communication. Configure `--proxy-headers` so that {project_name} parses the forwarded headers set by the proxy, including the real client IP address. +=== Edge termination + +The edge termination is similar to re-encrypt as the proxy terminates the TLS connection, but the connection between the proxy and {project_name} is then unencrypted. +As edge termination is generally considered less secure than re-encrypt, this {section} focuses on re-encrypt instead. + === Passthrough With passthrough, the reverse proxy forwards the raw TLS connection directly to {project_name} without terminating it. @@ -79,7 +84,9 @@ When using passthrough: * Enable the PROXY protocol (`--proxy-protocol-enabled=true`) to see the real client IP address. Note that the PROXY protocol is not compatible with the `--proxy-headers` option. + -The PROXY protocol header that the proxy adds to convey the client IP address is neither encrypted nor signed. Therefore, it can be spoofed, tampered with, or leaked. Mitigate that for example by restricting the network access. +The PROXY protocol header that the proxy adds to convey the client IP address is neither encrypted nor signed. +Therefore, it can be spoofed, tampered with, or leaked. +Mitigate this risk, for example, by restricting network access. * Allow a longer `--shutdown-delay` (for example, 10–30 seconds) to give keepalive connections time to drain during shutdown, since the proxy cannot signal a connection close at the HTTP connection level. @@ -91,17 +98,56 @@ The PROXY protocol header that the proxy adds to convey the client IP address is * `9000` -The port `8443` (or `8080` if HTTP is enabled) is used for the Admin UI, Account Console, SAML and OIDC endpoints and the Admin REST API as described in the <@links.server id="hostname"/> {section}. +The port `8443` (or `8080` if HTTP is enabled) is used for the Admin UI, Account Console, SAML and OIDC endpoints, and the Admin REST API as described in the <@links.server id="hostname"/> {section}. -The port `9000` is used for management, which includes endpoints for health checks and metrics as described in the <@links.server id="management-interface"/> {section}. +The port `9000` is used for management, which includes endpoints for health checks and metrics as described in the <@links.server id="management-interface"/> {section}. -You only need to proxy port `8443` (or `8080`) even when you use different host names for frontend/backend and administration as described at <@links.server id="configuration-production"/>. You should not proxy port `9000` as health checks and metrics use those ports directly, and you do not want to expose this information to external callers. +You only need to proxy port `8443` (or `8080`) even when you use different hostnames for frontend/backend and administration as described in <@links.server id="configuration-production"/>. +You should not proxy port `9000` because health checks and metrics use that port directly, and you do not want to expose this information to external callers. -== Configure the reverse proxy headers +== {project_name} with TLS passthrough -{project_name} will parse the reverse proxy headers based on the `proxy-headers` option which accepts several values: +In TLS passthrough mode, the proxy forwards the raw TLS connection to {project_name} without terminating it. +Because the proxy cannot inspect or modify HTTP traffic, it cannot set or overwrite HTTP headers such as `Forwarded` or `X-Forwarded-*`. + +[WARNING] +==== +Do not set `--proxy-headers` when using TLS passthrough. +The proxy cannot inject forwarded headers into the encrypted traffic, and any headers already present in the request originate from the client and cannot be trusted. +Setting `--proxy-headers` in this mode would allow clients to spoof their IP address or protocol by sending forged headers directly to {project_name}. +==== + +Without forwarded headers, the only way for {project_name} to see the real client IP address is through the PROXY protocol, which is strongly recommended for TLS passthrough deployments. + +=== PROXY Protocol + +The PROXY protocol allows the proxy to prepend the original client connection information (such as the source IP address) to the TCP stream before forwarding it to {project_name}. +This happens outside of the TLS-encrypted channel, so it works even though the proxy cannot modify HTTP traffic. + +Enable it in {project_name} with the `--proxy-protocol-enabled` option: + +<@kc.start parameters="--proxy-protocol-enabled true"/> + +The proxy must also be configured to send the PROXY protocol header. +Most proxies support both version 1 (text-based) and version 2 (binary). +Consult your proxy's documentation for the specific configuration directive (for example, `send-proxy-v2` in HAProxy). + +NOTE: The `--proxy-protocol-enabled` option and the `--proxy-headers` option are mutually exclusive. +Setting both will result in a configuration error. + +=== Blueprints + +* <@links.server id="haproxy-passthrough"/> + +== {project_name} with TLS reencrypt + +In TLS re-encrypt mode, the proxy terminates the client TLS connection and establishes a new TLS connection to {project_name}. +Because the proxy can inspect and modify HTTP traffic, additional configuration options are available, such as forwarded headers, path filtering, sticky sessions, and client certificate forwarding. + +=== Configure the reverse proxy headers + +{project_name} parses the reverse proxy headers based on the `proxy-headers` option, which accepts several values: -* By default if the option is not specified, no reverse proxy headers are parsed. This should be used when no proxy is in use or with TLS passthrough. * `forwarded` enables parsing of the `Forwarded` header as per https://www.rfc-editor.org/rfc/rfc7239.html[RFC 7239]. * `xforwarded` enables parsing of non-standard `X-Forwarded-*` headers, such as `X-Forwarded-For`, `X-Forwarded-Proto`, `X-Forwarded-Host`, `X-Forwarded-Port`, and `X-Forwarded-Prefix`. @@ -114,58 +160,22 @@ For example: WARNING: If either `forwarded` or `xforwarded` is selected, make sure your reverse proxy properly sets and overwrites the `Forwarded` or `X-Forwarded-*` headers respectively. To set these headers, consult the documentation for your reverse proxy. Do not use `forwarded` or `xforwarded` with TLS passthrough. Misconfiguration will leave {project_name} exposed to security vulnerabilities. Take extra precautions to ensure that the client address is properly set by your reverse proxy via the `Forwarded` or `X-Forwarded-For` headers. -If this header is incorrectly configured, rogue clients can set this header and trick {project_name} into thinking the client is connected from a different IP address than the actual address. This precaution can be more critical if you do any deny or allow listing of IP addresses. +If these headers are incorrectly configured, rogue clients can inject false values and trick {project_name} into thinking the client is connecting from a different IP address than the actual one. +This precaution is especially critical if you do any deny or allow listing of IP addresses. NOTE: When using the `xforwarded` setting, the `X-Forwarded-Port` takes precedence over any port included in the `X-Forwarded-Host`. NOTE: If the TLS connection is terminated at the reverse proxy (edge termination), enabling HTTP through the `http-enabled` setting is required. -== Different context path on reverse proxy +=== Trusted Proxies -By default {project_name} is exposed through the root context path (`/`). If the proxy is using a different context path than {project_name}, one of the following must be done: -- Use a simple hostname for the `hostname` option, `xforwarded` for the `proxy-headers` option, and have the proxy set the `X-Forwarded-Prefix` header. -- Use a full URL for the `hostname` option including the proxy context path, for example using `--hostname=https://my.keycloak.org/auth` if {project_name} is exposed through the reverse proxy on `/auth`. -- Change the context path of {project_name} itself to match the context path for the reverse proxy using the `http-relative-path` option. +To ensure that proxy headers are used only from proxies you trust, set the `proxy-trusted-addresses` option to a comma-separated list of IP addresses (IPv4 or IPv6) or Classless Inter-Domain Routing (CIDR) notations. -For more details on exposing {project_name} on different hostname or context path incl. Administration REST API and Console, see <@links.server id="hostname"/>. +For example: -== Enable sticky sessions +<@kc.start parameters="--proxy-headers forwarded --proxy-trusted-addresses=192.168.0.32,127.0.0.0/8"/> -Typical cluster deployment consists of the load balancer (reverse proxy) and 2 or more {project_name} servers on private network. -For performance purposes, it may be useful if load balancer forwards all requests related to particular browser session to the same {project_name} backend node. - -The reason is, that {project_name} is using Infinispan distributed cache under the covers for save data related to current authentication session and user session. -The Infinispan distributed caches are configured with limited number of owners. That means that session related data are stored only in some cluster nodes and the other nodes need to lookup the data remotely if they want to access it. - -For example if authentication session with ID 123 is saved in the Infinispan cache on node1, and then node2 needs to lookup this session, it needs to send the request to node1 over the network to return the particular session entity. - -It is beneficial if particular session entity is always available locally, which can be done with the help of sticky sessions. The workflow in the cluster environment with the public frontend load balancer and two backend {project_name} nodes can be like this: - -* User sends initial request to see the {project_name} login screen - -* This request is served by the frontend load balancer, which forwards it to some random node (eg. node1). Strictly said, the node doesn't need to be random, but can be chosen according to some other criteria (client IP address etc). It all depends on the implementation and configuration of underlying load balancer (reverse proxy). - -* {project_name} creates authentication session with random ID (eg. 123) and saves it to the Infinispan cache. - -* Infinispan distributed cache assigns the primary owner of the session based on the hash of session ID. See Infinispan documentation for more details around this. Let's assume that Infinispan assigned node2 to be the owner of this session. - -* {project_name} creates the cookie AUTH_SESSION_ID with the format like . . In our example case, it will be 123.node2 . - -* Response is returned to the user with the {project_name} login screen and the AUTH_SESSION_ID cookie in the browser - -From this point, it is beneficial if load balancer forwards all the next requests to the node2 as this is the node, who is owner of the authentication session with ID 123 and hence Infinispan can lookup this session locally. After authentication is finished, the authentication session is converted to user session, which will be also saved on node2 because it has same ID 123 . - -The sticky session is not mandatory for the cluster setup, however it is good for performance for the reasons mentioned above. You need to configure your loadbalancer to stick over the AUTH_SESSION_ID cookie. The appropriate procedure to make this change depends on your loadbalancer. - -If your proxy supports session affinity without processing cookies from backend nodes, you should set the `+spi-sticky-session-encoder--infinispan--should-attach-route+` option -to `false` in order to avoid attaching the node to cookies and just rely on the reverse proxy capabilities. - -<@kc.start parameters="--spi-sticky-session-encoder--infinispan--should-attach-route=false"/> - -By default, the `+spi-sticky-session-encoder--infinispan--should-attach-route+` option value is `true` so that the node name is attached to -cookies to indicate to the reverse proxy the node that subsequent requests should be sent to. - -== Exposed path recommendations +=== Exposed path recommendations When using a reverse proxy, {project_name} only requires certain paths to be exposed. The following table shows the recommended paths to expose. @@ -197,7 +207,7 @@ The following table shows the recommended paths to expose. |/.well-known/ |/.well-known/ |Yes -|This path is needed to resolve Authorization Server Metadata and other information via RFC 8414. +|This path is needed to resolve Authorization Server Metadata and other information via https://www.rfc-editor.org/rfc/rfc8414.html[RFC 8414]. |/metrics |- @@ -214,48 +224,85 @@ The following table shows the recommended paths to expose. We assume you run {project_name} on the root path `/` on your reverse proxy/gateway's public API. If not, prefix the path with your desired one. -NOTE: If you configured a `http-relative-path` on the server, proceed as follows to use discovery with RFC 8414: Configure a reverse proxy to map the `/.well-known/` path without the prefix to the path with the prefix on the server. +NOTE: If you configured an `http-relative-path` on the server, proceed as follows to use discovery with RFC 8414: Configure a reverse proxy to map the `/.well-known/` path without the prefix to the path with the prefix on the server. -== Trusted Proxies +=== Different context path on reverse proxy -To ensure that proxy headers are used only from proxies you trust, set the `proxy-trusted-addresses` option to a comma separated list of IP addresses (IPv4 or IPv6) or Classless Inter-Domain Routing (CIDR) notations. +By default, {project_name} is exposed through the root context path (`/`). +If the proxy uses a different context path than {project_name}, one of the following must be done: -For example: +* Use a simple hostname for the `hostname` option, `xforwarded` for the `proxy-headers` option, and have the proxy set the `X-Forwarded-Prefix` header. +* Use a full URL for the `hostname` option including the proxy context path, for example using `--hostname=https://my.keycloak.org/auth` if {project_name} is exposed through the reverse proxy on `/auth`. +* Change the context path of {project_name} itself to match the context path for the reverse proxy using the `http-relative-path` option. -<@kc.start parameters="--proxy-headers forwarded --proxy-trusted-addresses=192.168.0.32,127.0.0.0/8"/> +For more details on exposing {project_name} on a different hostname or context path, including the Administration REST API and Console, see <@links.server id="hostname"/>. -== PROXY Protocol +=== Enable sticky sessions -The `proxy-protocol-enabled` option controls whether the server should use the HA PROXY protocol when serving requests from behind a proxy. When set to true, the remote address returned will be the one from the actual connecting client. The value cannot be `true` when using the `proxy-headers` option. +A typical cluster deployment consists of a load balancer (reverse proxy) and two or more {project_name} servers on a private network. +For performance purposes, it may be useful if the load balancer forwards all requests related to a particular browser session to the same {project_name} backend node. -This is useful when running behind a compatible TLS passthrough proxy because the request headers cannot be manipulated. +The reason is that {project_name} uses an Infinispan distributed cache internally to store data related to the current authentication session and user session. +The Infinispan distributed caches are configured with a limited number of owners. +That means that session-related data is stored only on some cluster nodes, and the other nodes need to look up the data remotely if they want to access it. -For example: +For example, if an authentication session with ID 123 is saved in the Infinispan cache on node1, and then node2 needs to look up this session, it needs to send the request to node1 over the network to retrieve the session entity. -<@kc.start parameters="--proxy-protocol-enabled true"/> +It is beneficial if a particular session entity is always available locally, which can be achieved with sticky sessions. +The workflow in a cluster environment with a public frontend load balancer and two backend {project_name} nodes can be as follows: -== Enabling client certificate lookup +* The user sends the initial request to see the {project_name} login screen. -When the proxy is configured as a TLS termination proxy the client certificate information can be forwarded to the server through specific HTTP request headers and then used to authenticate -clients. You are able to configure how the server is going to retrieve client certificate information depending on the proxy you are using. +* This request is served by the frontend load balancer, which forwards it to some random node (e.g., node1). +Strictly speaking, the node does not need to be random, but can be chosen according to other criteria (client IP address, etc.). +It all depends on the implementation and configuration of the underlying load balancer (reverse proxy). + +* {project_name} creates an authentication session with a random ID (e.g., 123) and saves it to the Infinispan cache. + +* The Infinispan distributed cache assigns the primary owner of the session based on the hash of the session ID. +See the Infinispan documentation for more details. +Assume that Infinispan assigns node2 as the owner of this session. + +* {project_name} creates the cookie `AUTH_SESSION_ID` with the format `.`. +In this example, the value is `123.node2`. + +* The response is returned to the user with the {project_name} login screen and the `AUTH_SESSION_ID` cookie in the browser. + +From this point, it is beneficial if the load balancer forwards all subsequent requests to node2, as this is the node that owns the authentication session with ID 123 and hence Infinispan can look up this session locally. +After authentication is finished, the authentication session is converted to a user session, which is also saved on node2 because it has the same ID 123. + +Sticky sessions are not mandatory for a cluster setup; however, they improve performance for the reasons mentioned above. +You need to configure your load balancer to use the `AUTH_SESSION_ID` cookie for session affinity. +The appropriate procedure depends on your load balancer. + +If your proxy supports session affinity without processing cookies from backend nodes, you should set the `+spi-sticky-session-encoder--infinispan--should-attach-route+` option to `false` in order to avoid attaching the node to cookies and just rely on the reverse proxy capabilities. + +<@kc.start parameters="--spi-sticky-session-encoder--infinispan--should-attach-route=false"/> + +By default, the `+spi-sticky-session-encoder--infinispan--should-attach-route+` option value is `true` so that the node name is attached to cookies to indicate to the reverse proxy the node that subsequent requests should be sent to. + +=== Enabling client certificate lookup + +When the proxy is configured as a TLS termination proxy, the client certificate information can be forwarded to the server through specific HTTP request headers and then used to authenticate clients. +You can configure how the server retrieves client certificate information depending on the proxy you are using. [WARNING] ==== Client certificate lookup via a proxy header for X.509 authentication is considered security-sensitive. If misconfigured, a forged client certificate header can be used for authentication. *Extra precautions need to be taken to ensure that the client certificate information can be trusted when passed via a proxy header.* -* Double check your use case needs reencrypt or edge TLS termination which implies using a proxy header for client certificate lookup. TLS passthrough is recommended as a more secure option - when X.509 authentication is desired as it does not require passing the certificate via a proxy header. Client certificate lookup from a proxy header is applicable only to reencrypt +* Double-check that your use case needs re-encrypt or edge TLS termination, which implies using a proxy header for client certificate lookup. +TLS passthrough is recommended as a more secure option when X.509 authentication is desired because it does not require passing the certificate via a proxy header. +Client certificate lookup from a proxy header is applicable only to re-encrypt and edge TLS termination. * If passthrough is not an option, implement the following security measures: ** Configure your network so that {project_name} is isolated and can accept connections only from the proxy. -** Make sure that the proxy overwrites the header that is configured in `spi-x509cert-lookup----ssl-client-cert` option. +** Make sure that the proxy overwrites the header that is configured in the `spi-x509cert-lookup----ssl-client-cert` option. ** Pay extra attention to the `spi-x509cert-lookup----trust-proxy-verification` setting. Make sure you enable it only if you can trust your proxy to verify the client certificate. - Setting `spi-x509cert-lookup----trust-proxy-verification=true` without the proxy verifying the client certificate chain will expose {project_name} to security vulnerability - when a forged client certificate can be used for authentication. +Setting `spi-x509cert-lookup----trust-proxy-verification=true` without the proxy verifying the client certificate chain will expose {project_name} to a security vulnerability where a forged client certificate can be used for authentication. ==== -The server supports some of the most commons TLS termination proxies such as: +The server supports some of the most common TLS termination proxies: [%autowidth] |=== @@ -274,13 +321,13 @@ The server supports some of the most commons TLS termination proxies such as: |Traefik (PassTLSClientCert middleware with `pem: true`) |rfc9440 -|Proxies that are compliant to link:https://datatracker.ietf.org/doc/rfc9440/[RFC 9440] +|Proxies compliant with link:https://datatracker.ietf.org/doc/rfc9440/[RFC 9440] |envoy |Envoy |=== -To configure how client certificates are retrieved from the requests you need to: +To configure how client certificates are retrieved from the requests, you need to: .Enable the corresponding proxy provider <@kc.build parameters="--spi-x509cert-lookup--provider="/> @@ -288,7 +335,7 @@ To configure how client certificates are retrieved from the requests you need to .Configure the HTTP headers <@kc.start parameters="--spi-x509cert-lookup----ssl-client-cert=SSL_CLIENT_CERT --spi-x509cert-lookup----ssl-cert-chain-prefix=CERT_CHAIN --spi-x509cert-lookup---certificate-chain-length=10"/> -When configuring the HTTP headers, you need to make sure the values you are using correspond to the name of the headers +When configuring the HTTP headers, you need to make sure the values you are using correspond to the names of the headers forwarded by the proxy with the client certificate information. Common options for configuring providers are: @@ -303,7 +350,7 @@ Common options for configuring providers are: |ssl-cert-chain-prefix | The prefix of the headers holding additional certificates in the chain and used to retrieve individual -certificates accordingly to the length of the chain. For instance, a value `CERT_CHAIN` will tell the server +certificates according to the length of the chain. For instance, a value `CERT_CHAIN` will tell the server to load additional certificates from headers `CERT_CHAIN_0` to `CERT_CHAIN_9` if `certificate-chain-length` is set to `10`. | `apache`, `haproxy`, `nginx` @@ -313,7 +360,7 @@ to load additional certificates from headers `CERT_CHAIN_0` to `CERT_CHAIN_9` if |=== -=== Configuring the NGINX provider +==== Configuring the NGINX provider The NGINX SSL/TLS module does not expose the client certificate chain. {project_name}'s NGINX certificate lookup provider rebuilds it by using the {project_name} truststore. @@ -329,12 +376,12 @@ to configure a {project_name} Truststore. The options and defaults specific to ` | false |cert-is-url-encoded -| Whether the forwarded certificate is url-encoded or not. In NGINX, this corresponds to the `$ssl_client_cert` and `$ssl_client_escaped_cert` variables. +| Whether the forwarded certificate is URL-encoded or not. In NGINX, this corresponds to the `$ssl_client_cert` and `$ssl_client_escaped_cert` variables. | true |=== -=== Configuring the rfc9440 provider +==== Configuring the rfc9440 provider If you stick to the header names mentioned in RFC 9440, you do not need to configure any additional options after selecting the `rfc9440` provider. The options and defaults specific to `rfc9440` are as follows: @@ -354,10 +401,10 @@ The options and defaults specific to `rfc9440` are as follows: |=== -If your certificate chain is longer than the given default, you must define the option with an appropriate number. +If your certificate chain is longer than the default, you must set the `certificate-chain-length` option to an appropriate value. Otherwise, the provider will discard the request. -=== Configuring the Traefik provider +==== Configuring the Traefik provider The Traefik provider handles certificates forwarded by Traefik's link:https://doc.traefik.io/traefik/middlewares/http/passtlsclientcert/[PassTLSClientCert middleware] with `pem: true`. Traefik sends the client certificate and any intermediate CA certificates as PEM blocks in a single `X-Forwarded-Tls-Client-Cert` header, separated by commas. @@ -365,9 +412,9 @@ The `traefik` provider parses all certificates from this header. Other than possibly changing the `certificate-chain-length`, you do not need to configure additional options for the `traefik` provider. -=== Configuring the Envoy provider +==== Configuring the Envoy provider -The Envoy provider will automatically retrieve the client certificate and optional certificate chain from the `x-forwarded-client-cert` header. +The Envoy provider automatically retrieves the client certificate and optional certificate chain from the `x-forwarded-client-cert` header. You do not need to configure additional options for the `envoy` provider. [[graceful-http-shutdown]] @@ -422,7 +469,8 @@ Both values accept duration formats: `1s` (seconds), `500ms` (milliseconds), `2m === When to adjust shutdown timeouts -Consider adjusting these values based on your deployment configuration with the following example Scenarios: +Consider adjusting these values based on your deployment configuration. +The following table shows example scenarios: [%autowidth,cols="2,>1,>1,3a"] |=== @@ -433,7 +481,7 @@ Consider adjusting these values based on your deployment configuration with the | |Assumptions: -* 5s poll interval and a load balancer reconfiguring after two successive failed probes. +* A 5s poll interval and a load balancer reconfiguring after two successive failed probes. * 1s for reconfiguring the proxy. * Proxy using TLS re-encrypt or edge termination.