CFE-882: Route external certificate validation#1625
Conversation
|
@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. DetailsIn response to this:
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. |
|
Continued from #1549 |
|
/cc @alebedev87 |
|
/assign @alebedev87 |
|
/hold |
|
/assign @Miciah |
|
/unhold |
alebedev87
left a comment
There was a problem hiding this comment.
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.
alebedev87
left a comment
There was a problem hiding this comment.
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!
|
@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. |
|
@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! |
alebedev87
left a comment
There was a problem hiding this comment.
Another iteration, not fully done with the review yet.
|
/retest |
alebedev87
left a comment
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
Why the cert is considered changed here? Shouldn't we check whether the name of the external certificate changed before setting certChanged=true?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
I was wondering about the secret name, not the secret contents.
There was a problem hiding this comment.
Can the external certificate secret name change?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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".
There was a problem hiding this comment.
I do see you added a comment for this caveat to ValidateHostUpdate, but perhaps it deserves a code comment in certificateChangeRequiresAuth as well.
There was a problem hiding this comment.
Okay, I've added a comment for certificateChangeRequiresAuth as well
|
/lgtm |
5f39723 to
6cfa61a
Compare
| 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"))...) | ||
| } |
There was a problem hiding this comment.
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:
| 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.
There was a problem hiding this comment.
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
|
It looks like this suggestion from the original PR got dropped: #1549 (comment) |
| // 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 | ||
| } |
There was a problem hiding this comment.
Is the AllowExternalCertificates flag (or perhaps this whole struct) destined to be removed when the feature graduates from tech preview to GA?
There was a problem hiding this comment.
That's correct. The AllowExternalCertificates flag (along with RouteValidationOptions struct) will be removed while graduating from TP -> GA.
| // 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. |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
aa1e4b9 to
8015e1e
Compare
8015e1e to
e02155c
Compare
Update all route validation functions to parse TP featuregate and validate ExternalCertificate field on the route.
7bae080 to
6b9c3c0
Compare
|
@chiragkyal: all tests passed! Full PR test history. Your PR dashboard. DetailsInstructions 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. |
There was a problem hiding this comment.
| // we must assume that the secret content is changed, necessitating authentication. | |
| // we must assume that the secret content is changed, necessitating authorization. |
| 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 |
There was a problem hiding this comment.
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},
},|
Excellent! 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. |
|
[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 DetailsNeeds approval from an approver in each of these files:
Approvers can indicate their approval by writing |
|
/unhold |
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 validateexternalCertificateand the rbac required. Now it also does additional subject access review authorization checks to verify if the router SA has the correct permissions to parseexternalCertificate. It also adds a hard requirement that the secret referenced inexternalCertificatebe present prior to creating the route.ValidateHostUpdate()has also been updated since updating route host/subdomain is also affected by updatingcertificates on the route. Now
certificateChangeRequiresAuth()will always returntrueif the route has aexternalCertificateset (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 thecustom-hostsub-resource. Additional details can be found on the EP.