diff --git a/apis/config/v1alpha1/backend_types_test.go b/apis/config/v1alpha1/backend_types_test.go index 98ffa74..0a2c4bd 100644 --- a/apis/config/v1alpha1/backend_types_test.go +++ b/apis/config/v1alpha1/backend_types_test.go @@ -83,7 +83,6 @@ var _ = Describe("Backend", Label("type"), func() { Ω(backend.AddToParser(p)).ShouldNot(HaveOccurred()) Ω(p.String()).Should(ContainSubstring("hash-type consistent djb2 avalanche")) }) - It("should set ssl parameters", func() { backend := &configv1alpha1.Backend{ ObjectMeta: metav1.ObjectMeta{Name: "foo"}, @@ -117,7 +116,6 @@ var _ = Describe("Backend", Label("type"), func() { Ω(backend.AddToParser(p)).ShouldNot(HaveOccurred()) Ω(p.String()).Should(ContainSubstring("ssl alpn h2,http/1.0 ca-file /usr/local/etc/haproxy/test-ca.crt cookie 1c3c2192e2912699ccd31119b162666a inter 5000 verify required verifyhost routername.namespace.svc weight 256")) }) - It("should set option http-request deny", func() { var notFound int64 = 404 backend := &configv1alpha1.Backend{ @@ -140,7 +138,34 @@ var _ = Describe("Backend", Label("type"), func() { Ω(backend.AddToParser(p)).ShouldNot(HaveOccurred()) Ω(p.String()).Should(ContainSubstring("http-request deny deny_status 404 if { var(my-ip) -m ip 127.0.0.0/8 10.0.0.0/8 }\n")) }) - + It("should set option http-request replace-path", func() { + backend := &configv1alpha1.Backend{ + ObjectMeta: metav1.ObjectMeta{Name: "openshift_default"}, + Spec: configv1alpha1.BackendSpec{ + BaseSpec: configv1alpha1.BaseSpec{ + HTTPRequest: &configv1alpha1.HTTPRequestRules{ + ReplacePath: []configv1alpha1.ReplacePath{ + { + MatchRegex: "(.*)", + ReplaceFmt: "/foo\\1", + }, + { + Rule: configv1alpha1.Rule{ + ConditionType: "if", + Condition: "{ url_beg /foo/ }", + }, + MatchRegex: "/foo/(.*)", + ReplaceFmt: "/\\1", + }, + }, + }, + }, + }, + } + Ω(backend.AddToParser(p)).ShouldNot(HaveOccurred()) + Ω(p.String()).Should(ContainSubstring("http-request replace-path (.*) /foo\\1\n")) + Ω(p.String()).Should(ContainSubstring("http-request replace-path /foo/(.*) /\\1 if { url_beg /foo/ }\n")) + }) It("should set option http-pretend-keepalive", func() { backend := &configv1alpha1.Backend{ ObjectMeta: metav1.ObjectMeta{Name: "openshift_default"}, @@ -153,7 +178,6 @@ var _ = Describe("Backend", Label("type"), func() { Ω(backend.AddToParser(p)).ShouldNot(HaveOccurred()) Ω(p.String()).Should(ContainSubstring("option http-pretend-keepalive\n")) }) - It("should set option forwardfor", func() { backend := &configv1alpha1.Backend{ ObjectMeta: metav1.ObjectMeta{Name: "openshift_default"}, diff --git a/apis/config/v1alpha1/common_types.go b/apis/config/v1alpha1/common_types.go index c6f52b5..577ee2e 100644 --- a/apis/config/v1alpha1/common_types.go +++ b/apis/config/v1alpha1/common_types.go @@ -729,6 +729,10 @@ type HTTPRequestRules struct { // Redirect performs an HTTP redirection based on a redirect rule. // +optional Redirect []Redirect `json:"redirect,omitempty"` + // ReplacePath matches the value of the path using a regex and completely replaces it with the specified format. + // The replacement does not modify the scheme, the authority and the query-string. + // +optional + ReplacePath []ReplacePath `json:"replacePath,omitempty"` // Deny stops the evaluation of the rules and immediately rejects the request and emits an HTTP 403 error. // Optionally the status code specified as an argument to deny_status. // +optional @@ -777,6 +781,17 @@ func (h *HTTPRequestRules) Model() (models.HTTPRequestRules, error) { }) } + for idx, header := range h.ReplacePath { + model = append(model, &models.HTTPRequestRule{ + Type: "replace-path", + Index: ptr.To(int64(idx)), + PathMatch: header.MatchRegex, + PathFmt: header.ReplaceFmt, + Cond: header.ConditionType, + CondTest: header.Condition, + }) + } + if h.Deny != nil && h.Deny.Enabled { model = append(model, &models.HTTPRequestRule{ DenyStatus: h.DenyStatus, @@ -869,7 +884,7 @@ type HTTPReturn struct { } type HTTPReturnContent struct { - // Type specifies the content-type of the HTTP REsponse. + // Type specifies the content-type of the HTTP response. Type string `json:"type"` // ContentFormat defines the format of the Content. Can be one an errorfile or a string. // +kubebuilder:validation:Enum=default-errorfile;errorfile;errorfiles;file;lf-file;string;lf-string @@ -891,6 +906,7 @@ type HTTPPathRule struct { // Value specifies the path value Value string `json:"format,omitempty"` } + type HTTPHeaderValue struct { // Env variable with the header value Env *corev1.EnvVar `json:"env,omitempty"` @@ -900,6 +916,14 @@ type HTTPHeaderValue struct { Format *string `json:"format,omitempty"` } +type ReplacePath struct { + Rule `json:",inline"` + // MatchRegex is a string pattern used to identify the paths that need to be replaced. + MatchRegex string `json:"matchRegex"` + // ReplaceFmt defines the format string used to replace the values that match the pattern. + ReplaceFmt string `json:"replaceFmt"` +} + func (h *HTTPHeaderValue) String() string { str := ptr.Deref(h.Str, "") if h.Env != nil { diff --git a/docs/api-reference.md b/docs/api-reference.md index 0a1a9b4..983dcb1 100644 --- a/docs/api-reference.md +++ b/docs/api-reference.md @@ -416,6 +416,7 @@ _Appears in:_ | `setPath` _[HTTPPathRule](#httppathrule) array_ | SetPath sets request path | | `addHeader` _[HTTPHeaderRule](#httpheaderrule) array_ | AddHeader appends HTTP header fields | | `redirect` _[Redirect](#redirect) array_ | Redirect performs an HTTP redirection based on a redirect rule. | +| `replacePath` _[ReplacePath](#replacepath) array_ | ReplacePath matches the value of the path using a regex and completely replaces it with the specified format. The replacement does not modify the scheme, the authority and the query-string. | | `deny` _[Deny](#deny)_ | Deny stops the evaluation of the rules and immediately rejects the request and emits an HTTP 403 error. Optionally the status code specified as an argument to deny_status. | | `denyStatus` _[int64](#int64)_ | DenyStatus is the HTTP status code. | | `return` _[HTTPReturn](#httpreturn)_ | Return stops the evaluation of the rules and immediately returns a response. | @@ -649,6 +650,21 @@ _Appears in:_ | `selector` _[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.25/#labelselector-v1-meta)_ | LabelSelector to select multiple backends | +#### ReplacePath + + + + + +_Appears in:_ +- [HTTPRequestRules](#httprequestrules) + +| Field | Description | +| --- | --- | +| `matchRegex` _string_ | MatchRegex is a string pattern used to identify the paths that need to be replaced. | +| `replaceFmt` _string_ | ReplaceFmt defines the format string used to replace the values that match the pattern. | + + #### Resolver @@ -696,6 +712,7 @@ _Appears in:_ - [HTTPHeaderRule](#httpheaderrule) - [HTTPPathRule](#httppathrule) - [Redirect](#redirect) +- [ReplacePath](#replacepath) - [TCPRequestRule](#tcprequestrule) diff --git a/helm/haproxy-operator/crds/config.haproxy.com_backends.yaml b/helm/haproxy-operator/crds/config.haproxy.com_backends.yaml index 015b4b1..c78528c 100644 --- a/helm/haproxy-operator/crds/config.haproxy.com_backends.yaml +++ b/helm/haproxy-operator/crds/config.haproxy.com_backends.yaml @@ -623,6 +623,36 @@ spec: type: string type: object type: array + replacePath: + description: ReplacePath matches the value of the path using a + regex and completely replaces it with the specified format. + The replacement does not modify the scheme, the authority and + the query-string. + items: + properties: + condition: + description: Condition is a condition composed of ACLs. + type: string + conditionType: + description: ConditionType specifies the type of the condition + matching ('if' or 'unless') + enum: + - if + - unless + type: string + matchRegex: + description: MatchRegex is a string pattern used to identify + the paths that need to be replaced. + type: string + replaceFmt: + description: ReplaceFmt defines the format string used to + replace the values that match the pattern. + type: string + required: + - matchRegex + - replaceFmt + type: object + type: array return: description: Return stops the evaluation of the rules and immediately returns a response. @@ -646,7 +676,7 @@ spec: type: string type: description: Type specifies the content-type of the HTTP - REsponse. + response. type: string value: description: Value specifying the file or the string to diff --git a/helm/haproxy-operator/crds/config.haproxy.com_frontends.yaml b/helm/haproxy-operator/crds/config.haproxy.com_frontends.yaml index 7e6c3d2..26dd09b 100644 --- a/helm/haproxy-operator/crds/config.haproxy.com_frontends.yaml +++ b/helm/haproxy-operator/crds/config.haproxy.com_frontends.yaml @@ -874,6 +874,36 @@ spec: type: string type: object type: array + replacePath: + description: ReplacePath matches the value of the path using a + regex and completely replaces it with the specified format. + The replacement does not modify the scheme, the authority and + the query-string. + items: + properties: + condition: + description: Condition is a condition composed of ACLs. + type: string + conditionType: + description: ConditionType specifies the type of the condition + matching ('if' or 'unless') + enum: + - if + - unless + type: string + matchRegex: + description: MatchRegex is a string pattern used to identify + the paths that need to be replaced. + type: string + replaceFmt: + description: ReplaceFmt defines the format string used to + replace the values that match the pattern. + type: string + required: + - matchRegex + - replaceFmt + type: object + type: array return: description: Return stops the evaluation of the rules and immediately returns a response. @@ -897,7 +927,7 @@ spec: type: string type: description: Type specifies the content-type of the HTTP - REsponse. + response. type: string value: description: Value specifying the file or the string to diff --git a/helm/haproxy-operator/crds/config.haproxy.com_listens.yaml b/helm/haproxy-operator/crds/config.haproxy.com_listens.yaml index 2c4b4da..5c59915 100644 --- a/helm/haproxy-operator/crds/config.haproxy.com_listens.yaml +++ b/helm/haproxy-operator/crds/config.haproxy.com_listens.yaml @@ -981,6 +981,36 @@ spec: type: string type: object type: array + replacePath: + description: ReplacePath matches the value of the path using a + regex and completely replaces it with the specified format. + The replacement does not modify the scheme, the authority and + the query-string. + items: + properties: + condition: + description: Condition is a condition composed of ACLs. + type: string + conditionType: + description: ConditionType specifies the type of the condition + matching ('if' or 'unless') + enum: + - if + - unless + type: string + matchRegex: + description: MatchRegex is a string pattern used to identify + the paths that need to be replaced. + type: string + replaceFmt: + description: ReplaceFmt defines the format string used to + replace the values that match the pattern. + type: string + required: + - matchRegex + - replaceFmt + type: object + type: array return: description: Return stops the evaluation of the rules and immediately returns a response. @@ -1004,7 +1034,7 @@ spec: type: string type: description: Type specifies the content-type of the HTTP - REsponse. + response. type: string value: description: Value specifying the file or the string to diff --git a/helm/haproxy-operator/crds/proxy.haproxy.com_instances.yaml b/helm/haproxy-operator/crds/proxy.haproxy.com_instances.yaml index f1b334f..2287133 100644 --- a/helm/haproxy-operator/crds/proxy.haproxy.com_instances.yaml +++ b/helm/haproxy-operator/crds/proxy.haproxy.com_instances.yaml @@ -562,16 +562,16 @@ spec: description: 'RelabelConfigs to apply to samples before scraping. More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config' items: - description: 'RelabelConfig allows dynamic rewriting of the - label set, being applied to samples before ingestion. It defines - ``-section of Prometheus configuration. - More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#metric_relabel_configs' + description: "RelabelConfig allows dynamic rewriting of the + label set for targets, alerts, scraped samples and remote + write samples. \n More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config" properties: action: default: replace - description: Action to perform based on regex matching. - Default is 'replace'. uppercase and lowercase actions - require Prometheus >= 2.36. + description: "Action to perform based on the regex matching. + \n `Uppercase` and `Lowercase` actions require Prometheus + >= v2.36.0. `DropEqual` and `KeepEqual` actions require + Prometheus >= v2.41.0. \n Default: \"Replace\"" enum: - replace - Replace @@ -591,30 +591,33 @@ spec: - Lowercase - uppercase - Uppercase + - keepequal + - KeepEqual + - dropequal + - DropEqual type: string modulus: - description: Modulus to take of the hash of the source label - values. + description: "Modulus to take of the hash of the source + label values. \n Only applicable when the action is `HashMod`." format: int64 type: integer regex: description: Regular expression against which the extracted - value is matched. Default is '(.*)' + value is matched. type: string replacement: - description: Replacement value against which a regex replace - is performed if the regular expression matches. Regex - capture groups are available. Default is '$1' + description: "Replacement value against which a Replace + action is performed if the regular expression matches. + \n Regex capture groups are available." type: string separator: - description: Separator placed between concatenated source - label values. default is ';'. + description: Separator is the string between concatenated + SourceLabels. type: string sourceLabels: description: The source labels select values from existing labels. Their content is concatenated using the configured - separator and matched against the configured regular expression - for the replace, keep, and drop actions. + Separator and matched against the configured regular expression. items: description: LabelName is a valid Prometheus label name which may only contain ASCII letters, numbers, as well @@ -623,9 +626,10 @@ spec: type: string type: array targetLabel: - description: Label to which the resulting value is written - in a replace action. It is mandatory for replace actions. - Regex capture groups are available. + description: "Label to which the resulting string is written + in a replacement. \n It is mandatory for `Replace`, `HashMod`, + `Lowercase`, `Uppercase`, `KeepEqual` and `DropEqual` + actions. \n Regex capture groups are available." type: string type: object type: array @@ -662,7 +666,9 @@ spec: certificate contents type: string certificate: - description: certificate provides certificate contents + description: certificate provides certificate contents. + This should be a single serving certificate, not a certificate + chain. Do not include a CA certificate. type: string destinationCACertificate: description: destinationCACertificate provides the contents @@ -675,15 +681,33 @@ spec: which allows infrastructure generated certificates to automatically verify. type: string + externalCertificate: + description: externalCertificate provides certificate + contents as a secret reference. This should be a single + serving certificate, not a certificate chain. Do not + include a CA certificate. The secret referenced should + be present in the same namespace as that of the Route. + Forbidden when `certificate` is set. + properties: + name: + description: 'name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + type: object + x-kubernetes-map-type: atomic insecureEdgeTerminationPolicy: description: "insecureEdgeTerminationPolicy indicates the desired behavior for insecure connections to a route. While each router may make its own decisions on which ports to expose, this is normally port 80. \n * Allow - traffic is sent to the server on the insecure port - (default) * Disable - no traffic is allowed on the insecure - port. * Redirect - clients are redirected to the secure - port." + (edge/reencrypt terminations only) (default). * None + - no traffic is allowed on the insecure port. * Redirect + - clients are redirected to the secure port." + enum: + - Allow + - None + - Redirect + - "" type: string key: description: key provides key file contents @@ -695,11 +719,22 @@ spec: * passthrough - Traffic is sent straight to the destination without the router providing TLS termination * reencrypt - TLS termination is done by the router and https is - used to communicate with the backend" + used to communicate with the backend \n Note: passthrough + termination is incompatible with httpHeader actions" + enum: + - edge + - reencrypt + - passthrough type: string required: - termination type: object + x-kubernetes-validations: + - message: 'cannot have both spec.tls.termination: passthrough + and spec.tls.insecureEdgeTerminationPolicy: Allow' + rule: 'has(self.termination) && has(self.insecureEdgeTerminationPolicy) + ? !((self.termination==''passthrough'') && (self.insecureEdgeTerminationPolicy==''Allow'')) + : true' required: - enabled type: object @@ -780,14 +815,19 @@ spec: type: object x-kubernetes-map-type: atomic matchLabelKeys: - description: MatchLabelKeys is a set of pod label keys to - select the pods over which spreading will be calculated. + description: "MatchLabelKeys is a set of pod label keys + to select the pods over which spreading will be calculated. The keys are used to lookup values from the incoming pod labels, those key-value labels are ANDed with labelSelector to select the group of existing pods over which spreading - will be calculated for the incoming pod. Keys that don't - exist in the incoming pod labels will be ignored. A null - or empty list means only match against labelSelector. + will be calculated for the incoming pod. The same key + is forbidden to exist in both MatchLabelKeys and LabelSelector. + MatchLabelKeys cannot be set when LabelSelector isn't + set. Keys that don't exist in the incoming pod labels + will be ignored. A null or empty list means only match + against labelSelector. \n This is a beta field and requires + the MatchLabelKeysInPodTopologySpread feature gate to + be enabled (enabled by default)." items: type: string type: array @@ -847,7 +887,7 @@ spec: - Ignore: nodeAffinity/nodeSelector are ignored. All nodes are included in the calculations. \n If this value is nil, the behavior is equivalent to the Honor policy. This - is a alpha-level feature enabled by the NodeInclusionPolicyInPodTopologySpread + is a beta-level feature default enabled by the NodeInclusionPolicyInPodTopologySpread feature flag." type: string nodeTaintsPolicy: @@ -857,8 +897,8 @@ spec: tainted nodes for which the incoming pod has a toleration, are included. - Ignore: node taints are ignored. All nodes are included. \n If this value is nil, the behavior is - equivalent to the Ignore policy. This is a alpha-level - feature enabled by the NodeInclusionPolicyInPodTopologySpread + equivalent to the Ignore policy. This is a beta-level + feature default enabled by the NodeInclusionPolicyInPodTopologySpread feature flag." type: string topologyKey: @@ -1162,7 +1202,9 @@ spec: to be used in HTTP probes properties: name: - description: The header field name + description: The header field name. This will + be canonicalized upon output, so case-variant + names will be understood as the same header. type: string value: description: The header field value @@ -1258,7 +1300,9 @@ spec: to be used in HTTP probes properties: name: - description: The header field name + description: The header field name. This will + be canonicalized upon output, so case-variant + names will be understood as the same header. type: string value: description: The header field value @@ -1338,8 +1382,6 @@ spec: type: integer grpc: description: GRPC specifies an action involving a GRPC port. - This is a beta field and requires enabling GRPCContainerProbe - feature gate. properties: port: description: Port number of the gRPC service. Number @@ -1371,7 +1413,9 @@ spec: to be used in HTTP probes properties: name: - description: The header field name + description: The header field name. This will + be canonicalized upon output, so case-variant + names will be understood as the same header. type: string value: description: The header field value @@ -1540,8 +1584,6 @@ spec: type: integer grpc: description: GRPC specifies an action involving a GRPC port. - This is a beta field and requires enabling GRPCContainerProbe - feature gate. properties: port: description: Port number of the gRPC service. Number @@ -1573,7 +1615,9 @@ spec: to be used in HTTP probes properties: name: - description: The header field name + description: The header field name. This will + be canonicalized upon output, so case-variant + names will be understood as the same header. type: string value: description: The header field value @@ -1662,10 +1706,52 @@ spec: format: int32 type: integer type: object + resizePolicy: + description: Resources resize policy for the container. + items: + description: ContainerResizePolicy represents resource resize + policy for the container. + properties: + resourceName: + description: 'Name of the resource to which this resource + resize policy applies. Supported values: cpu, memory.' + type: string + restartPolicy: + description: Restart policy to apply when specified resource + is resized. If not specified, it defaults to NotRequired. + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic resources: description: 'Compute Resources required by this container. Cannot be updated. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' properties: + claims: + description: "Claims lists the names of resources, defined + in spec.resourceClaims, that are used by this container. + \n This is an alpha field and requires enabling the DynamicResourceAllocation + feature gate. \n This field is immutable. It can only + be set for containers." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry + in pod.spec.resourceClaims of the Pod where this + field is used. It makes that resource available + inside a container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map limits: additionalProperties: anyOf: @@ -1686,10 +1772,29 @@ spec: description: 'Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, - otherwise to an implementation-defined value. More info: - https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + otherwise to an implementation-defined value. Requests + cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object type: object + restartPolicy: + description: 'RestartPolicy defines the restart behavior of + individual containers in a pod. This field may only be set + for init containers, and the only allowed value is "Always". + For non-init containers or when this field is not specified, + the restart behavior is defined by the Pod''s restart policy + and the container type. Setting the RestartPolicy as "Always" + for the init container will have the following effect: this + init container will be continually restarted on exit until + all regular containers have terminated. Once all regular containers + have completed, all init containers with restartPolicy "Always" + will be shut down. This lifecycle differs from normal init + containers and is often referred to as a "sidecar" container. + Although this init container still starts in the init container + sequence, it does not wait for the container to complete before + proceeding to the next init container. Instead, the next init + container starts immediately after this init container is + started, or after any startupProbe has successfully completed.' + type: string securityContext: description: 'SecurityContext defines the security options the container should be run with. If set, the fields of SecurityContext @@ -1811,8 +1916,8 @@ spec: in a file on the node should be used. The profile must be preconfigured on the node to work. Must be a descending path, relative to the kubelet's configured - seccomp profile location. Must only be set if type - is "Localhost". + seccomp profile location. Must be set if type is "Localhost". + Must NOT be set for any other type. type: string type: description: "type indicates which kind of seccomp profile @@ -1845,16 +1950,12 @@ spec: type: string hostProcess: description: HostProcess determines if a container should - be run as a 'Host Process' container. This field is - alpha-level and will only be honored by components - that enable the WindowsHostProcessContainers feature - flag. Setting this field without the feature flag - will result in errors when validating the Pod. All - of a Pod's containers must have the same effective - HostProcess value (it is not allowed to have a mix - of HostProcess containers and non-HostProcess containers). In - addition, if HostProcess is true then HostNetwork - must also be set to true. + be run as a 'Host Process' container. All of a Pod's + containers must have the same effective HostProcess + value (it is not allowed to have a mix of HostProcess + containers and non-HostProcess containers). In addition, + if HostProcess is true then HostNetwork must also + be set to true. type: boolean runAsUserName: description: The UserName in Windows to run the entrypoint @@ -1900,8 +2001,6 @@ spec: type: integer grpc: description: GRPC specifies an action involving a GRPC port. - This is a beta field and requires enabling GRPCContainerProbe - feature gate. properties: port: description: Port number of the gRPC service. Number @@ -1933,7 +2032,9 @@ spec: to be used in HTTP probes properties: name: - description: The header field name + description: The header field name. This will + be canonicalized upon output, so case-variant + names will be understood as the same header. type: string value: description: The header field value