Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NE-1141: Adds logic for setting and deleting headers via Ingress Operator CR and Route Object. #438

Merged
merged 8 commits into from Aug 9, 2023

Conversation

miheer
Copy link
Contributor

@miheer miheer commented Dec 25, 2022

images/router/haproxy/conf/haproxy-config.template Sets the
http-response and http-request stanzas to set or delete custom HTTP
Response/Request headers.
pkg/cmd/infra/router/template.go This consists the logic to get the
value from the env var from router deployment and use it for
setting/deleting HTTP response/request headers.
pkg/router/template/plugin.go Adds new fields in the
TemplatePluginConfig struct to set or delete HTTP request/reponse
headers. These are needed to set the values we get from router
deployment env var.
pkg/router/template/router.go This consists of the logic extracting
header values from the route object and then set/delete
HTTP request/reponse headers.
pkg/router/template/router_test.go This consists of units test cases
to the logic to extract headers from route object and set/delete HTTP
request/reponse headers.
pkg/router/template/types.go This adds two fields HTTPResponseHeaders
and HTTPRequestHeaders of type []HTTPHeader in the ServiceAliasConfig
which is used in the haproxy-config.template to add the set/delete
http-reponse and http-request stanzas for the header values fetched from
the route object.
This also add a struct called HTTPHeader which helps setting the HTTP
response/request values in the http-reponse/http-request stanzas in the
haproxy-config.template.

Jira Number https://issues.redhat.com/browse/NE-982

@miheer miheer force-pushed the headers branch 2 times, most recently from fca6a85 to 54f0b6e Compare December 25, 2022 13:35
@miheer miheer changed the title Set/replace/delete/append Headers WIP: Set/replace/delete/append Headers Dec 25, 2022
@openshift-ci openshift-ci bot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Dec 25, 2022
@miheer miheer force-pushed the headers branch 2 times, most recently from efc4f2a to c312c22 Compare December 26, 2022 15:23
@miheer
Copy link
Contributor Author

miheer commented Dec 26, 2022

/test verify

@miheer miheer force-pushed the headers branch 4 times, most recently from ba2ad10 to 4c7e751 Compare December 27, 2022 07:26
@miheer miheer force-pushed the headers branch 9 times, most recently from 9ade113 to 066daad Compare January 13, 2023 06:12
@miheer miheer force-pushed the headers branch 2 times, most recently from 0a44eec to 975a0d9 Compare January 13, 2023 09:47
@miheer miheer force-pushed the headers branch 6 times, most recently from 7e2ad7a to 65b288d Compare February 15, 2023 00:04
// RFC 2616, section 4.2, states that the header name
// must be a valid token.
if !validTokenRE.MatchString(headerName) {
return fmt.Errorf("invalid HTTP header name: %v", headerName)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

Suggested change
return fmt.Errorf("invalid HTTP header name: %v", headerName)
return fmt.Errorf("invalid HTTP header name: %s", headerName)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

t.Errorf(" expected %s, got %s", tc.expectedValue, actualValue)
case tc.expectErrorMessage == "" && actualErrorMessage != nil && actualErrorMessage.Error() != "":
t.Fatalf("unexpected error: %v", actualErrorMessage)
case tc.expectErrorMessage != "" && actualErrorMessage != nil && actualErrorMessage.Error() == "":
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here you should be looking for the case where actualErrorMessage is nil or blank:

Suggested change
case tc.expectErrorMessage != "" && actualErrorMessage != nil && actualErrorMessage.Error() == "":
case tc.expectErrorMessage != "" && (actualErrorMessage == nil || actualErrorMessage.Error() == ""):

Copy link
Contributor Author

@miheer miheer Aug 4, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if actualErrorMessage == nil

then actualErrorMessage.Error() i.e nil.Error() will crash

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will stop processing the || conjunction if actualErrorMessage == nil.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok done

@miheer miheer force-pushed the headers branch 2 times, most recently from 30f3fa2 to fbe3330 Compare August 5, 2023 08:32
if err != nil {
return captureHeaders, fmt.Errorf("failed to decode percent encoding: %v", parts[1])
}
sanitizedHeaderValue := templateplugin.SanitizeHeaderValue(headerValue)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does the header value have to be sanitized? If it has a valid reason, why isn't the header name or action sanitized?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Header name goes through a regex check so a user can't provide an invalid value.
Header action also only 2 values are allowed via api so this check happens at api level.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The SanitizeHeaderValue is escaping single quotes, which we allow in the header name per the regex. Why don't we allow single quotes in the header value?

Copy link
Contributor

@Miciah Miciah Aug 9, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SanitizeHeaderValue doesn't actually sanitize, but rather it quotes the string (for this reason, I've suggested changing "sanitize" to "quote" a few times). I agree, it certainly seems like the header name can contain ' and therefore needs to be quoted in the configuration file, just as the value needs to be quoted.

