diff --git a/apis/config/v1alpha1/common_types.go b/apis/config/v1alpha1/common_types.go index 8e0ffdb..82b1b33 100644 --- a/apis/config/v1alpha1/common_types.go +++ b/apis/config/v1alpha1/common_types.go @@ -858,6 +858,9 @@ type HTTPRequestRules struct { // The replacement does not modify the scheme, the authority and the query-string. // +optional ReplacePath []ReplacePath `json:"replacePath,omitempty"` + // ReplaceValue matches occurrences of a header value using a regex and replaces them with the specified format. + // +optional + ReplaceValue []ReplaceValue `json:"replaceValue,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 @@ -918,6 +921,17 @@ func (h *HTTPRequestRules) Model() (models.HTTPRequestRules, error) { }) } + for _, header := range h.ReplaceValue { + model = append(model, &models.HTTPRequestRule{ + Type: "replace-value", + HdrName: header.Name, + HdrMatch: header.MatchRegex, + HdrFormat: header.ReplaceFmt, + Cond: header.ConditionType, + CondTest: header.Condition, + }) + } + for _, deny := range h.Deny { if deny.Enabled { model = append(model, &models.HTTPRequestRule{ @@ -1077,6 +1091,16 @@ type ReplacePath struct { ReplaceFmt string `json:"replaceFmt"` } +type ReplaceValue struct { + Rule `json:",inline"` + // Name specifies the header name. + Name string `json:"name"` + // MatchRegex is a string pattern used to identify the header values 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/apis/config/v1alpha1/listen_types_test.go b/apis/config/v1alpha1/listen_types_test.go index 3737228..1ce157f 100644 --- a/apis/config/v1alpha1/listen_types_test.go +++ b/apis/config/v1alpha1/listen_types_test.go @@ -131,6 +131,13 @@ var _ = Describe("Listen", Label("type"), func() { Name: "Proxy", }, }, + ReplaceValue: []configv1alpha1.ReplaceValue{ + { + Name: "Host", + MatchRegex: "(.*):.*", + ReplaceFmt: "\\1", + }, + }, }, }, }, @@ -144,6 +151,7 @@ var _ = Describe("Listen", Label("type"), func() { Ω(p.String()).Should(ContainSubstring("http-request set-path /metrics if !{ ssl_fc }")) Ω(p.String()).Should(ContainSubstring("http-request del-header Proxy")) Ω(p.String()).Should(ContainSubstring("http-request del-header regex -m str")) + Ω(p.String()).Should(ContainSubstring("http-request replace-value Host (.*):.* \\1")) Ω(p.String()).Should(ContainSubstring("http-response set-header Strict-Transport-Security max-age=16000000; includeSubDomains; preload; if !{ ssl_fc }")) }) It("should create binds", func() { diff --git a/apis/config/v1alpha1/zz_generated.deepcopy.go b/apis/config/v1alpha1/zz_generated.deepcopy.go index 3671876..6cdeffb 100644 --- a/apis/config/v1alpha1/zz_generated.deepcopy.go +++ b/apis/config/v1alpha1/zz_generated.deepcopy.go @@ -838,6 +838,11 @@ func (in *HTTPRequestRules) DeepCopyInto(out *HTTPRequestRules) { *out = make([]ReplacePath, len(*in)) copy(*out, *in) } + if in.ReplaceValue != nil { + in, out := &in.ReplaceValue, &out.ReplaceValue + *out = make([]ReplaceValue, len(*in)) + copy(*out, *in) + } if in.Deny != nil { in, out := &in.Deny, &out.Deny *out = make([]Deny, len(*in)) @@ -1320,6 +1325,22 @@ func (in *ReplacePath) DeepCopy() *ReplacePath { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ReplaceValue) DeepCopyInto(out *ReplaceValue) { + *out = *in + out.Rule = in.Rule +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ReplaceValue. +func (in *ReplaceValue) DeepCopy() *ReplaceValue { + if in == nil { + return nil + } + out := new(ReplaceValue) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Resolver) DeepCopyInto(out *Resolver) { *out = *in diff --git a/docs/api-reference.md b/docs/api-reference.md index 7066d7c..56eeaa8 100644 --- a/docs/api-reference.md +++ b/docs/api-reference.md @@ -547,6 +547,7 @@ _Appears in:_ | `delHeader` _[HTTPDeleteHeaderRule](#httpdeleteheaderrule) array_ | DelHeader removes all HTTP header fields | | | | `redirect` _[Redirect](#redirect) array_ | Redirect performs an HTTP redirection based on a redirect rule. | | Optional: \{\}
| | `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. | | Optional: \{\}
| +| `replaceValue` _[ReplaceValue](#replacevalue) array_ | ReplaceValue matches occurrences of a header value using a regex and replaces them with the specified format. | | Optional: \{\}
| | `deny` _[Deny](#deny) array_ | 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: \{\}
| | `return` _[HTTPReturn](#httpreturn)_ | Return stops the evaluation of the rules and immediately returns a response. | | | @@ -909,6 +910,26 @@ _Appears in:_ | `replaceFmt` _string_ | ReplaceFmt defines the format string used to replace the values that match the pattern. | | | +#### ReplaceValue + + + + + + + +_Appears in:_ +- [HTTPRequestRules](#httprequestrules) + +| Field | Description | Default | Validation | +| --- | --- | --- | --- | +| `conditionType` _string_ | ConditionType specifies the type of the condition matching ('if' or 'unless') | | Enum: [if unless]
Optional: \{\}
| +| `condition` _string_ | Condition is a condition composed of ACLs. | | Optional: \{\}
| +| `name` _string_ | Name specifies the header name. | | | +| `matchRegex` _string_ | MatchRegex is a string pattern used to identify the header values that need to be replaced. | | | +| `replaceFmt` _string_ | ReplaceFmt defines the format string used to replace the values that match the pattern. | | | + + #### Resolver diff --git a/helm/haproxy-operator/crds/config.haproxy.com_backends.yaml b/helm/haproxy-operator/crds/config.haproxy.com_backends.yaml index 3b8e92a..1003021 100644 --- a/helm/haproxy-operator/crds/config.haproxy.com_backends.yaml +++ b/helm/haproxy-operator/crds/config.haproxy.com_backends.yaml @@ -788,6 +788,38 @@ spec: - replaceFmt type: object type: array + replaceValue: + description: ReplaceValue matches occurrences of a header value + using a regex and replaces them with the specified format. + 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 header values that need to be replaced. + type: string + name: + description: Name specifies the header name. + type: string + replaceFmt: + description: ReplaceFmt defines the format string used to + replace the values that match the pattern. + type: string + required: + - matchRegex + - name + - replaceFmt + type: object + type: array return: description: Return stops the evaluation of the rules and immediately returns a response. diff --git a/helm/haproxy-operator/crds/config.haproxy.com_frontends.yaml b/helm/haproxy-operator/crds/config.haproxy.com_frontends.yaml index 4fdde7b..95990ea 100644 --- a/helm/haproxy-operator/crds/config.haproxy.com_frontends.yaml +++ b/helm/haproxy-operator/crds/config.haproxy.com_frontends.yaml @@ -1116,6 +1116,38 @@ spec: - replaceFmt type: object type: array + replaceValue: + description: ReplaceValue matches occurrences of a header value + using a regex and replaces them with the specified format. + 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 header values that need to be replaced. + type: string + name: + description: Name specifies the header name. + type: string + replaceFmt: + description: ReplaceFmt defines the format string used to + replace the values that match the pattern. + type: string + required: + - matchRegex + - name + - replaceFmt + type: object + type: array return: description: Return stops the evaluation of the rules and immediately returns a response. diff --git a/helm/haproxy-operator/crds/config.haproxy.com_listens.yaml b/helm/haproxy-operator/crds/config.haproxy.com_listens.yaml index 82fe7d5..4bdf654 100644 --- a/helm/haproxy-operator/crds/config.haproxy.com_listens.yaml +++ b/helm/haproxy-operator/crds/config.haproxy.com_listens.yaml @@ -1314,6 +1314,38 @@ spec: - replaceFmt type: object type: array + replaceValue: + description: ReplaceValue matches occurrences of a header value + using a regex and replaces them with the specified format. + 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 header values that need to be replaced. + type: string + name: + description: Name specifies the header name. + type: string + replaceFmt: + description: ReplaceFmt defines the format string used to + replace the values that match the pattern. + type: string + required: + - matchRegex + - name + - replaceFmt + type: object + type: array return: description: Return stops the evaluation of the rules and immediately returns a response.