Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ items:
- name: MixedCPUsAllocation
- name: NetworkLiveMigration
- name: NodeSwap
- name: RouteExternalCertificate
- name: SigstoreImageVerification
- name: VSphereControlPlaneMachineSet
- name: VSphereStaticIPs
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ status:
- name: MixedCPUsAllocation
- name: NetworkLiveMigration
- name: NodeSwap
- name: RouteExternalCertificate
- name: SigstoreImageVerification
- name: VSphereControlPlaneMachineSet
- name: VSphereStaticIPs
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ status:
- name: MixedCPUsAllocation
- name: NetworkLiveMigration
- name: NodeSwap
- name: RouteExternalCertificate
- name: SigstoreImageVerification
- name: VSphereControlPlaneMachineSet
- name: VSphereStaticIPs
Expand Down
9 changes: 0 additions & 9 deletions pkg/route/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,3 @@ import (
type SubjectAccessReviewCreator interface {
Create(ctx context.Context, sar *authorizationv1.SubjectAccessReview, opts metav1.CreateOptions) (*authorizationv1.SubjectAccessReview, error)
}

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

// AllowExternalCertificates option is set when the RouteExternalCertificate
// feature gate is enabled.
AllowExternalCertificates bool
}
38 changes: 17 additions & 21 deletions pkg/route/hostassignment/assignment.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,15 @@ type HostnameGenerator interface {
// AllocateHost allocates a host name ONLY if the route doesn't specify a subdomain wildcard policy and
// the host name on the route is empty and an allocator is configured.
// It must first allocate the shard and may return an error if shard allocation fails.
func AllocateHost(ctx context.Context, route *routev1.Route, sarc route.SubjectAccessReviewCreator, routeAllocator HostnameGenerator, opts route.RouteValidationOptions) field.ErrorList {
func AllocateHost(ctx context.Context, route *routev1.Route, sarc route.SubjectAccessReviewCreator, routeAllocator HostnameGenerator) field.ErrorList {
hostSet := len(route.Spec.Host) > 0
certSet := route.Spec.TLS != nil &&
(len(route.Spec.TLS.CACertificate) > 0 ||
len(route.Spec.TLS.Certificate) > 0 ||
len(route.Spec.TLS.DestinationCACertificate) > 0 ||
len(route.Spec.TLS.Key) > 0)

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

Expand Down Expand Up @@ -103,7 +103,7 @@ func AllocateHost(ctx context.Context, route *routev1.Route, sarc route.SubjectA
return nil
}

func hasCertificateInfo(tls *routev1.TLSConfig, opts route.RouteValidationOptions) bool {
func hasCertificateInfo(tls *routev1.TLSConfig) bool {
if tls == nil {
return false
}
Expand All @@ -112,7 +112,7 @@ func hasCertificateInfo(tls *routev1.TLSConfig, opts route.RouteValidationOption
len(tls.CACertificate) > 0 ||
len(tls.DestinationCACertificate) > 0

if opts.AllowExternalCertificates && tls.ExternalCertificate != nil {
if tls.ExternalCertificate != nil {
hasInfo = hasInfo || len(tls.ExternalCertificate.Name) > 0
}
return hasInfo
Expand All @@ -122,11 +122,11 @@ func hasCertificateInfo(tls *routev1.TLSConfig, opts route.RouteValidationOption
// Note: If (newer/updated) 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 authorization.
func certificateChangeRequiresAuth(route, older *routev1.Route, opts route.RouteValidationOptions) bool {
func certificateChangeRequiresAuth(route, older *routev1.Route) bool {
switch {
case route.Spec.TLS != nil && older.Spec.TLS != nil:
a, b := route.Spec.TLS, older.Spec.TLS
if !hasCertificateInfo(a, opts) {
if !hasCertificateInfo(a) {
// removing certificate info is allowed
return false
}
Expand All @@ -136,16 +136,14 @@ func certificateChangeRequiresAuth(route, older *routev1.Route, opts route.Route
a.DestinationCACertificate != b.DestinationCACertificate ||
a.Key != b.Key

if opts.AllowExternalCertificates {
if route.Spec.TLS.ExternalCertificate != nil {
certChanged = true
}
if route.Spec.TLS.ExternalCertificate != nil {
certChanged = true
}

return certChanged
case route.Spec.TLS != nil:
// using any default certificate is allowed
return hasCertificateInfo(route.Spec.TLS, opts)
return hasCertificateInfo(route.Spec.TLS)
default:
// all other cases we are not adding additional certificate info
return false
Expand Down Expand Up @@ -177,10 +175,10 @@ func validateImmutableField(newVal, oldVal interface{}, fldPath *field.Path, err
// since we cannot verify state of external secret object.
// Due to this it proceeds with the assumption that the certificate has changed
// when the route has externalCertificate set.
func ValidateHostUpdate(ctx context.Context, route, older *routev1.Route, sarc route.SubjectAccessReviewCreator, opts route.RouteValidationOptions) field.ErrorList {
func ValidateHostUpdate(ctx context.Context, route, older *routev1.Route, sarc route.SubjectAccessReviewCreator) field.ErrorList {
hostChanged := route.Spec.Host != older.Spec.Host
subdomainChanged := route.Spec.Subdomain != older.Spec.Subdomain
certChanged := certificateChangeRequiresAuth(route, older, opts)
certChanged := certificateChangeRequiresAuth(route, older)
if !hostChanged && !certChanged && !subdomainChanged {
return nil
}
Expand Down Expand Up @@ -251,14 +249,12 @@ func ValidateHostUpdate(ctx context.Context, route, older *routev1.Route, sarc r
errs = append(errs, validateImmutableField(route.Spec.TLS.DestinationCACertificate, older.Spec.TLS.DestinationCACertificate, field.NewPath("spec", "tls", "destinationCACertificate"), routeTLSPermissionErrMsg)...)
errs = append(errs, validateImmutableField(route.Spec.TLS.Key, older.Spec.TLS.Key, field.NewPath("spec", "tls", "key"), routeTLSPermissionErrMsg)...)

if opts.AllowExternalCertificates {
if route.Spec.TLS.ExternalCertificate == nil || older.Spec.TLS.ExternalCertificate == nil {
errs = append(errs, validateImmutableField(route.Spec.TLS.ExternalCertificate, older.Spec.TLS.ExternalCertificate, field.NewPath("spec", "tls", "externalCertificate"), routeTLSPermissionErrMsg)...)
} else {
// since the state of the external secret cannot be verified, return error (even when secret name remains unchanged)
// without performing immutability checks, if externalCertificate is set.
errs = append(errs, field.Invalid(field.NewPath("spec", "tls", "externalCertificate"), route.Spec.TLS.ExternalCertificate, routeTLSPermissionErrMsg))
}
if route.Spec.TLS.ExternalCertificate == nil || older.Spec.TLS.ExternalCertificate == nil {
errs = append(errs, validateImmutableField(route.Spec.TLS.ExternalCertificate, older.Spec.TLS.ExternalCertificate, field.NewPath("spec", "tls", "externalCertificate"), routeTLSPermissionErrMsg)...)
} else {
// since the state of the external secret cannot be verified, return error (even when secret name remains unchanged)
// without performing immutability checks, if externalCertificate is set.
errs = append(errs, field.Invalid(field.NewPath("spec", "tls", "externalCertificate"), route.Spec.TLS.ExternalCertificate, routeTLSPermissionErrMsg))
}
return errs
}
Expand Down
51 changes: 2 additions & 49 deletions pkg/route/hostassignment/assignment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (
"k8s.io/apiserver/pkg/endpoints/request"

routev1 "github.com/openshift/api/route/v1"
"github.com/openshift/library-go/pkg/route"
)

type testAllocator struct {
Expand Down Expand Up @@ -53,9 +52,6 @@ func TestHostWithWildcardPolicies(t *testing.T) {
expected string
expectedSubdomain string

// field for externalCertificate
opts route.RouteValidationOptions

errs int
allow bool
}{
Expand Down Expand Up @@ -139,8 +135,6 @@ func TestHostWithWildcardPolicies(t *testing.T) {
wildcardPolicy: routev1.WildcardPolicyNone,
allow: false,
errs: 1,

opts: route.RouteValidationOptions{AllowExternalCertificates: true},
},
{
name: "tls-permission-allowed-external-certificate",
Expand All @@ -149,8 +143,6 @@ func TestHostWithWildcardPolicies(t *testing.T) {
wildcardPolicy: routev1.WildcardPolicyNone,
allow: true,
errs: 0,

opts: route.RouteValidationOptions{AllowExternalCertificates: true},
},
{
name: "no-host-but-allowed",
Expand Down Expand Up @@ -293,7 +285,6 @@ func TestHostWithWildcardPolicies(t *testing.T) {
allow: false,
errs: 1, // AllocateHost() -> routeHostPermissionErrMsg

opts: route.RouteValidationOptions{AllowExternalCertificates: true},
},
{
name: "create-with-external-certificate-allowed",
Expand All @@ -303,8 +294,6 @@ func TestHostWithWildcardPolicies(t *testing.T) {
wildcardPolicy: routev1.WildcardPolicyNone,
allow: true,
errs: 0,

opts: route.RouteValidationOptions{AllowExternalCertificates: true},
},
{
name: "external-certificate-added-from-no-certificate-denied",
Expand All @@ -316,8 +305,6 @@ func TestHostWithWildcardPolicies(t *testing.T) {
wildcardPolicy: routev1.WildcardPolicyNone,
allow: false,
errs: 1,

opts: route.RouteValidationOptions{AllowExternalCertificates: true},
},
{
name: "external-certificate-added-from-no-certificate-allowed",
Expand All @@ -329,8 +316,6 @@ func TestHostWithWildcardPolicies(t *testing.T) {
wildcardPolicy: routev1.WildcardPolicyNone,
allow: true,
errs: 0,

opts: route.RouteValidationOptions{AllowExternalCertificates: true},
},
{
name: "external-certificate-added-from-nil-tls-denied",
Expand All @@ -342,8 +327,6 @@ func TestHostWithWildcardPolicies(t *testing.T) {
wildcardPolicy: routev1.WildcardPolicyNone,
allow: false,
errs: 1,

opts: route.RouteValidationOptions{AllowExternalCertificates: true},
},
{
name: "external-certificate-added-from-nil-tls-allowed",
Expand All @@ -355,8 +338,6 @@ func TestHostWithWildcardPolicies(t *testing.T) {
wildcardPolicy: routev1.WildcardPolicyNone,
allow: true,
errs: 0,

opts: route.RouteValidationOptions{AllowExternalCertificates: true},
},
{
name: "external-certificate-removed-and-set-no-certificate-denied",
Expand All @@ -369,8 +350,6 @@ func TestHostWithWildcardPolicies(t *testing.T) {
allow: false,
// removing certificate info is allowed
errs: 0,

opts: route.RouteValidationOptions{AllowExternalCertificates: true},
},
{
name: "external-certificate-removed-and-set-no-certificate-allowed",
Expand All @@ -383,8 +362,6 @@ func TestHostWithWildcardPolicies(t *testing.T) {
allow: true,
// removing certificate info is allowed
errs: 0,

opts: route.RouteValidationOptions{AllowExternalCertificates: true},
},
{
name: "external-certificate-removed-and-set-nil-tls-denied",
Expand All @@ -397,8 +374,6 @@ func TestHostWithWildcardPolicies(t *testing.T) {
allow: false,
// removing certificate info is allowed
errs: 0,

opts: route.RouteValidationOptions{AllowExternalCertificates: true},
},
{
name: "external-certificate-removed-and-set-nil-tls-allowed",
Expand All @@ -411,8 +386,6 @@ func TestHostWithWildcardPolicies(t *testing.T) {
allow: true,
// removing certificate info is allowed
errs: 0,

opts: route.RouteValidationOptions{AllowExternalCertificates: true},
},
{
name: "external-certificate-changed-to-certificate-denied",
Expand All @@ -424,8 +397,6 @@ func TestHostWithWildcardPolicies(t *testing.T) {
wildcardPolicy: routev1.WildcardPolicyNone,
allow: false,
errs: 2,

opts: route.RouteValidationOptions{AllowExternalCertificates: true},
},
{
name: "external-certificate-changed-to-certificate-allowed",
Expand All @@ -437,8 +408,6 @@ func TestHostWithWildcardPolicies(t *testing.T) {
wildcardPolicy: routev1.WildcardPolicyNone,
allow: true,
errs: 0,

opts: route.RouteValidationOptions{AllowExternalCertificates: true},
},
{
name: "certificate-changed-to-external-certificate-denied",
Expand All @@ -450,8 +419,6 @@ func TestHostWithWildcardPolicies(t *testing.T) {
wildcardPolicy: routev1.WildcardPolicyNone,
allow: false,
errs: 2,

opts: route.RouteValidationOptions{AllowExternalCertificates: true},
},
{
name: "certificate-changed-to-external-certificate-allowed",
Expand All @@ -463,8 +430,6 @@ func TestHostWithWildcardPolicies(t *testing.T) {
wildcardPolicy: routev1.WildcardPolicyNone,
allow: true,
errs: 0,

opts: route.RouteValidationOptions{AllowExternalCertificates: true},
},
{
name: "certificate-changed-to-external-certificate-denied-and-featuregate-is-not-set",
Expand All @@ -478,8 +443,6 @@ func TestHostWithWildcardPolicies(t *testing.T) {
wildcardPolicy: routev1.WildcardPolicyNone,
allow: false,
errs: 0,

opts: route.RouteValidationOptions{AllowExternalCertificates: false},
},
{
name: "certificate-changed-to-external-certificate-allowed-but-featuregate-is-not-set",
Expand All @@ -493,8 +456,6 @@ func TestHostWithWildcardPolicies(t *testing.T) {
wildcardPolicy: routev1.WildcardPolicyNone,
allow: true,
errs: 0,

opts: route.RouteValidationOptions{AllowExternalCertificates: false},
},
{
name: "external-certificate-changed-denied",
Expand All @@ -506,8 +467,6 @@ func TestHostWithWildcardPolicies(t *testing.T) {
wildcardPolicy: routev1.WildcardPolicyNone,
allow: false,
errs: 1,

opts: route.RouteValidationOptions{AllowExternalCertificates: true},
},
{
name: "external-certificate-changed-allowed",
Expand All @@ -519,8 +478,6 @@ func TestHostWithWildcardPolicies(t *testing.T) {
wildcardPolicy: routev1.WildcardPolicyNone,
allow: true,
errs: 0,

opts: route.RouteValidationOptions{AllowExternalCertificates: true},
},
{
name: "external-certificate-unchanged-denied",
Expand All @@ -533,8 +490,6 @@ func TestHostWithWildcardPolicies(t *testing.T) {
allow: false,
// permission is required even if referenced secret name remains unchanged
errs: 1,

opts: route.RouteValidationOptions{AllowExternalCertificates: true},
},
{
name: "external-certificate-unchanged-allowed",
Expand All @@ -546,8 +501,6 @@ func TestHostWithWildcardPolicies(t *testing.T) {
wildcardPolicy: routev1.WildcardPolicyNone,
allow: true,
errs: 0,

opts: route.RouteValidationOptions{AllowExternalCertificates: true},
},
{
name: "ca-certificate-unchanged",
Expand Down Expand Up @@ -702,9 +655,9 @@ func TestHostWithWildcardPolicies(t *testing.T) {
},
},
}
errs = ValidateHostUpdate(ctx, route, oldRoute, &testSAR{allow: tc.allow}, tc.opts)
errs = ValidateHostUpdate(ctx, route, oldRoute, &testSAR{allow: tc.allow})
} else {
errs = AllocateHost(ctx, route, &testSAR{allow: tc.allow}, testAllocator{}, tc.opts)
errs = AllocateHost(ctx, route, &testSAR{allow: tc.allow}, testAllocator{})
}

if route.Spec.Host != tc.expected {
Expand Down
Loading