func checkValidHeaderName(headerName string) error {
// RFC 2616, section 4.2, states that the header name
// must be a valid token.
if !validTokenRE.MatchString(headerName) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The validTokenRE is the same now as the regex for header name in https://github.com/openshift/library-go/pull/1449/files#diff-bfc6ba95cbc94986901ce1d0e3b74ee966499eab85a96e1d1ff763d2d74a7eeeR64

How are you going to make sure changes between these get propagated? I think you probably need to choose one or the other.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This point is valid. Both check the same chars. However I can add a regex check here to maintain consistency.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will put a comment here.


func checkValidAction(action string) error {
if action != string(routev1.Set) && action != string(routev1.Delete) {
return fmt.Errorf("invalid action %s", action)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a semi-colon between the error message and the error value, as customary. Unit tests will need to be updated too.

Suggested change
return fmt.Errorf("invalid action %s", action)
return fmt.Errorf("invalid action: %s", action)

var capture templateplugin.HTTPHeader
var err error
if len(in) == 0 {
return captureHeaders, fmt.Errorf("encoded header string not present.")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the only error message with a period at the end. Please remove.

Suggested change
return captureHeaders, fmt.Errorf("encoded header string not present.")
return captureHeaders, fmt.Errorf("encoded header string not present")

miheer and others added 6 commits August 9, 2023 18:45
gomfmt -w pkg/router/template/certmanager_test.go
…nd Route Object.

`images/router/haproxy/conf/haproxy-config.template`  Sets the
http-response and http-request stanzas to set or delete custom HTTP
Response/Request headers.
`pkg/cmd/infra/router/template.go` This consists the logic to get the
value from the env var from router deployment and use it for
setting/deleting HTTP response/request headers.
`pkg/router/template/plugin.go` Adds new fields in the
TemplatePluginConfig struct to set or delete HTTP request/reponse
headers. These are needed to set the values we get from router
deployment env var.
`pkg/router/template/router.go` This consists of the logic extracting
header values from the route object and then set/delete
HTTP request/reponse headers.
`pkg/router/template/router_test.go` This consists of units test cases
to the logic to extract headers from route object and set/delete HTTP
request/reponse headers.
`pkg/router/template/types.go` This adds two fields HTTPResponseHeaders
and HTTPRequestHeaders of type []HTTPHeader in the ServiceAliasConfig
which is used in the haproxy-config.template to add the set/delete
http-reponse and http-request stanzas for the header values fetched from
the route object.
This also add a struct called HTTPHeader which helps setting the HTTP
response/request values in the http-reponse/http-request stanzas in the
haproxy-config.template.
* pkg/router/router_test.go (TestConfigTemplate): Use Fatal, not Fatalf, to
print the error message from expectation.Match, which is not expected to be
a formatted string.
* pkg/router/router_test.go (TestConfigTemplate): Print the HAProxy config
file if the test case fails.
* pkg/router/router_test.go (TestMain): Define some global HTTP request and
response headers.
(TestConfigTemplate): Add test cases for global and route HTTP request and
response headers to verify that the template plugin properly quotes values.
(mustCreate): Add godoc comments, and add an httpHeaders field for
specifying spec.httpHeaders on the route.
(mustCreate): If the route name is empty, don't create a route.  Copy
httpHeaders from mustCreate to the route's spec.httpHeaders field.
(insecureBackendName): New helper, used by TestConfigTemplate.
* pkg/router/template/router.go (configsAreEqual): Add a comment explaining
why this function does not check the ServiceUnitNames, ActiveServiceUnits,
or ActiveEndpoints fields.
* pkg/router/template/router_test.go (Test_configsAreEqual): New test.
Verify that configsAreEqual behaves correctly.
@ShudiLi
Copy link
Member

ShudiLi commented Aug 9, 2023

/label qe-approved
If needed, I can list the test cases what I have done, thanks.

@openshift-ci openshift-ci bot added the qe-approved Signifies that QE has signed off on this PR label Aug 9, 2023
@miheer
Copy link
Contributor Author

miheer commented Aug 9, 2023

/retest

@miheer
Copy link
Contributor Author

miheer commented Aug 9, 2023

/label qe-approved If needed, I can list the test cases what I have done, thanks.

OK. If you can add them in a google doc and share us the link then that will be great.

Comment on lines 379 to 386
func checkValidHeaderName(headerName string) error {
// RFC 2616, section 4.2, states that the header name
// must be a valid token.
// Any changes made to regex of header name in route type in openshift/api and `validation.go` file in library-go shall be reflected here and
// vice-versa
permittedHeaderNameRE := regexp.MustCompile("^[-!#$%&'*+.0-9A-Z^_`a-z|~]+$")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't recompile the regexp every time the function is called. Don't use the word "shall". End your sentences with a period. Remember to add godoc.

Suggested change
func checkValidHeaderName(headerName string) error {
// RFC 2616, section 4.2, states that the header name
// must be a valid token.
// Any changes made to regex of header name in route type in openshift/api and `validation.go` file in library-go shall be reflected here and
// vice-versa
permittedHeaderNameRE := regexp.MustCompile("^[-!#$%&'*+.0-9A-Z^_`a-z|~]+$")
// permittedHeaderNameRE is a compiled regexp to match an HTTP header name
// as specified in RFC 2616, section 4.2.
// Any changes made to regex of header name in route type in openshift/api and `validation.go` file in library-go must be reflected here and
// vice versa.
var permittedHeaderNameRE = regexp.MustCompile("^[-!#$%&'*+.0-9A-Z^_`a-z|~]+$")
// checkValidHeaderName verifies that the given HTTP header name is valid.
func checkValidHeaderName(headerName string) error {

The header name needs to be sanitized as a single quote(') can be
provided by a user for a header name which is allowed.
However, passing a single quote in the http-request or http-reponse stanza
in the haproxy-config causes haproxy to fail with:
[ALERT]    (78703) : config : Error(s) found in configuration file : ./haproxy.cfg
[ALERT]    (78703) : config : Fatal errors found in configuration.
That is why escape and quote single quote present in a header value.
For example: X'-Forwarded-For will get sanitized to
'X'\''-Forwarded-For' using escaping and quoting.
@openshift-ci
Copy link
Contributor

openshift-ci bot commented Aug 9, 2023

@miheer: all tests passed!

Full PR test history. Your PR dashboard.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. I understand the commands that are listed here.

@candita
Copy link
Contributor

candita commented Aug 9, 2023

/approve
/lgtm

@openshift-ci openshift-ci bot added the lgtm Indicates that a PR is ready to be merged. label Aug 9, 2023
@openshift-ci
Copy link
Contributor

openshift-ci bot commented Aug 9, 2023

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: candita

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@openshift-ci openshift-ci bot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Aug 9, 2023
@openshift-merge-robot openshift-merge-robot merged commit 44a5304 into openshift:master Aug 9, 2023
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
approved Indicates a PR has been approved by an approver from all required OWNERS files. jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. lgtm Indicates that a PR is ready to be merged. qe-approved Signifies that QE has signed off on this PR
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

9 participants