Skip to content

CFE-882: Route external certificate validation#1625

Merged
openshift-merge-bot[bot] merged 2 commits intoopenshift:masterfrom
chiragkyal:feature/route-external-certificate
Apr 3, 2024
Merged

CFE-882: Route external certificate validation#1625
openshift-merge-bot[bot] merged 2 commits intoopenshift:masterfrom
chiragkyal:feature/route-external-certificate

Conversation

@chiragkyal
Copy link
Member

Description

Updates route validations based on openshift/enhancements#1307 which introduces a new field in the route API externalCertificate behind the TP feature gate.

Changes

  • ValidateRoute() has been updated to pass additional args (secrets lister and subjectaccessreviewer) to validate
    externalCertificate and the rbac required. Now it also does additional subject access review authorization checks to verify if the router SA has the correct permissions to parse externalCertificate. It also adds a hard requirement that the secret referenced in externalCertificate be present prior to creating the route.
  • ValidateHostUpdate() has also been updated since updating route host/subdomain is also affected by updating
    certificates on the route. Now certificateChangeRequiresAuth() will always return true if the route has a externalCertificate set (check EP for additional info).
  • ValidateHostExternalCertificate() has been introduced as part of the validations done during route updates, this function specifically checks if the user has the correct permissions on the custom-host sub-resource. Additional details can be found on the EP.

@openshift-ci-robot openshift-ci-robot added the jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. label Nov 22, 2023
@openshift-ci-robot
Copy link

openshift-ci-robot commented Nov 22, 2023

@chiragkyal: This pull request references CFE-882 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the sub-task to target the "4.15.0" version, but no target version was set.

Details

In response to this:

Description

Updates route validations based on openshift/enhancements#1307 which introduces a new field in the route API externalCertificate behind the TP feature gate.

Changes

  • ValidateRoute() has been updated to pass additional args (secrets lister and subjectaccessreviewer) to validate
    externalCertificate and the rbac required. Now it also does additional subject access review authorization checks to verify if the router SA has the correct permissions to parse externalCertificate. It also adds a hard requirement that the secret referenced in externalCertificate be present prior to creating the route.
  • ValidateHostUpdate() has also been updated since updating route host/subdomain is also affected by updating
    certificates on the route. Now certificateChangeRequiresAuth() will always return true if the route has a externalCertificate set (check EP for additional info).
  • ValidateHostExternalCertificate() has been introduced as part of the validations done during route updates, this function specifically checks if the user has the correct permissions on the custom-host sub-resource. Additional details can be found on the EP.

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.

@openshift-ci openshift-ci bot requested review from candita and frobware November 22, 2023 07:14
@chiragkyal
Copy link
Member Author

Continued from #1549

@chiragkyal
Copy link
Member Author

/cc @alebedev87

@chiragkyal
Copy link
Member Author

/assign @alebedev87

@candita
Copy link
Contributor

candita commented Nov 30, 2023

/hold

@openshift-ci openshift-ci bot added the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Nov 30, 2023
@candita
Copy link
Contributor

candita commented Dec 6, 2023

/assign @Miciah

@candita
Copy link
Contributor

candita commented Dec 6, 2023

/unhold

@openshift-ci openshift-ci bot removed the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Dec 6, 2023
Copy link
Contributor

@alebedev87 alebedev87 left a comment

Choose a reason for hiding this comment

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

Focus of this review was on the public functions and their signatures as they should be concise and meaningful. Also I took a glance at the test cases t understand the new logic but the test cases need some structural changes. Will have to do another re-iteration with the focus on the logic.

Copy link
Contributor

@alebedev87 alebedev87 left a comment

Choose a reason for hiding this comment

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

I see resolved comments which are said to be addressed but they are actually not. Maybe you forgot to push the latest changes? If yes, can you please do so? Thanks!

@chiragkyal
Copy link
Member Author

@alebedev87 that's weird, I do have the latest changes pushed. Can you access bb12c49 commit and let me know please, if it's visible.

