From 756d2e4c971f8b5cab5cd52fb7c6bf809d87576a Mon Sep 17 00:00:00 2001 From: Jesse Dohmann Date: Mon, 20 Feb 2023 17:03:14 -0600 Subject: [PATCH] add http header modification feature --- modules/nw-http-header-configuration.adoc | 122 ++++++++++++++++++ ...s-controller-configuration-parameters.adoc | 6 + ...nw-ingress-set-or-delete-http-headers.adoc | 60 +++++++++ .../nw-route-set-or-delete-http-headers.adoc | 58 +++++++++ networking/ingress-operator.adoc | 4 + networking/routes/route-configuration.adoc | 4 + 6 files changed, 254 insertions(+) create mode 100644 modules/nw-http-header-configuration.adoc create mode 100644 modules/nw-ingress-set-or-delete-http-headers.adoc create mode 100644 modules/nw-route-set-or-delete-http-headers.adoc diff --git a/modules/nw-http-header-configuration.adoc b/modules/nw-http-header-configuration.adoc new file mode 100644 index 000000000000..16b636bf6f1a --- /dev/null +++ b/modules/nw-http-header-configuration.adoc @@ -0,0 +1,122 @@ +// Module included in the following assemblies: +// +// * networking/ingress-operator.adoc +// * networking/route-configuration.adoc + +:_content-type: CONCEPT +[id="nw-http-header-configuration_{context}"] += HTTP header configuration + +{product-title} provides different methods for working with HTTP headers. When setting or deleting headers, you can use specific fields in the Ingress Controller or an individual route to modify request and response headers. You can also set certain headers by using route annotations. The various ways of configuring headers can present challenges when working together. + +[NOTE] +==== +You can only set or delete headers within an `IngressController` or `Route` CR, you cannot append them. If an HTTP header is set with a value, that value must be complete and not require appending in the future. In situations where it makes sense to append a header, such as the X-Forwarded-For header, use the `spec.httpHeaders.forwardedHeaderPolicy` field, instead of `spec.httpHeaders.actions`. +==== + +[id="nw-http-header-configuration-order_{context}"] +== Order of precedence + +When the same HTTP header is modified both in the Ingress Controller and in a route, HAProxy prioritizes the actions in certain ways depending on whether it is a request or response header. + +* For HTTP response headers, actions specified in the Ingress Controller are executed after the actions specified in a route. This means that the actions specified in the Ingress Controller take precedence. + +* For HTTP request headers, actions specified in a route are executed after the actions specified in the Ingress Controller. This means that the actions specified in the route take precedence. + +For example, a cluster administrator sets the X-Frame-Options response header with the value `DENY` in the Ingress Controller using the following configuration: + +.Example `IngressController` spec +[source,yaml] +---- +apiVersion: operator.openshift.io/v1 +kind: IngressController +# ... +spec: + httpHeaders: + actions: + response: + - name: X-Frame-Options + action: + type: Set + set: + value: DENY +---- + +A route owner sets the same response header that the cluster administrator set in the Ingress Controller, but with the value `SAMEORIGIN` using the following configuration: + +.Example `Route` spec +[source,yaml] +---- +apiVersion: route.openshift.io/v1 +kind: Route +# ... +spec: + httpHeaders: + actions: + response: + - name: X-Frame-Options + action: + type: Set + set: + value: SAMEORIGIN +---- + +When both the `IngressController` spec and `Route` spec are configuring the X-Frame-Options header, then the value set for this header at the global level in the Ingress Controller will take precedence, even if a specific route allows frames. + +This prioritzation occurs because the `haproxy.config` file uses the following logic, where the Ingress Controller is considered the front end and individual routes are considered the back end. The header value `DENY` applied to the front end configurations overrides the same header with the value `SAMEORIGIN` that is set in the back end: + +[source,text] +---- +frontend public + http-response set-header X-Frame-Options 'DENY' + +frontend fe_sni + http-response set-header X-Frame-Options 'DENY' + +frontend fe_no_sni + http-response set-header X-Frame-Options 'DENY' + +backend be_secure:openshift-monitoring:alertmanager-main + http-response set-header X-Frame-Options 'SAMEORIGIN' +---- + +Additionally, any actions defined in either the Ingress Controller or a route override values set using route annotations. + +[id="nw-http-header-configuration-special-cases_{context}"] +== Special case headers + +The following headers are either prevented entirely from being set or deleted, or allowed under specific circumstances: + +.Special case header configuration options +[cols="5*a",options="header"] +|=== +|Header name |Configurable using `IngressController` spec |Configurable using `Route` spec |Reason for disallowment |Configurable using another method + +|`proxy` +|No +|No +|The `proxy` HTTP request header can be used to exploit vulnerable CGI applications by injecting the header value into the `HTTP_PROXY` environment variable. The `proxy` HTTP request header is also non-standard and prone to error during configuration. +|No + +|`host` +|No +|Yes +|When the `host` HTTP request header is set using the `IngressController` CR, HAProxy can fail when looking up the correct route. +|No + +|`strict-transport-security` +|No +|No +|The `strict-transport-security` HTTP response header is already handled using route annotations and does not need a separate implementation. +|Yes: the `haproxy.router.openshift.io/hsts_header` route annotation + +|`cookie` and `set-cookie` +|No +|No +|The cookies that HAProxy sets are used for session tracking to map client connections to particular back-end servers. Allowing these headers to be set could interfere with HAProxy's session affinity and restrict HAProxy's ownership of a cookie. +|Yes: + +* the `haproxy.router.openshift.io/disable_cookie` route annotation +* the `haproxy.router.openshift.io/cookie_name` route annotation + +|=== diff --git a/modules/nw-ingress-controller-configuration-parameters.adoc b/modules/nw-ingress-controller-configuration-parameters.adoc index ee3b9e31f148..6d5dd81bd7d6 100644 --- a/modules/nw-ingress-controller-configuration-parameters.adoc +++ b/modules/nw-ingress-controller-configuration-parameters.adoc @@ -199,6 +199,12 @@ These adjustments are only applied to cleartext, edge-terminated, and re-encrypt For request headers, these adjustments are applied only for routes that have the `haproxy.router.openshift.io/h1-adjust-case=true` annotation. For response headers, these adjustments are applied to all HTTP responses. If this field is empty, no request headers are adjusted. +`actions` specifies options for performing certain actions on headers. Headers cannot be set or deleted for TLS passthrough connections. The `actions` field has additional subfields `spec.httpHeader.actions.response` and `spec.httpHeader.actions.request`: + +* The `response` subfield specifies a list of HTTP response headers to set or delete. + +* The `request` subfield specifies a list of HTTP request headers to set or delete. + |`httpCompression` |`httpCompression` defines the policy for HTTP traffic compression. diff --git a/modules/nw-ingress-set-or-delete-http-headers.adoc b/modules/nw-ingress-set-or-delete-http-headers.adoc new file mode 100644 index 000000000000..61d6fa9c84a6 --- /dev/null +++ b/modules/nw-ingress-set-or-delete-http-headers.adoc @@ -0,0 +1,60 @@ +// Module included in the following assemblies: +// +// * networking/ingress-operator.adoc + +:_content-type: PROCEDURE +[id="nw-ingress-set-or-delete-http-headers_{context}"] += Setting or deleting HTTP request and response headers in an Ingress Controller + +You can set or delete certain HTTP request and response headers for compliance purposes or other reasons. You can set or delete these headers either for all routes served by an Ingress Controller or for specific routes. + +For example, you might want to migrate an application running on your cluster to use mutual TLS, which requires that your application checks for an X-Forwarded-Client-Cert request header, but the {product-title} default Ingress Controller provides an X-SSL-Client-Der request header. + +The following procedure modifies the Ingress Controller to set the X-Forwarded-Client-Cert request header, and delete the X-SSL-Client-Der request header. + +.Prerequisites +* You have installed the OpenShift CLI (`oc`). +* You have access to an {product-title} cluster as a user with the `cluster-admin` role. + +.Procedure +. Edit the Ingress Controller resource: ++ +[source,terminal] +---- +$ oc -n openshift-ingress-operator edit ingresscontroller/default +---- + +. Replace the X-SSL-Client-Der HTTP request header with the X-Forwarded-Client-Cert HTTP request header: ++ +[source,yaml] +---- +apiVersion: operator.openshift.io/v1 +kind: IngressController +metadata: + name: default + namespace: openshift-ingress-operator +spec: + httpHeaders: + actions: <1> + request: <2> + - name: X-Forwarded-Client-Cert <3> + action: + type: Set <4> + set: + value: "%{+Q}[ssl_c_der,base64]" <5> + - name: X-SSL-Client-Der + action: + type: Delete +---- +<1> The list of actions you want to perform on the HTTP headers. +<2> The type of header you want to change. In this case, a request header. +<3> The name of the header you want to change. For a list of available headers you can set or delete, see _HTTP header configuration_. +<4> The type of action being taken on the header. This field can have the value `Set` or `Delete`. +<5> When setting HTTP headers, you must provide a `value`. The value can be a string from a list of available directives for that header, for example `DENY`, or it can be a dynamic value that will be interpreted using HAProxy's dynamic value syntax. In this case, a dynamic value is added. ++ +[NOTE] +==== +For setting dynamic header values for HTTP responses, allowed sample fetchers are `res.hdr` and `ssl_c_der`. For setting dynamic header values for HTTP requests, allowed sample fetchers are `req.hdr` and `ssl_c_der`. Both request and response dynamic values can use the `lower` and `base64` converters. +==== + +. Save the file to apply the changes. diff --git a/modules/nw-route-set-or-delete-http-headers.adoc b/modules/nw-route-set-or-delete-http-headers.adoc new file mode 100644 index 000000000000..81204a048c50 --- /dev/null +++ b/modules/nw-route-set-or-delete-http-headers.adoc @@ -0,0 +1,58 @@ +// Module included in the following assemblies: +// +// * networking/route-configuration.adoc + +:_content-type: PROCEDURE +[id="nw-route-set-or-delete-http-headers_{context}"] += Setting or deleting HTTP request and response headers in a route + +You can set or delete certain HTTP request and response headers for compliance purposes or other reasons. You can set or delete these headers either for all routes served by an Ingress Controller or for specific routes. + +For example, you might want to enable a web application to serve content in alternate locations for specific routes if that content is written in multiple languages, even if there is a default global location specified by the Ingress Controller serving the routes. + +The following procedure creates a route that sets the Content-Location HTTP request header so that the URL associated with the application, `\https://app.example.com`, directs to the location `\https://app.example.com/lang/en-us`. Directing application traffic to this location means that anyone using that specific route is accessing web content written in American English. + +.Prerequisites +* You have installed the OpenShift CLI (`oc`). +* You are logged into an {product-title} cluster as a project administrator. +* You have a web application that exposes a port and an HTTP or TLS endpoint listening for traffic on the port. + +.Procedure +. Create a route definition and save it in a file called `app-example-route.yaml`: ++ +.YAML definition of the created route with HTTP header directives +[source,yaml] +---- +apiVersion: route.openshift.io/v1 +kind: Route +# ... +spec: + host: app.example.com + tls: + termination: edge + to: + kind: Service + name: app-example + httpHeaders: + actions: <1> + response: <2> + - name: Content-Location <3> + action: + type: Set <4> + set: + value: /lang/en-us <5> +---- +<1> The list of actions you want to perform on the HTTP headers. +<2> The type of header you want to change. In this case, a response header. +<3> The name of the header you want to change. For a list of available headers you can set or delete, see _HTTP header configuration_. +<4> The type of action being taken on the header. This field can have the value `Set` or `Delete`. +<5> When setting HTTP headers, you must provide a `value`. The value can be a string from a list of available directives for that header, for example `DENY`, or it can be a dynamic value that will be interpreted using HAProxy's dynamic value syntax. In this case, the value is set to the relative location of the content. + +. Create a route to your existing web application using the newly created route definition: ++ +[source,terminal] +---- +$ oc -n app-example create -f app-example-route.yaml +---- + +For HTTP request headers, the actions specified in the route definitions are executed after any actions performed on HTTP request headers in the Ingress Controller. This means that any values set for those request headers in a route will take precedence over the ones set in the Ingress Controller. For more information on the processing order of HTTP headers, see _HTTP header configuration_. diff --git a/networking/ingress-operator.adoc b/networking/ingress-operator.adoc index dd918690ba48..eeb834b53d16 100644 --- a/networking/ingress-operator.adoc +++ b/networking/ingress-operator.adoc @@ -79,6 +79,10 @@ include::modules/nw-route-admission-policy.adoc[leveloffset=+2] include::modules/using-wildcard-routes.adoc[leveloffset=+2] +include::modules/nw-http-header-configuration.adoc[leveloffset=+2] + +include::modules/nw-ingress-set-or-delete-http-headers.adoc[leveloffset=+2] + include::modules/nw-using-ingress-forwarded.adoc[leveloffset=+2] include::modules/nw-http2-haproxy.adoc[leveloffset=+2] diff --git a/networking/routes/route-configuration.adoc b/networking/routes/route-configuration.adoc index 3f5c0b2534cb..bc3891fac714 100644 --- a/networking/routes/route-configuration.adoc +++ b/networking/routes/route-configuration.adoc @@ -53,6 +53,10 @@ include::modules/nw-annotating-a-route-with-a-cookie-name.adoc[leveloffset=+2] include::modules/nw-path-based-routes.adoc[leveloffset=+1] +include::modules/nw-http-header-configuration.adoc[leveloffset=+1] + +include::modules/nw-route-set-or-delete-http-headers.adoc[leveloffset=+1] + include::modules/nw-route-specific-annotations.adoc[leveloffset=+1] ifndef::openshift-rosa,openshift-dedicated[]