@alebedev87
Copy link
Contributor

alebedev87 commented Jan 26, 2024

@chiragkyal : I can see the changes now, seems like a GitHub glitch as I refreshed the PR many times. Sorry about that, I continue the review!

Copy link
Contributor

@alebedev87 alebedev87 left a comment

Choose a reason for hiding this comment

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

Another iteration, not fully done with the review yet.

@chiragkyal
Copy link
Member Author

/retest

Copy link
Contributor

@alebedev87 alebedev87 left a comment

Choose a reason for hiding this comment

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

Last iteration, some questions and remarks. Overall I think I'm close to the end :)


if opts.AllowExternalCertificates && route.Spec.TLS != nil && older.Spec.TLS != nil {
if route.Spec.TLS.ExternalCertificate != nil || older.Spec.TLS.ExternalCertificate != nil {
certChanged = true
Copy link
Contributor

Choose a reason for hiding this comment

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

Why the cert is considered changed here? Shouldn't we check whether the name of the external certificate changed before setting certChanged=true?

Copy link
Member Author

Choose a reason for hiding this comment

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

Because we cannot definitively verify if the content of the secret that is referenced has been modified.

The reason this also mentioned in the EP:

If the old route or the new route uses .spec.tls.externalCertificate this validation will always have the precondition certificateChangeRequiresAuth() return true since we cannot definitively verify if the content of the secret that is referenced has been modified. Since the previous validation func (ValidateHostExternalCertificate()) would have already validation user permissions, we can safely make this assumption.

Copy link
Contributor

@alebedev87 alebedev87 Feb 8, 2024

Choose a reason for hiding this comment

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

I was wondering about the secret name, not the secret contents.

Copy link
Contributor

Choose a reason for hiding this comment

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

Can the external certificate secret name change?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah! The external certificate secret name can be changed, but the caveat here is even if the secret name remains the same, we need to assume that the secret content is changed, and hence, the certificate is changed.

Copy link
Contributor

@alebedev87 alebedev87 Feb 9, 2024

Choose a reason for hiding this comment

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

but the caveat here is even if the secret name remains the same, we need to assume that the secret content is changed

True, we have no choice here but treat this as "always changed".

Copy link
Contributor

Choose a reason for hiding this comment

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

I do see you added a comment for this caveat to ValidateHostUpdate, but perhaps it deserves a code comment in certificateChangeRequiresAuth as well.

Copy link
Member Author

Choose a reason for hiding this comment

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

Okay, I've added a comment for certificateChangeRequiresAuth as well

@alebedev87
Copy link
Contributor

/lgtm

@openshift-ci openshift-ci bot added the lgtm Indicates that a PR is ready to be merged. label Feb 9, 2024
@chiragkyal chiragkyal force-pushed the feature/route-external-certificate branch from 5f39723 to 6cfa61a Compare February 9, 2024 18:11
@openshift-ci openshift-ci bot removed the lgtm Indicates that a PR is ready to be merged. label Feb 9, 2024
Comment on lines 219 to 229
if opts.AllowExternalCertificates && route.Spec.TLS.ExternalCertificate != nil && older.Spec.TLS.ExternalCertificate != nil {
errs = append(errs, apimachineryvalidation.ValidateImmutableField(route.Spec.TLS.ExternalCertificate.Name, older.Spec.TLS.ExternalCertificate.Name, field.NewPath("spec", "tls", "externalCertificate"))...)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this test case correct?

		{
			name:           "no-certificate-changed-to-external-certificate-denied",
			host:           "host",
			expected:       "host",
			oldHost:        "host",
			tls:            &routev1.TLSConfig{Termination: routev1.TLSTerminationEdge, ExternalCertificate: &routev1.LocalObjectReference{Name: "b"}},
			oldTLS:         &routev1.TLSConfig{Termination: routev1.TLSTerminationEdge, ExternalCertificate: nil},
			wildcardPolicy: routev1.WildcardPolicyNone,
			allow:          false,
			errs:           1,

			opts: route.RouteValidationOptions{AllowExternalCertificates: true},
		},

The above test case fails if I add it to TestHostWithWildcardPolicies:

go test ./pkg/route/hostassignment -run TestHostWithWildcardPolicies
# github.com/openshift/library-go/pkg/route/hostassignment.test
guile: warning: failed to install locale
--- FAIL: TestHostWithWildcardPolicies (0.00s)
    --- FAIL: TestHostWithWildcardPolicies/no-certificate-changed-to-external-certificate-denied (0.00s)
        assignment_test.go:506: expected 1 errors, got 0: []

It seems to me that the logic in ValidateHostUpdate should be as follows:

Suggested change
if opts.AllowExternalCertificates && route.Spec.TLS.ExternalCertificate != nil && older.Spec.TLS.ExternalCertificate != nil {
errs = append(errs, apimachineryvalidation.ValidateImmutableField(route.Spec.TLS.ExternalCertificate.Name, older.Spec.TLS.ExternalCertificate.Name, field.NewPath("spec", "tls", "externalCertificate"))...)
}
if opts.AllowExternalCertificates {
if route.Spec.TLS.ExternalCertificate == nil || older.Spec.TLS.ExternalCertificate == nil {
errs = append(errs, apimachineryvalidation.ValidateImmutableField(route.Spec.TLS.ExternalCertificate, older.Spec.TLS.ExternalCertificate, field.NewPath("spec", "tls", "externalCertificate"))...)
} else {
errs = append(errs, apimachineryvalidation.ValidateImmutableField(route.Spec.TLS.ExternalCertificate.Name, older.Spec.TLS.ExternalCertificate.Name, field.NewPath("spec", "tls", "externalCertificate"))...)
}
}

You'll need to modify the "external-certificate-changed-to-certificate-denied" and "certificate-changed-to-external-certificate-denied" test cases to expect an additional error.

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks for the suggestion and finding this edge case. I've updated the logic in ValidateHostUpdate as suggested, and also added the above test case along with "no-certificate-changed-to-external-certificate-allowed" test into TestHostWithWildcardPolicies

@Miciah
Copy link
Contributor

Miciah commented Apr 1, 2024

It looks like this suggestion from the original PR got dropped: #1549 (comment)

Comment on lines 21 to 28
// RouteValidationOptions used to tweak how/what fields are validated. These
// options are propagated by the apiserver.
type RouteValidationOptions struct {

// AllowExternalCertificates option is set when RouteExternalCertificate
AllowExternalCertificates bool
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Is the AllowExternalCertificates flag (or perhaps this whole struct) destined to be removed when the feature graduates from tech preview to GA?

Copy link
Member Author

Choose a reason for hiding this comment

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

That's correct. The AllowExternalCertificates flag (along with RouteValidationOptions struct) will be removed while graduating from TP -> GA.

Comment on lines +13 to +17
// custom-host subresource of routes. This check is required to be done prior to ValidateHostUpdate()
// since updating hosts while using externalCertificate is contingent on the user having both these
// permissions. The ValidateHostUpdate() cannot differentiate if the certificate has changed since
// now the certificates will be present as a secret object, due to this it proceeds with the assumption
// that the certificate has changed when the route has externalCertificate set.
Copy link
Contributor

Choose a reason for hiding this comment

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

Ultimately, ValidateUpdate is going to call ValidateHostExternalCertificate and then call ValidateHostUpdate, right? This comment is helpful, but it still isn't quite clear why the logic in ValidateHostExternalCertificate cannot simply be added to ValidateHostUpdate.

Copy link
Contributor

Choose a reason for hiding this comment

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

Reminded me of my comment.

Copy link
Member Author

Choose a reason for hiding this comment

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

The EP did mention about invoking ValidateHostExternalCertificate prior to ValidateHostUpdate, but it's body lacks the actual reason why we decided to choose this approach.

As per our discussion over Slack and further investigation, I've added a TODO to consider merging ValidateHostExternalCertificate function into ValidateHostUpdate later.

@openshift-ci openshift-ci bot removed the lgtm Indicates that a PR is ready to be merged. label Apr 2, 2024
@chiragkyal chiragkyal force-pushed the feature/route-external-certificate branch from aa1e4b9 to 8015e1e Compare April 2, 2024 15:06
@chiragkyal chiragkyal force-pushed the feature/route-external-certificate branch from 8015e1e to e02155c Compare April 2, 2024 15:29
Update all route validation functions to parse TP featuregate
and validate ExternalCertificate field on the route.
@chiragkyal chiragkyal force-pushed the feature/route-external-certificate branch from 7bae080 to 6b9c3c0 Compare April 2, 2024 18:33
@openshift-ci
Copy link
Contributor

openshift-ci bot commented Apr 2, 2024

@chiragkyal: all tests passed!

Full PR test history. Your PR dashboard.

Details

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.

// certificateChangeRequiresAuth determines whether changes to the TLS certificate configuration require authentication.
// Note: If either route uses externalCertificate, this function always returns true, as we cannot definitively verify if
// the content of the referenced secret has been modified. Even if the secret name remains the same,
// we must assume that the secret content is changed, necessitating authentication.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
// we must assume that the secret content is changed, necessitating authentication.
// we must assume that the secret content is changed, necessitating authorization.

Copy link
Member Author

Choose a reason for hiding this comment

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

Covered in #1706

len(route.Spec.TLS.Key) > 0)

if opts.AllowExternalCertificates && route.Spec.TLS != nil && route.Spec.TLS.ExternalCertificate != nil {
certSet = certSet || len(route.Spec.TLS.ExternalCertificate.Name) > 0
Copy link
Contributor

Choose a reason for hiding this comment

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

I just noticed that the tests don't cover this case (i.e. creating a new route that specifies externalCertificate). These test cases would give you coverage:

		{
			name:           "create-with-external-certificate-denied",
			host:           "host",
			expected:       "host",
			tls:            &routev1.TLSConfig{Termination: routev1.TLSTerminationEdge, ExternalCertificate: &routev1.LocalObjectReference{Name: "b"}},
			wildcardPolicy: routev1.WildcardPolicyNone,
			allow:          false,
			errs:           1,

			opts: route.RouteValidationOptions{AllowExternalCertificates: true},
		},
		{
			name:           "create-with-external-certificate-allowed",
			host:           "host",
			expected:       "host",
			tls:            &routev1.TLSConfig{Termination: routev1.TLSTerminationEdge, ExternalCertificate: &routev1.LocalObjectReference{Name: "b"}},
			wildcardPolicy: routev1.WildcardPolicyNone,
			allow:          true,
			errs:           0,

			opts: route.RouteValidationOptions{AllowExternalCertificates: true},
		},

Copy link
Member Author

Choose a reason for hiding this comment

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

Covered in #1706

@Miciah
Copy link
Contributor

Miciah commented Apr 2, 2024

Excellent!
/approve
/lgtm

I added two comments since your last update, but they are very minor things. @alebedev87 might want to take another quick look after your most recent changes, so I'll add a hold just in case.
/hold

@openshift-ci openshift-ci bot added do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. lgtm Indicates that a PR is ready to be merged. labels Apr 2, 2024
@openshift-ci
Copy link
Contributor

openshift-ci bot commented Apr 2, 2024

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: alebedev87, chiragkyal, Miciah

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

The pull request process is described here

Details 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 Apr 2, 2024
@alebedev87
Copy link
Contributor

/unhold

@openshift-ci openshift-ci bot removed the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Apr 3, 2024
@openshift-merge-bot openshift-merge-bot bot merged commit 5a3db7d into openshift:master Apr 3, 2024
@chiragkyal chiragkyal deleted the feature/route-external-certificate branch April 3, 2024 11:41
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.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

Comments