From 842f14af4c822c21c2c48d9fc2c60ea2d9139ef5 Mon Sep 17 00:00:00 2001 From: Ashish Amarnath Date: Wed, 19 Jun 2024 14:29:43 -0700 Subject: [PATCH 01/99] update go templates for TLSSpec for concierge and supervisor Signed-off-by: Ashish Amarnath --- .../authentication/v1alpha1/types_tls.go.tmpl | 18 ++++++++++++++++++ .../supervisor/idp/v1alpha1/types_tls.go.tmpl | 19 +++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/apis/concierge/authentication/v1alpha1/types_tls.go.tmpl b/apis/concierge/authentication/v1alpha1/types_tls.go.tmpl index 12231665d..e8916dfa5 100644 --- a/apis/concierge/authentication/v1alpha1/types_tls.go.tmpl +++ b/apis/concierge/authentication/v1alpha1/types_tls.go.tmpl @@ -3,9 +3,27 @@ package v1alpha1 +// CABundleSource provides a source for CA bundle used for client-side TLS verification. +type CABundleSource struct { + // Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + // Secrets must be of type kubernetes.io/tls or Opaque. + // For configmaps, the value associated with the key is not expected to be base64 encoded. + // +kubebuilder:validation:Enum=Secret;ConfigMap + Kind string `json:"kind"` + // Name of the secret or configmap from which to read the CA bundle. + // +kubebuilder:validation:MinLength=1 + Name string `json:"name"` + // Key within the secret or configmap from which to read the CA bundle. + // +kubebuilder:validation:MinLength=1 + Key string `json:"key"` +} + // Configuration for configuring TLS on various authenticators. type TLSSpec struct { // X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. // +optional CertificateAuthorityData string `json:"certificateAuthorityData,omitempty"` + // Reference to a CA bundle in a secret or a configmap. + // +optional + CertificateAuthorityDataSource *CABundleSource `json:"certificateAuthorityDataSource,omitempty"` } diff --git a/apis/supervisor/idp/v1alpha1/types_tls.go.tmpl b/apis/supervisor/idp/v1alpha1/types_tls.go.tmpl index 49b49373c..c0fc606f6 100644 --- a/apis/supervisor/idp/v1alpha1/types_tls.go.tmpl +++ b/apis/supervisor/idp/v1alpha1/types_tls.go.tmpl @@ -3,9 +3,28 @@ package v1alpha1 + +// CABundleSource provides a source for CA bundle used for client-side TLS verification. +type CABundleSource struct { + // Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + // Secrets must be of type kubernetes.io/tls or Opaque. + // For configmaps, the value associated with the key is not expected to be base64 encoded. + // +kubebuilder:validation:Enum=Secret;ConfigMap + Kind string `json:"kind"` + // Name of the secret or configmap from which to read the CA bundle. + // +kubebuilder:validation:MinLength=1 + Name string `json:"name"` + // Key within the secret or configmap from which to read the CA bundle. + // +kubebuilder:validation:MinLength=1 + Key string `json:"key"` +} + // TLSSpec provides TLS configuration for identity provider integration. type TLSSpec struct { // X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. // +optional CertificateAuthorityData string `json:"certificateAuthorityData,omitempty"` + // Reference to a CA bundle in a secret or a configmap. + // +optional + CertificateAuthorityDataSource *CABundleSource `json:"certificateAuthorityDataSource,omitempty"` } From 19c3f2cb04c47de0e32caf314b46953b7324a7ce Mon Sep 17 00:00:00 2001 From: Ashish Amarnath Date: Fri, 21 Jun 2024 13:39:20 -0700 Subject: [PATCH 02/99] run hack/update.sh Signed-off-by: Ashish Amarnath --- ...cierge.pinniped.dev_jwtauthenticators.yaml | 27 ++++++++++++ ...ge.pinniped.dev_webhookauthenticators.yaml | 27 ++++++++++++ ....dev_activedirectoryidentityproviders.yaml | 27 ++++++++++++ ....pinniped.dev_githubidentityproviders.yaml | 27 ++++++++++++ ...or.pinniped.dev_ldapidentityproviders.yaml | 27 ++++++++++++ ...or.pinniped.dev_oidcidentityproviders.yaml | 27 ++++++++++++ generated/1.24/README.adoc | 44 +++++++++++++++++++ .../authentication/v1alpha1/types_tls.go | 18 ++++++++ .../v1alpha1/zz_generated.deepcopy.go | 25 ++++++++++- .../apis/supervisor/idp/v1alpha1/types_tls.go | 19 ++++++++ .../idp/v1alpha1/zz_generated.deepcopy.go | 29 ++++++++++-- ...cierge.pinniped.dev_jwtauthenticators.yaml | 27 ++++++++++++ ...ge.pinniped.dev_webhookauthenticators.yaml | 27 ++++++++++++ ....dev_activedirectoryidentityproviders.yaml | 27 ++++++++++++ ....pinniped.dev_githubidentityproviders.yaml | 27 ++++++++++++ ...or.pinniped.dev_ldapidentityproviders.yaml | 27 ++++++++++++ ...or.pinniped.dev_oidcidentityproviders.yaml | 27 ++++++++++++ generated/1.25/README.adoc | 44 +++++++++++++++++++ .../authentication/v1alpha1/types_tls.go | 18 ++++++++ .../v1alpha1/zz_generated.deepcopy.go | 25 ++++++++++- .../apis/supervisor/idp/v1alpha1/types_tls.go | 19 ++++++++ .../idp/v1alpha1/zz_generated.deepcopy.go | 29 ++++++++++-- ...cierge.pinniped.dev_jwtauthenticators.yaml | 27 ++++++++++++ ...ge.pinniped.dev_webhookauthenticators.yaml | 27 ++++++++++++ ....dev_activedirectoryidentityproviders.yaml | 27 ++++++++++++ ....pinniped.dev_githubidentityproviders.yaml | 27 ++++++++++++ ...or.pinniped.dev_ldapidentityproviders.yaml | 27 ++++++++++++ ...or.pinniped.dev_oidcidentityproviders.yaml | 27 ++++++++++++ generated/1.26/README.adoc | 44 +++++++++++++++++++ .../authentication/v1alpha1/types_tls.go | 18 ++++++++ .../v1alpha1/zz_generated.deepcopy.go | 25 ++++++++++- .../apis/supervisor/idp/v1alpha1/types_tls.go | 19 ++++++++ .../idp/v1alpha1/zz_generated.deepcopy.go | 29 ++++++++++-- ...cierge.pinniped.dev_jwtauthenticators.yaml | 27 ++++++++++++ ...ge.pinniped.dev_webhookauthenticators.yaml | 27 ++++++++++++ ....dev_activedirectoryidentityproviders.yaml | 27 ++++++++++++ ....pinniped.dev_githubidentityproviders.yaml | 27 ++++++++++++ ...or.pinniped.dev_ldapidentityproviders.yaml | 27 ++++++++++++ ...or.pinniped.dev_oidcidentityproviders.yaml | 27 ++++++++++++ generated/1.27/README.adoc | 44 +++++++++++++++++++ .../authentication/v1alpha1/types_tls.go | 18 ++++++++ .../v1alpha1/zz_generated.deepcopy.go | 25 ++++++++++- .../apis/supervisor/idp/v1alpha1/types_tls.go | 19 ++++++++ .../idp/v1alpha1/zz_generated.deepcopy.go | 29 ++++++++++-- ...cierge.pinniped.dev_jwtauthenticators.yaml | 27 ++++++++++++ ...ge.pinniped.dev_webhookauthenticators.yaml | 27 ++++++++++++ ....dev_activedirectoryidentityproviders.yaml | 27 ++++++++++++ ....pinniped.dev_githubidentityproviders.yaml | 27 ++++++++++++ ...or.pinniped.dev_ldapidentityproviders.yaml | 27 ++++++++++++ ...or.pinniped.dev_oidcidentityproviders.yaml | 27 ++++++++++++ generated/1.28/README.adoc | 44 +++++++++++++++++++ .../authentication/v1alpha1/types_tls.go | 18 ++++++++ .../v1alpha1/zz_generated.deepcopy.go | 25 ++++++++++- .../apis/supervisor/idp/v1alpha1/types_tls.go | 19 ++++++++ .../idp/v1alpha1/zz_generated.deepcopy.go | 29 ++++++++++-- ...cierge.pinniped.dev_jwtauthenticators.yaml | 27 ++++++++++++ ...ge.pinniped.dev_webhookauthenticators.yaml | 27 ++++++++++++ ....dev_activedirectoryidentityproviders.yaml | 27 ++++++++++++ ....pinniped.dev_githubidentityproviders.yaml | 27 ++++++++++++ ...or.pinniped.dev_ldapidentityproviders.yaml | 27 ++++++++++++ ...or.pinniped.dev_oidcidentityproviders.yaml | 27 ++++++++++++ generated/1.29/README.adoc | 44 +++++++++++++++++++ .../authentication/v1alpha1/types_tls.go | 18 ++++++++ .../v1alpha1/zz_generated.deepcopy.go | 25 ++++++++++- .../apis/supervisor/idp/v1alpha1/types_tls.go | 19 ++++++++ .../idp/v1alpha1/zz_generated.deepcopy.go | 29 ++++++++++-- ...cierge.pinniped.dev_jwtauthenticators.yaml | 27 ++++++++++++ ...ge.pinniped.dev_webhookauthenticators.yaml | 27 ++++++++++++ ....dev_activedirectoryidentityproviders.yaml | 27 ++++++++++++ ....pinniped.dev_githubidentityproviders.yaml | 27 ++++++++++++ ...or.pinniped.dev_ldapidentityproviders.yaml | 27 ++++++++++++ ...or.pinniped.dev_oidcidentityproviders.yaml | 27 ++++++++++++ generated/1.30/README.adoc | 44 +++++++++++++++++++ .../authentication/v1alpha1/types_tls.go | 18 ++++++++ .../v1alpha1/zz_generated.deepcopy.go | 25 ++++++++++- .../apis/supervisor/idp/v1alpha1/types_tls.go | 19 ++++++++ .../idp/v1alpha1/zz_generated.deepcopy.go | 29 ++++++++++-- ...cierge.pinniped.dev_jwtauthenticators.yaml | 27 ++++++++++++ ...ge.pinniped.dev_webhookauthenticators.yaml | 27 ++++++++++++ ....dev_activedirectoryidentityproviders.yaml | 27 ++++++++++++ ....pinniped.dev_githubidentityproviders.yaml | 27 ++++++++++++ ...or.pinniped.dev_ldapidentityproviders.yaml | 27 ++++++++++++ ...or.pinniped.dev_oidcidentityproviders.yaml | 27 ++++++++++++ generated/latest/README.adoc | 44 +++++++++++++++++++ .../authentication/v1alpha1/types_tls.go | 18 ++++++++ .../v1alpha1/zz_generated.deepcopy.go | 25 ++++++++++- .../apis/supervisor/idp/v1alpha1/types_tls.go | 19 ++++++++ .../idp/v1alpha1/zz_generated.deepcopy.go | 29 ++++++++++-- 88 files changed, 2328 insertions(+), 48 deletions(-) diff --git a/deploy/concierge/authentication.concierge.pinniped.dev_jwtauthenticators.yaml b/deploy/concierge/authentication.concierge.pinniped.dev_jwtauthenticators.yaml index d59fcb783..1e503bda2 100644 --- a/deploy/concierge/authentication.concierge.pinniped.dev_jwtauthenticators.yaml +++ b/deploy/concierge/authentication.concierge.pinniped.dev_jwtauthenticators.yaml @@ -92,6 +92,33 @@ spec: description: X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. type: string + certificateAuthorityDataSource: + description: Reference to a CA bundle in a secret or a configmap. + properties: + key: + description: Key within the secret or configmap from which + to read the CA bundle. + minLength: 1 + type: string + kind: + description: |- + Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + enum: + - Secret + - ConfigMap + type: string + name: + description: Name of the secret or configmap from which to + read the CA bundle. + minLength: 1 + type: string + required: + - key + - kind + - name + type: object type: object required: - audience diff --git a/deploy/concierge/authentication.concierge.pinniped.dev_webhookauthenticators.yaml b/deploy/concierge/authentication.concierge.pinniped.dev_webhookauthenticators.yaml index 4ccd53770..b3b024977 100644 --- a/deploy/concierge/authentication.concierge.pinniped.dev_webhookauthenticators.yaml +++ b/deploy/concierge/authentication.concierge.pinniped.dev_webhookauthenticators.yaml @@ -63,6 +63,33 @@ spec: description: X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. type: string + certificateAuthorityDataSource: + description: Reference to a CA bundle in a secret or a configmap. + properties: + key: + description: Key within the secret or configmap from which + to read the CA bundle. + minLength: 1 + type: string + kind: + description: |- + Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + enum: + - Secret + - ConfigMap + type: string + name: + description: Name of the secret or configmap from which to + read the CA bundle. + minLength: 1 + type: string + required: + - key + - kind + - name + type: object type: object required: - endpoint diff --git a/deploy/supervisor/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml b/deploy/supervisor/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml index 062251102..8940aebb9 100644 --- a/deploy/supervisor/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml +++ b/deploy/supervisor/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml @@ -170,6 +170,33 @@ spec: description: X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. type: string + certificateAuthorityDataSource: + description: Reference to a CA bundle in a secret or a configmap. + properties: + key: + description: Key within the secret or configmap from which + to read the CA bundle. + minLength: 1 + type: string + kind: + description: |- + Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + enum: + - Secret + - ConfigMap + type: string + name: + description: Name of the secret or configmap from which to + read the CA bundle. + minLength: 1 + type: string + required: + - key + - kind + - name + type: object type: object userSearch: description: UserSearch contains the configuration for searching for diff --git a/deploy/supervisor/idp.supervisor.pinniped.dev_githubidentityproviders.yaml b/deploy/supervisor/idp.supervisor.pinniped.dev_githubidentityproviders.yaml index f93108700..39db773a2 100644 --- a/deploy/supervisor/idp.supervisor.pinniped.dev_githubidentityproviders.yaml +++ b/deploy/supervisor/idp.supervisor.pinniped.dev_githubidentityproviders.yaml @@ -225,6 +225,33 @@ spec: bundle). If omitted, a default set of system roots will be trusted. type: string + certificateAuthorityDataSource: + description: Reference to a CA bundle in a secret or a configmap. + properties: + key: + description: Key within the secret or configmap from which + to read the CA bundle. + minLength: 1 + type: string + kind: + description: |- + Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + enum: + - Secret + - ConfigMap + type: string + name: + description: Name of the secret or configmap from which + to read the CA bundle. + minLength: 1 + type: string + required: + - key + - kind + - name + type: object type: object type: object required: diff --git a/deploy/supervisor/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml b/deploy/supervisor/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml index 711e9a754..34f0cb92a 100644 --- a/deploy/supervisor/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml +++ b/deploy/supervisor/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml @@ -161,6 +161,33 @@ spec: description: X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. type: string + certificateAuthorityDataSource: + description: Reference to a CA bundle in a secret or a configmap. + properties: + key: + description: Key within the secret or configmap from which + to read the CA bundle. + minLength: 1 + type: string + kind: + description: |- + Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + enum: + - Secret + - ConfigMap + type: string + name: + description: Name of the secret or configmap from which to + read the CA bundle. + minLength: 1 + type: string + required: + - key + - kind + - name + type: object type: object userSearch: description: UserSearch contains the configuration for searching for diff --git a/deploy/supervisor/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml b/deploy/supervisor/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml index acfca1573..ff69c2d8c 100644 --- a/deploy/supervisor/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml +++ b/deploy/supervisor/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml @@ -211,6 +211,33 @@ spec: description: X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. type: string + certificateAuthorityDataSource: + description: Reference to a CA bundle in a secret or a configmap. + properties: + key: + description: Key within the secret or configmap from which + to read the CA bundle. + minLength: 1 + type: string + kind: + description: |- + Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + enum: + - Secret + - ConfigMap + type: string + name: + description: Name of the secret or configmap from which to + read the CA bundle. + minLength: 1 + type: string + required: + - key + - kind + - name + type: object type: object required: - client diff --git a/generated/1.24/README.adoc b/generated/1.24/README.adoc index 3311e31fc..9c8e11adf 100644 --- a/generated/1.24/README.adoc +++ b/generated/1.24/README.adoc @@ -23,6 +23,27 @@ Package v1alpha1 is the v1alpha1 version of the Pinniped concierge authenticatio +[id="{anchor_prefix}-go-pinniped-dev-generated-1-24-apis-concierge-authentication-v1alpha1-cabundlesource"] +==== CABundleSource + +CABundleSource provides a source for CA bundle used for client-side TLS verification. + +.Appears In: +**** +- xref:{anchor_prefix}-go-pinniped-dev-generated-1-24-apis-concierge-authentication-v1alpha1-tlsspec[$$TLSSpec$$] +**** + +[cols="25a,75a", options="header"] +|=== +| Field | Description +| *`kind`* __string__ | Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + +Secrets must be of type kubernetes.io/tls or Opaque. + +For configmaps, the value associated with the key is not expected to be base64 encoded. + +| *`name`* __string__ | Name of the secret or configmap from which to read the CA bundle. + +| *`key`* __string__ | Key within the secret or configmap from which to read the CA bundle. + +|=== + + [id="{anchor_prefix}-go-pinniped-dev-generated-1-24-apis-concierge-authentication-v1alpha1-jwtauthenticator"] ==== JWTAuthenticator @@ -137,6 +158,7 @@ Configuration for configuring TLS on various authenticators. |=== | Field | Description | *`certificateAuthorityData`* __string__ | X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. + +| *`certificateAuthorityDataSource`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-24-apis-concierge-authentication-v1alpha1-cabundlesource[$$CABundleSource$$]__ | Reference to a CA bundle in a secret or a configmap. + |=== @@ -1645,6 +1667,27 @@ Optional, when empty this defaults to "objectGUID". + |=== +[id="{anchor_prefix}-go-pinniped-dev-generated-1-24-apis-supervisor-idp-v1alpha1-cabundlesource"] +==== CABundleSource + +CABundleSource provides a source for CA bundle used for client-side TLS verification. + +.Appears In: +**** +- xref:{anchor_prefix}-go-pinniped-dev-generated-1-24-apis-supervisor-idp-v1alpha1-tlsspec[$$TLSSpec$$] +**** + +[cols="25a,75a", options="header"] +|=== +| Field | Description +| *`kind`* __string__ | Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + +Secrets must be of type kubernetes.io/tls or Opaque. + +For configmaps, the value associated with the key is not expected to be base64 encoded. + +| *`name`* __string__ | Name of the secret or configmap from which to read the CA bundle. + +| *`key`* __string__ | Key within the secret or configmap from which to read the CA bundle. + +|=== + + [id="{anchor_prefix}-go-pinniped-dev-generated-1-24-apis-supervisor-idp-v1alpha1-githubapiconfig"] ==== GitHubAPIConfig @@ -2401,6 +2444,7 @@ TLSSpec provides TLS configuration for identity provider integration. |=== | Field | Description | *`certificateAuthorityData`* __string__ | X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. + +| *`certificateAuthorityDataSource`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-24-apis-supervisor-idp-v1alpha1-cabundlesource[$$CABundleSource$$]__ | Reference to a CA bundle in a secret or a configmap. + |=== diff --git a/generated/1.24/apis/concierge/authentication/v1alpha1/types_tls.go b/generated/1.24/apis/concierge/authentication/v1alpha1/types_tls.go index 12231665d..e8916dfa5 100644 --- a/generated/1.24/apis/concierge/authentication/v1alpha1/types_tls.go +++ b/generated/1.24/apis/concierge/authentication/v1alpha1/types_tls.go @@ -3,9 +3,27 @@ package v1alpha1 +// CABundleSource provides a source for CA bundle used for client-side TLS verification. +type CABundleSource struct { + // Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + // Secrets must be of type kubernetes.io/tls or Opaque. + // For configmaps, the value associated with the key is not expected to be base64 encoded. + // +kubebuilder:validation:Enum=Secret;ConfigMap + Kind string `json:"kind"` + // Name of the secret or configmap from which to read the CA bundle. + // +kubebuilder:validation:MinLength=1 + Name string `json:"name"` + // Key within the secret or configmap from which to read the CA bundle. + // +kubebuilder:validation:MinLength=1 + Key string `json:"key"` +} + // Configuration for configuring TLS on various authenticators. type TLSSpec struct { // X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. // +optional CertificateAuthorityData string `json:"certificateAuthorityData,omitempty"` + // Reference to a CA bundle in a secret or a configmap. + // +optional + CertificateAuthorityDataSource *CABundleSource `json:"certificateAuthorityDataSource,omitempty"` } diff --git a/generated/1.24/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go b/generated/1.24/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go index 5d36cf81b..27cbcc844 100644 --- a/generated/1.24/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go +++ b/generated/1.24/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go @@ -13,6 +13,22 @@ import ( runtime "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CABundleSource) DeepCopyInto(out *CABundleSource) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CABundleSource. +func (in *CABundleSource) DeepCopy() *CABundleSource { + if in == nil { + return nil + } + out := new(CABundleSource) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *JWTAuthenticator) DeepCopyInto(out *JWTAuthenticator) { *out = *in @@ -81,7 +97,7 @@ func (in *JWTAuthenticatorSpec) DeepCopyInto(out *JWTAuthenticatorSpec) { if in.TLS != nil { in, out := &in.TLS, &out.TLS *out = new(TLSSpec) - **out = **in + (*in).DeepCopyInto(*out) } return } @@ -138,6 +154,11 @@ func (in *JWTTokenClaims) DeepCopy() *JWTTokenClaims { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TLSSpec) DeepCopyInto(out *TLSSpec) { *out = *in + if in.CertificateAuthorityDataSource != nil { + in, out := &in.CertificateAuthorityDataSource, &out.CertificateAuthorityDataSource + *out = new(CABundleSource) + **out = **in + } return } @@ -218,7 +239,7 @@ func (in *WebhookAuthenticatorSpec) DeepCopyInto(out *WebhookAuthenticatorSpec) if in.TLS != nil { in, out := &in.TLS, &out.TLS *out = new(TLSSpec) - **out = **in + (*in).DeepCopyInto(*out) } return } diff --git a/generated/1.24/apis/supervisor/idp/v1alpha1/types_tls.go b/generated/1.24/apis/supervisor/idp/v1alpha1/types_tls.go index 49b49373c..c0fc606f6 100644 --- a/generated/1.24/apis/supervisor/idp/v1alpha1/types_tls.go +++ b/generated/1.24/apis/supervisor/idp/v1alpha1/types_tls.go @@ -3,9 +3,28 @@ package v1alpha1 + +// CABundleSource provides a source for CA bundle used for client-side TLS verification. +type CABundleSource struct { + // Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + // Secrets must be of type kubernetes.io/tls or Opaque. + // For configmaps, the value associated with the key is not expected to be base64 encoded. + // +kubebuilder:validation:Enum=Secret;ConfigMap + Kind string `json:"kind"` + // Name of the secret or configmap from which to read the CA bundle. + // +kubebuilder:validation:MinLength=1 + Name string `json:"name"` + // Key within the secret or configmap from which to read the CA bundle. + // +kubebuilder:validation:MinLength=1 + Key string `json:"key"` +} + // TLSSpec provides TLS configuration for identity provider integration. type TLSSpec struct { // X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. // +optional CertificateAuthorityData string `json:"certificateAuthorityData,omitempty"` + // Reference to a CA bundle in a secret or a configmap. + // +optional + CertificateAuthorityDataSource *CABundleSource `json:"certificateAuthorityDataSource,omitempty"` } diff --git a/generated/1.24/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go b/generated/1.24/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go index e48860e82..41d44d226 100644 --- a/generated/1.24/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go +++ b/generated/1.24/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go @@ -129,7 +129,7 @@ func (in *ActiveDirectoryIdentityProviderSpec) DeepCopyInto(out *ActiveDirectory if in.TLS != nil { in, out := &in.TLS, &out.TLS *out = new(TLSSpec) - **out = **in + (*in).DeepCopyInto(*out) } out.Bind = in.Bind out.UserSearch = in.UserSearch @@ -203,6 +203,22 @@ func (in *ActiveDirectoryIdentityProviderUserSearchAttributes) DeepCopy() *Activ return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CABundleSource) DeepCopyInto(out *CABundleSource) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CABundleSource. +func (in *CABundleSource) DeepCopy() *CABundleSource { + if in == nil { + return nil + } + out := new(CABundleSource) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *GitHubAPIConfig) DeepCopyInto(out *GitHubAPIConfig) { *out = *in @@ -214,7 +230,7 @@ func (in *GitHubAPIConfig) DeepCopyInto(out *GitHubAPIConfig) { if in.TLS != nil { in, out := &in.TLS, &out.TLS *out = new(TLSSpec) - **out = **in + (*in).DeepCopyInto(*out) } return } @@ -534,7 +550,7 @@ func (in *LDAPIdentityProviderSpec) DeepCopyInto(out *LDAPIdentityProviderSpec) if in.TLS != nil { in, out := &in.TLS, &out.TLS *out = new(TLSSpec) - **out = **in + (*in).DeepCopyInto(*out) } out.Bind = in.Bind out.UserSearch = in.UserSearch @@ -740,7 +756,7 @@ func (in *OIDCIdentityProviderSpec) DeepCopyInto(out *OIDCIdentityProviderSpec) if in.TLS != nil { in, out := &in.TLS, &out.TLS *out = new(TLSSpec) - **out = **in + (*in).DeepCopyInto(*out) } in.AuthorizationConfig.DeepCopyInto(&out.AuthorizationConfig) in.Claims.DeepCopyInto(&out.Claims) @@ -800,6 +816,11 @@ func (in *Parameter) DeepCopy() *Parameter { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TLSSpec) DeepCopyInto(out *TLSSpec) { *out = *in + if in.CertificateAuthorityDataSource != nil { + in, out := &in.CertificateAuthorityDataSource, &out.CertificateAuthorityDataSource + *out = new(CABundleSource) + **out = **in + } return } diff --git a/generated/1.24/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml b/generated/1.24/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml index d59fcb783..1e503bda2 100644 --- a/generated/1.24/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml +++ b/generated/1.24/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml @@ -92,6 +92,33 @@ spec: description: X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. type: string + certificateAuthorityDataSource: + description: Reference to a CA bundle in a secret or a configmap. + properties: + key: + description: Key within the secret or configmap from which + to read the CA bundle. + minLength: 1 + type: string + kind: + description: |- + Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + enum: + - Secret + - ConfigMap + type: string + name: + description: Name of the secret or configmap from which to + read the CA bundle. + minLength: 1 + type: string + required: + - key + - kind + - name + type: object type: object required: - audience diff --git a/generated/1.24/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml b/generated/1.24/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml index 4ccd53770..b3b024977 100644 --- a/generated/1.24/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml +++ b/generated/1.24/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml @@ -63,6 +63,33 @@ spec: description: X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. type: string + certificateAuthorityDataSource: + description: Reference to a CA bundle in a secret or a configmap. + properties: + key: + description: Key within the secret or configmap from which + to read the CA bundle. + minLength: 1 + type: string + kind: + description: |- + Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + enum: + - Secret + - ConfigMap + type: string + name: + description: Name of the secret or configmap from which to + read the CA bundle. + minLength: 1 + type: string + required: + - key + - kind + - name + type: object type: object required: - endpoint diff --git a/generated/1.24/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml b/generated/1.24/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml index 062251102..8940aebb9 100644 --- a/generated/1.24/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml +++ b/generated/1.24/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml @@ -170,6 +170,33 @@ spec: description: X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. type: string + certificateAuthorityDataSource: + description: Reference to a CA bundle in a secret or a configmap. + properties: + key: + description: Key within the secret or configmap from which + to read the CA bundle. + minLength: 1 + type: string + kind: + description: |- + Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + enum: + - Secret + - ConfigMap + type: string + name: + description: Name of the secret or configmap from which to + read the CA bundle. + minLength: 1 + type: string + required: + - key + - kind + - name + type: object type: object userSearch: description: UserSearch contains the configuration for searching for diff --git a/generated/1.24/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml b/generated/1.24/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml index f93108700..39db773a2 100644 --- a/generated/1.24/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml +++ b/generated/1.24/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml @@ -225,6 +225,33 @@ spec: bundle). If omitted, a default set of system roots will be trusted. type: string + certificateAuthorityDataSource: + description: Reference to a CA bundle in a secret or a configmap. + properties: + key: + description: Key within the secret or configmap from which + to read the CA bundle. + minLength: 1 + type: string + kind: + description: |- + Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + enum: + - Secret + - ConfigMap + type: string + name: + description: Name of the secret or configmap from which + to read the CA bundle. + minLength: 1 + type: string + required: + - key + - kind + - name + type: object type: object type: object required: diff --git a/generated/1.24/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml b/generated/1.24/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml index 711e9a754..34f0cb92a 100644 --- a/generated/1.24/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml +++ b/generated/1.24/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml @@ -161,6 +161,33 @@ spec: description: X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. type: string + certificateAuthorityDataSource: + description: Reference to a CA bundle in a secret or a configmap. + properties: + key: + description: Key within the secret or configmap from which + to read the CA bundle. + minLength: 1 + type: string + kind: + description: |- + Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + enum: + - Secret + - ConfigMap + type: string + name: + description: Name of the secret or configmap from which to + read the CA bundle. + minLength: 1 + type: string + required: + - key + - kind + - name + type: object type: object userSearch: description: UserSearch contains the configuration for searching for diff --git a/generated/1.24/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml b/generated/1.24/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml index acfca1573..ff69c2d8c 100644 --- a/generated/1.24/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml +++ b/generated/1.24/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml @@ -211,6 +211,33 @@ spec: description: X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. type: string + certificateAuthorityDataSource: + description: Reference to a CA bundle in a secret or a configmap. + properties: + key: + description: Key within the secret or configmap from which + to read the CA bundle. + minLength: 1 + type: string + kind: + description: |- + Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + enum: + - Secret + - ConfigMap + type: string + name: + description: Name of the secret or configmap from which to + read the CA bundle. + minLength: 1 + type: string + required: + - key + - kind + - name + type: object type: object required: - client diff --git a/generated/1.25/README.adoc b/generated/1.25/README.adoc index 3ae558150..e3cc6695a 100644 --- a/generated/1.25/README.adoc +++ b/generated/1.25/README.adoc @@ -23,6 +23,27 @@ Package v1alpha1 is the v1alpha1 version of the Pinniped concierge authenticatio +[id="{anchor_prefix}-go-pinniped-dev-generated-1-25-apis-concierge-authentication-v1alpha1-cabundlesource"] +==== CABundleSource + +CABundleSource provides a source for CA bundle used for client-side TLS verification. + +.Appears In: +**** +- xref:{anchor_prefix}-go-pinniped-dev-generated-1-25-apis-concierge-authentication-v1alpha1-tlsspec[$$TLSSpec$$] +**** + +[cols="25a,75a", options="header"] +|=== +| Field | Description +| *`kind`* __string__ | Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + +Secrets must be of type kubernetes.io/tls or Opaque. + +For configmaps, the value associated with the key is not expected to be base64 encoded. + +| *`name`* __string__ | Name of the secret or configmap from which to read the CA bundle. + +| *`key`* __string__ | Key within the secret or configmap from which to read the CA bundle. + +|=== + + [id="{anchor_prefix}-go-pinniped-dev-generated-1-25-apis-concierge-authentication-v1alpha1-jwtauthenticator"] ==== JWTAuthenticator @@ -137,6 +158,7 @@ Configuration for configuring TLS on various authenticators. |=== | Field | Description | *`certificateAuthorityData`* __string__ | X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. + +| *`certificateAuthorityDataSource`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-25-apis-concierge-authentication-v1alpha1-cabundlesource[$$CABundleSource$$]__ | Reference to a CA bundle in a secret or a configmap. + |=== @@ -1645,6 +1667,27 @@ Optional, when empty this defaults to "objectGUID". + |=== +[id="{anchor_prefix}-go-pinniped-dev-generated-1-25-apis-supervisor-idp-v1alpha1-cabundlesource"] +==== CABundleSource + +CABundleSource provides a source for CA bundle used for client-side TLS verification. + +.Appears In: +**** +- xref:{anchor_prefix}-go-pinniped-dev-generated-1-25-apis-supervisor-idp-v1alpha1-tlsspec[$$TLSSpec$$] +**** + +[cols="25a,75a", options="header"] +|=== +| Field | Description +| *`kind`* __string__ | Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + +Secrets must be of type kubernetes.io/tls or Opaque. + +For configmaps, the value associated with the key is not expected to be base64 encoded. + +| *`name`* __string__ | Name of the secret or configmap from which to read the CA bundle. + +| *`key`* __string__ | Key within the secret or configmap from which to read the CA bundle. + +|=== + + [id="{anchor_prefix}-go-pinniped-dev-generated-1-25-apis-supervisor-idp-v1alpha1-githubapiconfig"] ==== GitHubAPIConfig @@ -2401,6 +2444,7 @@ TLSSpec provides TLS configuration for identity provider integration. |=== | Field | Description | *`certificateAuthorityData`* __string__ | X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. + +| *`certificateAuthorityDataSource`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-25-apis-supervisor-idp-v1alpha1-cabundlesource[$$CABundleSource$$]__ | Reference to a CA bundle in a secret or a configmap. + |=== diff --git a/generated/1.25/apis/concierge/authentication/v1alpha1/types_tls.go b/generated/1.25/apis/concierge/authentication/v1alpha1/types_tls.go index 12231665d..e8916dfa5 100644 --- a/generated/1.25/apis/concierge/authentication/v1alpha1/types_tls.go +++ b/generated/1.25/apis/concierge/authentication/v1alpha1/types_tls.go @@ -3,9 +3,27 @@ package v1alpha1 +// CABundleSource provides a source for CA bundle used for client-side TLS verification. +type CABundleSource struct { + // Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + // Secrets must be of type kubernetes.io/tls or Opaque. + // For configmaps, the value associated with the key is not expected to be base64 encoded. + // +kubebuilder:validation:Enum=Secret;ConfigMap + Kind string `json:"kind"` + // Name of the secret or configmap from which to read the CA bundle. + // +kubebuilder:validation:MinLength=1 + Name string `json:"name"` + // Key within the secret or configmap from which to read the CA bundle. + // +kubebuilder:validation:MinLength=1 + Key string `json:"key"` +} + // Configuration for configuring TLS on various authenticators. type TLSSpec struct { // X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. // +optional CertificateAuthorityData string `json:"certificateAuthorityData,omitempty"` + // Reference to a CA bundle in a secret or a configmap. + // +optional + CertificateAuthorityDataSource *CABundleSource `json:"certificateAuthorityDataSource,omitempty"` } diff --git a/generated/1.25/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go b/generated/1.25/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go index 5d36cf81b..27cbcc844 100644 --- a/generated/1.25/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go +++ b/generated/1.25/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go @@ -13,6 +13,22 @@ import ( runtime "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CABundleSource) DeepCopyInto(out *CABundleSource) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CABundleSource. +func (in *CABundleSource) DeepCopy() *CABundleSource { + if in == nil { + return nil + } + out := new(CABundleSource) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *JWTAuthenticator) DeepCopyInto(out *JWTAuthenticator) { *out = *in @@ -81,7 +97,7 @@ func (in *JWTAuthenticatorSpec) DeepCopyInto(out *JWTAuthenticatorSpec) { if in.TLS != nil { in, out := &in.TLS, &out.TLS *out = new(TLSSpec) - **out = **in + (*in).DeepCopyInto(*out) } return } @@ -138,6 +154,11 @@ func (in *JWTTokenClaims) DeepCopy() *JWTTokenClaims { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TLSSpec) DeepCopyInto(out *TLSSpec) { *out = *in + if in.CertificateAuthorityDataSource != nil { + in, out := &in.CertificateAuthorityDataSource, &out.CertificateAuthorityDataSource + *out = new(CABundleSource) + **out = **in + } return } @@ -218,7 +239,7 @@ func (in *WebhookAuthenticatorSpec) DeepCopyInto(out *WebhookAuthenticatorSpec) if in.TLS != nil { in, out := &in.TLS, &out.TLS *out = new(TLSSpec) - **out = **in + (*in).DeepCopyInto(*out) } return } diff --git a/generated/1.25/apis/supervisor/idp/v1alpha1/types_tls.go b/generated/1.25/apis/supervisor/idp/v1alpha1/types_tls.go index 49b49373c..c0fc606f6 100644 --- a/generated/1.25/apis/supervisor/idp/v1alpha1/types_tls.go +++ b/generated/1.25/apis/supervisor/idp/v1alpha1/types_tls.go @@ -3,9 +3,28 @@ package v1alpha1 + +// CABundleSource provides a source for CA bundle used for client-side TLS verification. +type CABundleSource struct { + // Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + // Secrets must be of type kubernetes.io/tls or Opaque. + // For configmaps, the value associated with the key is not expected to be base64 encoded. + // +kubebuilder:validation:Enum=Secret;ConfigMap + Kind string `json:"kind"` + // Name of the secret or configmap from which to read the CA bundle. + // +kubebuilder:validation:MinLength=1 + Name string `json:"name"` + // Key within the secret or configmap from which to read the CA bundle. + // +kubebuilder:validation:MinLength=1 + Key string `json:"key"` +} + // TLSSpec provides TLS configuration for identity provider integration. type TLSSpec struct { // X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. // +optional CertificateAuthorityData string `json:"certificateAuthorityData,omitempty"` + // Reference to a CA bundle in a secret or a configmap. + // +optional + CertificateAuthorityDataSource *CABundleSource `json:"certificateAuthorityDataSource,omitempty"` } diff --git a/generated/1.25/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go b/generated/1.25/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go index e48860e82..41d44d226 100644 --- a/generated/1.25/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go +++ b/generated/1.25/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go @@ -129,7 +129,7 @@ func (in *ActiveDirectoryIdentityProviderSpec) DeepCopyInto(out *ActiveDirectory if in.TLS != nil { in, out := &in.TLS, &out.TLS *out = new(TLSSpec) - **out = **in + (*in).DeepCopyInto(*out) } out.Bind = in.Bind out.UserSearch = in.UserSearch @@ -203,6 +203,22 @@ func (in *ActiveDirectoryIdentityProviderUserSearchAttributes) DeepCopy() *Activ return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CABundleSource) DeepCopyInto(out *CABundleSource) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CABundleSource. +func (in *CABundleSource) DeepCopy() *CABundleSource { + if in == nil { + return nil + } + out := new(CABundleSource) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *GitHubAPIConfig) DeepCopyInto(out *GitHubAPIConfig) { *out = *in @@ -214,7 +230,7 @@ func (in *GitHubAPIConfig) DeepCopyInto(out *GitHubAPIConfig) { if in.TLS != nil { in, out := &in.TLS, &out.TLS *out = new(TLSSpec) - **out = **in + (*in).DeepCopyInto(*out) } return } @@ -534,7 +550,7 @@ func (in *LDAPIdentityProviderSpec) DeepCopyInto(out *LDAPIdentityProviderSpec) if in.TLS != nil { in, out := &in.TLS, &out.TLS *out = new(TLSSpec) - **out = **in + (*in).DeepCopyInto(*out) } out.Bind = in.Bind out.UserSearch = in.UserSearch @@ -740,7 +756,7 @@ func (in *OIDCIdentityProviderSpec) DeepCopyInto(out *OIDCIdentityProviderSpec) if in.TLS != nil { in, out := &in.TLS, &out.TLS *out = new(TLSSpec) - **out = **in + (*in).DeepCopyInto(*out) } in.AuthorizationConfig.DeepCopyInto(&out.AuthorizationConfig) in.Claims.DeepCopyInto(&out.Claims) @@ -800,6 +816,11 @@ func (in *Parameter) DeepCopy() *Parameter { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TLSSpec) DeepCopyInto(out *TLSSpec) { *out = *in + if in.CertificateAuthorityDataSource != nil { + in, out := &in.CertificateAuthorityDataSource, &out.CertificateAuthorityDataSource + *out = new(CABundleSource) + **out = **in + } return } diff --git a/generated/1.25/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml b/generated/1.25/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml index d59fcb783..1e503bda2 100644 --- a/generated/1.25/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml +++ b/generated/1.25/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml @@ -92,6 +92,33 @@ spec: description: X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. type: string + certificateAuthorityDataSource: + description: Reference to a CA bundle in a secret or a configmap. + properties: + key: + description: Key within the secret or configmap from which + to read the CA bundle. + minLength: 1 + type: string + kind: + description: |- + Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + enum: + - Secret + - ConfigMap + type: string + name: + description: Name of the secret or configmap from which to + read the CA bundle. + minLength: 1 + type: string + required: + - key + - kind + - name + type: object type: object required: - audience diff --git a/generated/1.25/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml b/generated/1.25/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml index 4ccd53770..b3b024977 100644 --- a/generated/1.25/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml +++ b/generated/1.25/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml @@ -63,6 +63,33 @@ spec: description: X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. type: string + certificateAuthorityDataSource: + description: Reference to a CA bundle in a secret or a configmap. + properties: + key: + description: Key within the secret or configmap from which + to read the CA bundle. + minLength: 1 + type: string + kind: + description: |- + Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + enum: + - Secret + - ConfigMap + type: string + name: + description: Name of the secret or configmap from which to + read the CA bundle. + minLength: 1 + type: string + required: + - key + - kind + - name + type: object type: object required: - endpoint diff --git a/generated/1.25/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml b/generated/1.25/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml index 062251102..8940aebb9 100644 --- a/generated/1.25/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml +++ b/generated/1.25/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml @@ -170,6 +170,33 @@ spec: description: X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. type: string + certificateAuthorityDataSource: + description: Reference to a CA bundle in a secret or a configmap. + properties: + key: + description: Key within the secret or configmap from which + to read the CA bundle. + minLength: 1 + type: string + kind: + description: |- + Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + enum: + - Secret + - ConfigMap + type: string + name: + description: Name of the secret or configmap from which to + read the CA bundle. + minLength: 1 + type: string + required: + - key + - kind + - name + type: object type: object userSearch: description: UserSearch contains the configuration for searching for diff --git a/generated/1.25/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml b/generated/1.25/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml index f93108700..39db773a2 100644 --- a/generated/1.25/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml +++ b/generated/1.25/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml @@ -225,6 +225,33 @@ spec: bundle). If omitted, a default set of system roots will be trusted. type: string + certificateAuthorityDataSource: + description: Reference to a CA bundle in a secret or a configmap. + properties: + key: + description: Key within the secret or configmap from which + to read the CA bundle. + minLength: 1 + type: string + kind: + description: |- + Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + enum: + - Secret + - ConfigMap + type: string + name: + description: Name of the secret or configmap from which + to read the CA bundle. + minLength: 1 + type: string + required: + - key + - kind + - name + type: object type: object type: object required: diff --git a/generated/1.25/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml b/generated/1.25/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml index 711e9a754..34f0cb92a 100644 --- a/generated/1.25/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml +++ b/generated/1.25/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml @@ -161,6 +161,33 @@ spec: description: X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. type: string + certificateAuthorityDataSource: + description: Reference to a CA bundle in a secret or a configmap. + properties: + key: + description: Key within the secret or configmap from which + to read the CA bundle. + minLength: 1 + type: string + kind: + description: |- + Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + enum: + - Secret + - ConfigMap + type: string + name: + description: Name of the secret or configmap from which to + read the CA bundle. + minLength: 1 + type: string + required: + - key + - kind + - name + type: object type: object userSearch: description: UserSearch contains the configuration for searching for diff --git a/generated/1.25/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml b/generated/1.25/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml index acfca1573..ff69c2d8c 100644 --- a/generated/1.25/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml +++ b/generated/1.25/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml @@ -211,6 +211,33 @@ spec: description: X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. type: string + certificateAuthorityDataSource: + description: Reference to a CA bundle in a secret or a configmap. + properties: + key: + description: Key within the secret or configmap from which + to read the CA bundle. + minLength: 1 + type: string + kind: + description: |- + Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + enum: + - Secret + - ConfigMap + type: string + name: + description: Name of the secret or configmap from which to + read the CA bundle. + minLength: 1 + type: string + required: + - key + - kind + - name + type: object type: object required: - client diff --git a/generated/1.26/README.adoc b/generated/1.26/README.adoc index a3f58fc82..183d82ba9 100644 --- a/generated/1.26/README.adoc +++ b/generated/1.26/README.adoc @@ -23,6 +23,27 @@ Package v1alpha1 is the v1alpha1 version of the Pinniped concierge authenticatio +[id="{anchor_prefix}-go-pinniped-dev-generated-1-26-apis-concierge-authentication-v1alpha1-cabundlesource"] +==== CABundleSource + +CABundleSource provides a source for CA bundle used for client-side TLS verification. + +.Appears In: +**** +- xref:{anchor_prefix}-go-pinniped-dev-generated-1-26-apis-concierge-authentication-v1alpha1-tlsspec[$$TLSSpec$$] +**** + +[cols="25a,75a", options="header"] +|=== +| Field | Description +| *`kind`* __string__ | Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + +Secrets must be of type kubernetes.io/tls or Opaque. + +For configmaps, the value associated with the key is not expected to be base64 encoded. + +| *`name`* __string__ | Name of the secret or configmap from which to read the CA bundle. + +| *`key`* __string__ | Key within the secret or configmap from which to read the CA bundle. + +|=== + + [id="{anchor_prefix}-go-pinniped-dev-generated-1-26-apis-concierge-authentication-v1alpha1-jwtauthenticator"] ==== JWTAuthenticator @@ -137,6 +158,7 @@ Configuration for configuring TLS on various authenticators. |=== | Field | Description | *`certificateAuthorityData`* __string__ | X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. + +| *`certificateAuthorityDataSource`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-26-apis-concierge-authentication-v1alpha1-cabundlesource[$$CABundleSource$$]__ | Reference to a CA bundle in a secret or a configmap. + |=== @@ -1645,6 +1667,27 @@ Optional, when empty this defaults to "objectGUID". + |=== +[id="{anchor_prefix}-go-pinniped-dev-generated-1-26-apis-supervisor-idp-v1alpha1-cabundlesource"] +==== CABundleSource + +CABundleSource provides a source for CA bundle used for client-side TLS verification. + +.Appears In: +**** +- xref:{anchor_prefix}-go-pinniped-dev-generated-1-26-apis-supervisor-idp-v1alpha1-tlsspec[$$TLSSpec$$] +**** + +[cols="25a,75a", options="header"] +|=== +| Field | Description +| *`kind`* __string__ | Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + +Secrets must be of type kubernetes.io/tls or Opaque. + +For configmaps, the value associated with the key is not expected to be base64 encoded. + +| *`name`* __string__ | Name of the secret or configmap from which to read the CA bundle. + +| *`key`* __string__ | Key within the secret or configmap from which to read the CA bundle. + +|=== + + [id="{anchor_prefix}-go-pinniped-dev-generated-1-26-apis-supervisor-idp-v1alpha1-githubapiconfig"] ==== GitHubAPIConfig @@ -2401,6 +2444,7 @@ TLSSpec provides TLS configuration for identity provider integration. |=== | Field | Description | *`certificateAuthorityData`* __string__ | X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. + +| *`certificateAuthorityDataSource`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-26-apis-supervisor-idp-v1alpha1-cabundlesource[$$CABundleSource$$]__ | Reference to a CA bundle in a secret or a configmap. + |=== diff --git a/generated/1.26/apis/concierge/authentication/v1alpha1/types_tls.go b/generated/1.26/apis/concierge/authentication/v1alpha1/types_tls.go index 12231665d..e8916dfa5 100644 --- a/generated/1.26/apis/concierge/authentication/v1alpha1/types_tls.go +++ b/generated/1.26/apis/concierge/authentication/v1alpha1/types_tls.go @@ -3,9 +3,27 @@ package v1alpha1 +// CABundleSource provides a source for CA bundle used for client-side TLS verification. +type CABundleSource struct { + // Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + // Secrets must be of type kubernetes.io/tls or Opaque. + // For configmaps, the value associated with the key is not expected to be base64 encoded. + // +kubebuilder:validation:Enum=Secret;ConfigMap + Kind string `json:"kind"` + // Name of the secret or configmap from which to read the CA bundle. + // +kubebuilder:validation:MinLength=1 + Name string `json:"name"` + // Key within the secret or configmap from which to read the CA bundle. + // +kubebuilder:validation:MinLength=1 + Key string `json:"key"` +} + // Configuration for configuring TLS on various authenticators. type TLSSpec struct { // X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. // +optional CertificateAuthorityData string `json:"certificateAuthorityData,omitempty"` + // Reference to a CA bundle in a secret or a configmap. + // +optional + CertificateAuthorityDataSource *CABundleSource `json:"certificateAuthorityDataSource,omitempty"` } diff --git a/generated/1.26/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go b/generated/1.26/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go index 5d36cf81b..27cbcc844 100644 --- a/generated/1.26/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go +++ b/generated/1.26/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go @@ -13,6 +13,22 @@ import ( runtime "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CABundleSource) DeepCopyInto(out *CABundleSource) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CABundleSource. +func (in *CABundleSource) DeepCopy() *CABundleSource { + if in == nil { + return nil + } + out := new(CABundleSource) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *JWTAuthenticator) DeepCopyInto(out *JWTAuthenticator) { *out = *in @@ -81,7 +97,7 @@ func (in *JWTAuthenticatorSpec) DeepCopyInto(out *JWTAuthenticatorSpec) { if in.TLS != nil { in, out := &in.TLS, &out.TLS *out = new(TLSSpec) - **out = **in + (*in).DeepCopyInto(*out) } return } @@ -138,6 +154,11 @@ func (in *JWTTokenClaims) DeepCopy() *JWTTokenClaims { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TLSSpec) DeepCopyInto(out *TLSSpec) { *out = *in + if in.CertificateAuthorityDataSource != nil { + in, out := &in.CertificateAuthorityDataSource, &out.CertificateAuthorityDataSource + *out = new(CABundleSource) + **out = **in + } return } @@ -218,7 +239,7 @@ func (in *WebhookAuthenticatorSpec) DeepCopyInto(out *WebhookAuthenticatorSpec) if in.TLS != nil { in, out := &in.TLS, &out.TLS *out = new(TLSSpec) - **out = **in + (*in).DeepCopyInto(*out) } return } diff --git a/generated/1.26/apis/supervisor/idp/v1alpha1/types_tls.go b/generated/1.26/apis/supervisor/idp/v1alpha1/types_tls.go index 49b49373c..c0fc606f6 100644 --- a/generated/1.26/apis/supervisor/idp/v1alpha1/types_tls.go +++ b/generated/1.26/apis/supervisor/idp/v1alpha1/types_tls.go @@ -3,9 +3,28 @@ package v1alpha1 + +// CABundleSource provides a source for CA bundle used for client-side TLS verification. +type CABundleSource struct { + // Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + // Secrets must be of type kubernetes.io/tls or Opaque. + // For configmaps, the value associated with the key is not expected to be base64 encoded. + // +kubebuilder:validation:Enum=Secret;ConfigMap + Kind string `json:"kind"` + // Name of the secret or configmap from which to read the CA bundle. + // +kubebuilder:validation:MinLength=1 + Name string `json:"name"` + // Key within the secret or configmap from which to read the CA bundle. + // +kubebuilder:validation:MinLength=1 + Key string `json:"key"` +} + // TLSSpec provides TLS configuration for identity provider integration. type TLSSpec struct { // X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. // +optional CertificateAuthorityData string `json:"certificateAuthorityData,omitempty"` + // Reference to a CA bundle in a secret or a configmap. + // +optional + CertificateAuthorityDataSource *CABundleSource `json:"certificateAuthorityDataSource,omitempty"` } diff --git a/generated/1.26/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go b/generated/1.26/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go index e48860e82..41d44d226 100644 --- a/generated/1.26/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go +++ b/generated/1.26/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go @@ -129,7 +129,7 @@ func (in *ActiveDirectoryIdentityProviderSpec) DeepCopyInto(out *ActiveDirectory if in.TLS != nil { in, out := &in.TLS, &out.TLS *out = new(TLSSpec) - **out = **in + (*in).DeepCopyInto(*out) } out.Bind = in.Bind out.UserSearch = in.UserSearch @@ -203,6 +203,22 @@ func (in *ActiveDirectoryIdentityProviderUserSearchAttributes) DeepCopy() *Activ return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CABundleSource) DeepCopyInto(out *CABundleSource) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CABundleSource. +func (in *CABundleSource) DeepCopy() *CABundleSource { + if in == nil { + return nil + } + out := new(CABundleSource) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *GitHubAPIConfig) DeepCopyInto(out *GitHubAPIConfig) { *out = *in @@ -214,7 +230,7 @@ func (in *GitHubAPIConfig) DeepCopyInto(out *GitHubAPIConfig) { if in.TLS != nil { in, out := &in.TLS, &out.TLS *out = new(TLSSpec) - **out = **in + (*in).DeepCopyInto(*out) } return } @@ -534,7 +550,7 @@ func (in *LDAPIdentityProviderSpec) DeepCopyInto(out *LDAPIdentityProviderSpec) if in.TLS != nil { in, out := &in.TLS, &out.TLS *out = new(TLSSpec) - **out = **in + (*in).DeepCopyInto(*out) } out.Bind = in.Bind out.UserSearch = in.UserSearch @@ -740,7 +756,7 @@ func (in *OIDCIdentityProviderSpec) DeepCopyInto(out *OIDCIdentityProviderSpec) if in.TLS != nil { in, out := &in.TLS, &out.TLS *out = new(TLSSpec) - **out = **in + (*in).DeepCopyInto(*out) } in.AuthorizationConfig.DeepCopyInto(&out.AuthorizationConfig) in.Claims.DeepCopyInto(&out.Claims) @@ -800,6 +816,11 @@ func (in *Parameter) DeepCopy() *Parameter { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TLSSpec) DeepCopyInto(out *TLSSpec) { *out = *in + if in.CertificateAuthorityDataSource != nil { + in, out := &in.CertificateAuthorityDataSource, &out.CertificateAuthorityDataSource + *out = new(CABundleSource) + **out = **in + } return } diff --git a/generated/1.26/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml b/generated/1.26/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml index d59fcb783..1e503bda2 100644 --- a/generated/1.26/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml +++ b/generated/1.26/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml @@ -92,6 +92,33 @@ spec: description: X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. type: string + certificateAuthorityDataSource: + description: Reference to a CA bundle in a secret or a configmap. + properties: + key: + description: Key within the secret or configmap from which + to read the CA bundle. + minLength: 1 + type: string + kind: + description: |- + Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + enum: + - Secret + - ConfigMap + type: string + name: + description: Name of the secret or configmap from which to + read the CA bundle. + minLength: 1 + type: string + required: + - key + - kind + - name + type: object type: object required: - audience diff --git a/generated/1.26/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml b/generated/1.26/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml index 4ccd53770..b3b024977 100644 --- a/generated/1.26/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml +++ b/generated/1.26/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml @@ -63,6 +63,33 @@ spec: description: X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. type: string + certificateAuthorityDataSource: + description: Reference to a CA bundle in a secret or a configmap. + properties: + key: + description: Key within the secret or configmap from which + to read the CA bundle. + minLength: 1 + type: string + kind: + description: |- + Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + enum: + - Secret + - ConfigMap + type: string + name: + description: Name of the secret or configmap from which to + read the CA bundle. + minLength: 1 + type: string + required: + - key + - kind + - name + type: object type: object required: - endpoint diff --git a/generated/1.26/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml b/generated/1.26/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml index 062251102..8940aebb9 100644 --- a/generated/1.26/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml +++ b/generated/1.26/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml @@ -170,6 +170,33 @@ spec: description: X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. type: string + certificateAuthorityDataSource: + description: Reference to a CA bundle in a secret or a configmap. + properties: + key: + description: Key within the secret or configmap from which + to read the CA bundle. + minLength: 1 + type: string + kind: + description: |- + Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + enum: + - Secret + - ConfigMap + type: string + name: + description: Name of the secret or configmap from which to + read the CA bundle. + minLength: 1 + type: string + required: + - key + - kind + - name + type: object type: object userSearch: description: UserSearch contains the configuration for searching for diff --git a/generated/1.26/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml b/generated/1.26/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml index f93108700..39db773a2 100644 --- a/generated/1.26/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml +++ b/generated/1.26/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml @@ -225,6 +225,33 @@ spec: bundle). If omitted, a default set of system roots will be trusted. type: string + certificateAuthorityDataSource: + description: Reference to a CA bundle in a secret or a configmap. + properties: + key: + description: Key within the secret or configmap from which + to read the CA bundle. + minLength: 1 + type: string + kind: + description: |- + Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + enum: + - Secret + - ConfigMap + type: string + name: + description: Name of the secret or configmap from which + to read the CA bundle. + minLength: 1 + type: string + required: + - key + - kind + - name + type: object type: object type: object required: diff --git a/generated/1.26/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml b/generated/1.26/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml index 711e9a754..34f0cb92a 100644 --- a/generated/1.26/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml +++ b/generated/1.26/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml @@ -161,6 +161,33 @@ spec: description: X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. type: string + certificateAuthorityDataSource: + description: Reference to a CA bundle in a secret or a configmap. + properties: + key: + description: Key within the secret or configmap from which + to read the CA bundle. + minLength: 1 + type: string + kind: + description: |- + Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + enum: + - Secret + - ConfigMap + type: string + name: + description: Name of the secret or configmap from which to + read the CA bundle. + minLength: 1 + type: string + required: + - key + - kind + - name + type: object type: object userSearch: description: UserSearch contains the configuration for searching for diff --git a/generated/1.26/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml b/generated/1.26/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml index acfca1573..ff69c2d8c 100644 --- a/generated/1.26/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml +++ b/generated/1.26/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml @@ -211,6 +211,33 @@ spec: description: X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. type: string + certificateAuthorityDataSource: + description: Reference to a CA bundle in a secret or a configmap. + properties: + key: + description: Key within the secret or configmap from which + to read the CA bundle. + minLength: 1 + type: string + kind: + description: |- + Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + enum: + - Secret + - ConfigMap + type: string + name: + description: Name of the secret or configmap from which to + read the CA bundle. + minLength: 1 + type: string + required: + - key + - kind + - name + type: object type: object required: - client diff --git a/generated/1.27/README.adoc b/generated/1.27/README.adoc index 3fd81787a..8b3958ac5 100644 --- a/generated/1.27/README.adoc +++ b/generated/1.27/README.adoc @@ -23,6 +23,27 @@ Package v1alpha1 is the v1alpha1 version of the Pinniped concierge authenticatio +[id="{anchor_prefix}-go-pinniped-dev-generated-1-27-apis-concierge-authentication-v1alpha1-cabundlesource"] +==== CABundleSource + +CABundleSource provides a source for CA bundle used for client-side TLS verification. + +.Appears In: +**** +- xref:{anchor_prefix}-go-pinniped-dev-generated-1-27-apis-concierge-authentication-v1alpha1-tlsspec[$$TLSSpec$$] +**** + +[cols="25a,75a", options="header"] +|=== +| Field | Description +| *`kind`* __string__ | Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + +Secrets must be of type kubernetes.io/tls or Opaque. + +For configmaps, the value associated with the key is not expected to be base64 encoded. + +| *`name`* __string__ | Name of the secret or configmap from which to read the CA bundle. + +| *`key`* __string__ | Key within the secret or configmap from which to read the CA bundle. + +|=== + + [id="{anchor_prefix}-go-pinniped-dev-generated-1-27-apis-concierge-authentication-v1alpha1-jwtauthenticator"] ==== JWTAuthenticator @@ -137,6 +158,7 @@ Configuration for configuring TLS on various authenticators. |=== | Field | Description | *`certificateAuthorityData`* __string__ | X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. + +| *`certificateAuthorityDataSource`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-27-apis-concierge-authentication-v1alpha1-cabundlesource[$$CABundleSource$$]__ | Reference to a CA bundle in a secret or a configmap. + |=== @@ -1645,6 +1667,27 @@ Optional, when empty this defaults to "objectGUID". + |=== +[id="{anchor_prefix}-go-pinniped-dev-generated-1-27-apis-supervisor-idp-v1alpha1-cabundlesource"] +==== CABundleSource + +CABundleSource provides a source for CA bundle used for client-side TLS verification. + +.Appears In: +**** +- xref:{anchor_prefix}-go-pinniped-dev-generated-1-27-apis-supervisor-idp-v1alpha1-tlsspec[$$TLSSpec$$] +**** + +[cols="25a,75a", options="header"] +|=== +| Field | Description +| *`kind`* __string__ | Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + +Secrets must be of type kubernetes.io/tls or Opaque. + +For configmaps, the value associated with the key is not expected to be base64 encoded. + +| *`name`* __string__ | Name of the secret or configmap from which to read the CA bundle. + +| *`key`* __string__ | Key within the secret or configmap from which to read the CA bundle. + +|=== + + [id="{anchor_prefix}-go-pinniped-dev-generated-1-27-apis-supervisor-idp-v1alpha1-githubapiconfig"] ==== GitHubAPIConfig @@ -2401,6 +2444,7 @@ TLSSpec provides TLS configuration for identity provider integration. |=== | Field | Description | *`certificateAuthorityData`* __string__ | X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. + +| *`certificateAuthorityDataSource`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-27-apis-supervisor-idp-v1alpha1-cabundlesource[$$CABundleSource$$]__ | Reference to a CA bundle in a secret or a configmap. + |=== diff --git a/generated/1.27/apis/concierge/authentication/v1alpha1/types_tls.go b/generated/1.27/apis/concierge/authentication/v1alpha1/types_tls.go index 12231665d..e8916dfa5 100644 --- a/generated/1.27/apis/concierge/authentication/v1alpha1/types_tls.go +++ b/generated/1.27/apis/concierge/authentication/v1alpha1/types_tls.go @@ -3,9 +3,27 @@ package v1alpha1 +// CABundleSource provides a source for CA bundle used for client-side TLS verification. +type CABundleSource struct { + // Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + // Secrets must be of type kubernetes.io/tls or Opaque. + // For configmaps, the value associated with the key is not expected to be base64 encoded. + // +kubebuilder:validation:Enum=Secret;ConfigMap + Kind string `json:"kind"` + // Name of the secret or configmap from which to read the CA bundle. + // +kubebuilder:validation:MinLength=1 + Name string `json:"name"` + // Key within the secret or configmap from which to read the CA bundle. + // +kubebuilder:validation:MinLength=1 + Key string `json:"key"` +} + // Configuration for configuring TLS on various authenticators. type TLSSpec struct { // X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. // +optional CertificateAuthorityData string `json:"certificateAuthorityData,omitempty"` + // Reference to a CA bundle in a secret or a configmap. + // +optional + CertificateAuthorityDataSource *CABundleSource `json:"certificateAuthorityDataSource,omitempty"` } diff --git a/generated/1.27/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go b/generated/1.27/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go index 5d36cf81b..27cbcc844 100644 --- a/generated/1.27/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go +++ b/generated/1.27/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go @@ -13,6 +13,22 @@ import ( runtime "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CABundleSource) DeepCopyInto(out *CABundleSource) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CABundleSource. +func (in *CABundleSource) DeepCopy() *CABundleSource { + if in == nil { + return nil + } + out := new(CABundleSource) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *JWTAuthenticator) DeepCopyInto(out *JWTAuthenticator) { *out = *in @@ -81,7 +97,7 @@ func (in *JWTAuthenticatorSpec) DeepCopyInto(out *JWTAuthenticatorSpec) { if in.TLS != nil { in, out := &in.TLS, &out.TLS *out = new(TLSSpec) - **out = **in + (*in).DeepCopyInto(*out) } return } @@ -138,6 +154,11 @@ func (in *JWTTokenClaims) DeepCopy() *JWTTokenClaims { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TLSSpec) DeepCopyInto(out *TLSSpec) { *out = *in + if in.CertificateAuthorityDataSource != nil { + in, out := &in.CertificateAuthorityDataSource, &out.CertificateAuthorityDataSource + *out = new(CABundleSource) + **out = **in + } return } @@ -218,7 +239,7 @@ func (in *WebhookAuthenticatorSpec) DeepCopyInto(out *WebhookAuthenticatorSpec) if in.TLS != nil { in, out := &in.TLS, &out.TLS *out = new(TLSSpec) - **out = **in + (*in).DeepCopyInto(*out) } return } diff --git a/generated/1.27/apis/supervisor/idp/v1alpha1/types_tls.go b/generated/1.27/apis/supervisor/idp/v1alpha1/types_tls.go index 49b49373c..c0fc606f6 100644 --- a/generated/1.27/apis/supervisor/idp/v1alpha1/types_tls.go +++ b/generated/1.27/apis/supervisor/idp/v1alpha1/types_tls.go @@ -3,9 +3,28 @@ package v1alpha1 + +// CABundleSource provides a source for CA bundle used for client-side TLS verification. +type CABundleSource struct { + // Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + // Secrets must be of type kubernetes.io/tls or Opaque. + // For configmaps, the value associated with the key is not expected to be base64 encoded. + // +kubebuilder:validation:Enum=Secret;ConfigMap + Kind string `json:"kind"` + // Name of the secret or configmap from which to read the CA bundle. + // +kubebuilder:validation:MinLength=1 + Name string `json:"name"` + // Key within the secret or configmap from which to read the CA bundle. + // +kubebuilder:validation:MinLength=1 + Key string `json:"key"` +} + // TLSSpec provides TLS configuration for identity provider integration. type TLSSpec struct { // X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. // +optional CertificateAuthorityData string `json:"certificateAuthorityData,omitempty"` + // Reference to a CA bundle in a secret or a configmap. + // +optional + CertificateAuthorityDataSource *CABundleSource `json:"certificateAuthorityDataSource,omitempty"` } diff --git a/generated/1.27/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go b/generated/1.27/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go index e48860e82..41d44d226 100644 --- a/generated/1.27/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go +++ b/generated/1.27/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go @@ -129,7 +129,7 @@ func (in *ActiveDirectoryIdentityProviderSpec) DeepCopyInto(out *ActiveDirectory if in.TLS != nil { in, out := &in.TLS, &out.TLS *out = new(TLSSpec) - **out = **in + (*in).DeepCopyInto(*out) } out.Bind = in.Bind out.UserSearch = in.UserSearch @@ -203,6 +203,22 @@ func (in *ActiveDirectoryIdentityProviderUserSearchAttributes) DeepCopy() *Activ return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CABundleSource) DeepCopyInto(out *CABundleSource) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CABundleSource. +func (in *CABundleSource) DeepCopy() *CABundleSource { + if in == nil { + return nil + } + out := new(CABundleSource) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *GitHubAPIConfig) DeepCopyInto(out *GitHubAPIConfig) { *out = *in @@ -214,7 +230,7 @@ func (in *GitHubAPIConfig) DeepCopyInto(out *GitHubAPIConfig) { if in.TLS != nil { in, out := &in.TLS, &out.TLS *out = new(TLSSpec) - **out = **in + (*in).DeepCopyInto(*out) } return } @@ -534,7 +550,7 @@ func (in *LDAPIdentityProviderSpec) DeepCopyInto(out *LDAPIdentityProviderSpec) if in.TLS != nil { in, out := &in.TLS, &out.TLS *out = new(TLSSpec) - **out = **in + (*in).DeepCopyInto(*out) } out.Bind = in.Bind out.UserSearch = in.UserSearch @@ -740,7 +756,7 @@ func (in *OIDCIdentityProviderSpec) DeepCopyInto(out *OIDCIdentityProviderSpec) if in.TLS != nil { in, out := &in.TLS, &out.TLS *out = new(TLSSpec) - **out = **in + (*in).DeepCopyInto(*out) } in.AuthorizationConfig.DeepCopyInto(&out.AuthorizationConfig) in.Claims.DeepCopyInto(&out.Claims) @@ -800,6 +816,11 @@ func (in *Parameter) DeepCopy() *Parameter { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TLSSpec) DeepCopyInto(out *TLSSpec) { *out = *in + if in.CertificateAuthorityDataSource != nil { + in, out := &in.CertificateAuthorityDataSource, &out.CertificateAuthorityDataSource + *out = new(CABundleSource) + **out = **in + } return } diff --git a/generated/1.27/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml b/generated/1.27/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml index d59fcb783..1e503bda2 100644 --- a/generated/1.27/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml +++ b/generated/1.27/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml @@ -92,6 +92,33 @@ spec: description: X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. type: string + certificateAuthorityDataSource: + description: Reference to a CA bundle in a secret or a configmap. + properties: + key: + description: Key within the secret or configmap from which + to read the CA bundle. + minLength: 1 + type: string + kind: + description: |- + Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + enum: + - Secret + - ConfigMap + type: string + name: + description: Name of the secret or configmap from which to + read the CA bundle. + minLength: 1 + type: string + required: + - key + - kind + - name + type: object type: object required: - audience diff --git a/generated/1.27/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml b/generated/1.27/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml index 4ccd53770..b3b024977 100644 --- a/generated/1.27/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml +++ b/generated/1.27/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml @@ -63,6 +63,33 @@ spec: description: X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. type: string + certificateAuthorityDataSource: + description: Reference to a CA bundle in a secret or a configmap. + properties: + key: + description: Key within the secret or configmap from which + to read the CA bundle. + minLength: 1 + type: string + kind: + description: |- + Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + enum: + - Secret + - ConfigMap + type: string + name: + description: Name of the secret or configmap from which to + read the CA bundle. + minLength: 1 + type: string + required: + - key + - kind + - name + type: object type: object required: - endpoint diff --git a/generated/1.27/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml b/generated/1.27/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml index 062251102..8940aebb9 100644 --- a/generated/1.27/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml +++ b/generated/1.27/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml @@ -170,6 +170,33 @@ spec: description: X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. type: string + certificateAuthorityDataSource: + description: Reference to a CA bundle in a secret or a configmap. + properties: + key: + description: Key within the secret or configmap from which + to read the CA bundle. + minLength: 1 + type: string + kind: + description: |- + Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + enum: + - Secret + - ConfigMap + type: string + name: + description: Name of the secret or configmap from which to + read the CA bundle. + minLength: 1 + type: string + required: + - key + - kind + - name + type: object type: object userSearch: description: UserSearch contains the configuration for searching for diff --git a/generated/1.27/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml b/generated/1.27/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml index f93108700..39db773a2 100644 --- a/generated/1.27/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml +++ b/generated/1.27/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml @@ -225,6 +225,33 @@ spec: bundle). If omitted, a default set of system roots will be trusted. type: string + certificateAuthorityDataSource: + description: Reference to a CA bundle in a secret or a configmap. + properties: + key: + description: Key within the secret or configmap from which + to read the CA bundle. + minLength: 1 + type: string + kind: + description: |- + Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + enum: + - Secret + - ConfigMap + type: string + name: + description: Name of the secret or configmap from which + to read the CA bundle. + minLength: 1 + type: string + required: + - key + - kind + - name + type: object type: object type: object required: diff --git a/generated/1.27/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml b/generated/1.27/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml index 711e9a754..34f0cb92a 100644 --- a/generated/1.27/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml +++ b/generated/1.27/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml @@ -161,6 +161,33 @@ spec: description: X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. type: string + certificateAuthorityDataSource: + description: Reference to a CA bundle in a secret or a configmap. + properties: + key: + description: Key within the secret or configmap from which + to read the CA bundle. + minLength: 1 + type: string + kind: + description: |- + Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + enum: + - Secret + - ConfigMap + type: string + name: + description: Name of the secret or configmap from which to + read the CA bundle. + minLength: 1 + type: string + required: + - key + - kind + - name + type: object type: object userSearch: description: UserSearch contains the configuration for searching for diff --git a/generated/1.27/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml b/generated/1.27/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml index acfca1573..ff69c2d8c 100644 --- a/generated/1.27/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml +++ b/generated/1.27/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml @@ -211,6 +211,33 @@ spec: description: X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. type: string + certificateAuthorityDataSource: + description: Reference to a CA bundle in a secret or a configmap. + properties: + key: + description: Key within the secret or configmap from which + to read the CA bundle. + minLength: 1 + type: string + kind: + description: |- + Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + enum: + - Secret + - ConfigMap + type: string + name: + description: Name of the secret or configmap from which to + read the CA bundle. + minLength: 1 + type: string + required: + - key + - kind + - name + type: object type: object required: - client diff --git a/generated/1.28/README.adoc b/generated/1.28/README.adoc index fb7366e47..913c0b5f7 100644 --- a/generated/1.28/README.adoc +++ b/generated/1.28/README.adoc @@ -23,6 +23,27 @@ Package v1alpha1 is the v1alpha1 version of the Pinniped concierge authenticatio +[id="{anchor_prefix}-go-pinniped-dev-generated-1-28-apis-concierge-authentication-v1alpha1-cabundlesource"] +==== CABundleSource + +CABundleSource provides a source for CA bundle used for client-side TLS verification. + +.Appears In: +**** +- xref:{anchor_prefix}-go-pinniped-dev-generated-1-28-apis-concierge-authentication-v1alpha1-tlsspec[$$TLSSpec$$] +**** + +[cols="25a,75a", options="header"] +|=== +| Field | Description +| *`kind`* __string__ | Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + +Secrets must be of type kubernetes.io/tls or Opaque. + +For configmaps, the value associated with the key is not expected to be base64 encoded. + +| *`name`* __string__ | Name of the secret or configmap from which to read the CA bundle. + +| *`key`* __string__ | Key within the secret or configmap from which to read the CA bundle. + +|=== + + [id="{anchor_prefix}-go-pinniped-dev-generated-1-28-apis-concierge-authentication-v1alpha1-jwtauthenticator"] ==== JWTAuthenticator @@ -137,6 +158,7 @@ Configuration for configuring TLS on various authenticators. |=== | Field | Description | *`certificateAuthorityData`* __string__ | X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. + +| *`certificateAuthorityDataSource`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-28-apis-concierge-authentication-v1alpha1-cabundlesource[$$CABundleSource$$]__ | Reference to a CA bundle in a secret or a configmap. + |=== @@ -1645,6 +1667,27 @@ Optional, when empty this defaults to "objectGUID". + |=== +[id="{anchor_prefix}-go-pinniped-dev-generated-1-28-apis-supervisor-idp-v1alpha1-cabundlesource"] +==== CABundleSource + +CABundleSource provides a source for CA bundle used for client-side TLS verification. + +.Appears In: +**** +- xref:{anchor_prefix}-go-pinniped-dev-generated-1-28-apis-supervisor-idp-v1alpha1-tlsspec[$$TLSSpec$$] +**** + +[cols="25a,75a", options="header"] +|=== +| Field | Description +| *`kind`* __string__ | Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + +Secrets must be of type kubernetes.io/tls or Opaque. + +For configmaps, the value associated with the key is not expected to be base64 encoded. + +| *`name`* __string__ | Name of the secret or configmap from which to read the CA bundle. + +| *`key`* __string__ | Key within the secret or configmap from which to read the CA bundle. + +|=== + + [id="{anchor_prefix}-go-pinniped-dev-generated-1-28-apis-supervisor-idp-v1alpha1-githubapiconfig"] ==== GitHubAPIConfig @@ -2401,6 +2444,7 @@ TLSSpec provides TLS configuration for identity provider integration. |=== | Field | Description | *`certificateAuthorityData`* __string__ | X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. + +| *`certificateAuthorityDataSource`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-28-apis-supervisor-idp-v1alpha1-cabundlesource[$$CABundleSource$$]__ | Reference to a CA bundle in a secret or a configmap. + |=== diff --git a/generated/1.28/apis/concierge/authentication/v1alpha1/types_tls.go b/generated/1.28/apis/concierge/authentication/v1alpha1/types_tls.go index 12231665d..e8916dfa5 100644 --- a/generated/1.28/apis/concierge/authentication/v1alpha1/types_tls.go +++ b/generated/1.28/apis/concierge/authentication/v1alpha1/types_tls.go @@ -3,9 +3,27 @@ package v1alpha1 +// CABundleSource provides a source for CA bundle used for client-side TLS verification. +type CABundleSource struct { + // Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + // Secrets must be of type kubernetes.io/tls or Opaque. + // For configmaps, the value associated with the key is not expected to be base64 encoded. + // +kubebuilder:validation:Enum=Secret;ConfigMap + Kind string `json:"kind"` + // Name of the secret or configmap from which to read the CA bundle. + // +kubebuilder:validation:MinLength=1 + Name string `json:"name"` + // Key within the secret or configmap from which to read the CA bundle. + // +kubebuilder:validation:MinLength=1 + Key string `json:"key"` +} + // Configuration for configuring TLS on various authenticators. type TLSSpec struct { // X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. // +optional CertificateAuthorityData string `json:"certificateAuthorityData,omitempty"` + // Reference to a CA bundle in a secret or a configmap. + // +optional + CertificateAuthorityDataSource *CABundleSource `json:"certificateAuthorityDataSource,omitempty"` } diff --git a/generated/1.28/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go b/generated/1.28/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go index 5d36cf81b..27cbcc844 100644 --- a/generated/1.28/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go +++ b/generated/1.28/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go @@ -13,6 +13,22 @@ import ( runtime "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CABundleSource) DeepCopyInto(out *CABundleSource) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CABundleSource. +func (in *CABundleSource) DeepCopy() *CABundleSource { + if in == nil { + return nil + } + out := new(CABundleSource) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *JWTAuthenticator) DeepCopyInto(out *JWTAuthenticator) { *out = *in @@ -81,7 +97,7 @@ func (in *JWTAuthenticatorSpec) DeepCopyInto(out *JWTAuthenticatorSpec) { if in.TLS != nil { in, out := &in.TLS, &out.TLS *out = new(TLSSpec) - **out = **in + (*in).DeepCopyInto(*out) } return } @@ -138,6 +154,11 @@ func (in *JWTTokenClaims) DeepCopy() *JWTTokenClaims { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TLSSpec) DeepCopyInto(out *TLSSpec) { *out = *in + if in.CertificateAuthorityDataSource != nil { + in, out := &in.CertificateAuthorityDataSource, &out.CertificateAuthorityDataSource + *out = new(CABundleSource) + **out = **in + } return } @@ -218,7 +239,7 @@ func (in *WebhookAuthenticatorSpec) DeepCopyInto(out *WebhookAuthenticatorSpec) if in.TLS != nil { in, out := &in.TLS, &out.TLS *out = new(TLSSpec) - **out = **in + (*in).DeepCopyInto(*out) } return } diff --git a/generated/1.28/apis/supervisor/idp/v1alpha1/types_tls.go b/generated/1.28/apis/supervisor/idp/v1alpha1/types_tls.go index 49b49373c..c0fc606f6 100644 --- a/generated/1.28/apis/supervisor/idp/v1alpha1/types_tls.go +++ b/generated/1.28/apis/supervisor/idp/v1alpha1/types_tls.go @@ -3,9 +3,28 @@ package v1alpha1 + +// CABundleSource provides a source for CA bundle used for client-side TLS verification. +type CABundleSource struct { + // Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + // Secrets must be of type kubernetes.io/tls or Opaque. + // For configmaps, the value associated with the key is not expected to be base64 encoded. + // +kubebuilder:validation:Enum=Secret;ConfigMap + Kind string `json:"kind"` + // Name of the secret or configmap from which to read the CA bundle. + // +kubebuilder:validation:MinLength=1 + Name string `json:"name"` + // Key within the secret or configmap from which to read the CA bundle. + // +kubebuilder:validation:MinLength=1 + Key string `json:"key"` +} + // TLSSpec provides TLS configuration for identity provider integration. type TLSSpec struct { // X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. // +optional CertificateAuthorityData string `json:"certificateAuthorityData,omitempty"` + // Reference to a CA bundle in a secret or a configmap. + // +optional + CertificateAuthorityDataSource *CABundleSource `json:"certificateAuthorityDataSource,omitempty"` } diff --git a/generated/1.28/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go b/generated/1.28/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go index e48860e82..41d44d226 100644 --- a/generated/1.28/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go +++ b/generated/1.28/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go @@ -129,7 +129,7 @@ func (in *ActiveDirectoryIdentityProviderSpec) DeepCopyInto(out *ActiveDirectory if in.TLS != nil { in, out := &in.TLS, &out.TLS *out = new(TLSSpec) - **out = **in + (*in).DeepCopyInto(*out) } out.Bind = in.Bind out.UserSearch = in.UserSearch @@ -203,6 +203,22 @@ func (in *ActiveDirectoryIdentityProviderUserSearchAttributes) DeepCopy() *Activ return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CABundleSource) DeepCopyInto(out *CABundleSource) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CABundleSource. +func (in *CABundleSource) DeepCopy() *CABundleSource { + if in == nil { + return nil + } + out := new(CABundleSource) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *GitHubAPIConfig) DeepCopyInto(out *GitHubAPIConfig) { *out = *in @@ -214,7 +230,7 @@ func (in *GitHubAPIConfig) DeepCopyInto(out *GitHubAPIConfig) { if in.TLS != nil { in, out := &in.TLS, &out.TLS *out = new(TLSSpec) - **out = **in + (*in).DeepCopyInto(*out) } return } @@ -534,7 +550,7 @@ func (in *LDAPIdentityProviderSpec) DeepCopyInto(out *LDAPIdentityProviderSpec) if in.TLS != nil { in, out := &in.TLS, &out.TLS *out = new(TLSSpec) - **out = **in + (*in).DeepCopyInto(*out) } out.Bind = in.Bind out.UserSearch = in.UserSearch @@ -740,7 +756,7 @@ func (in *OIDCIdentityProviderSpec) DeepCopyInto(out *OIDCIdentityProviderSpec) if in.TLS != nil { in, out := &in.TLS, &out.TLS *out = new(TLSSpec) - **out = **in + (*in).DeepCopyInto(*out) } in.AuthorizationConfig.DeepCopyInto(&out.AuthorizationConfig) in.Claims.DeepCopyInto(&out.Claims) @@ -800,6 +816,11 @@ func (in *Parameter) DeepCopy() *Parameter { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TLSSpec) DeepCopyInto(out *TLSSpec) { *out = *in + if in.CertificateAuthorityDataSource != nil { + in, out := &in.CertificateAuthorityDataSource, &out.CertificateAuthorityDataSource + *out = new(CABundleSource) + **out = **in + } return } diff --git a/generated/1.28/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml b/generated/1.28/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml index d59fcb783..1e503bda2 100644 --- a/generated/1.28/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml +++ b/generated/1.28/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml @@ -92,6 +92,33 @@ spec: description: X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. type: string + certificateAuthorityDataSource: + description: Reference to a CA bundle in a secret or a configmap. + properties: + key: + description: Key within the secret or configmap from which + to read the CA bundle. + minLength: 1 + type: string + kind: + description: |- + Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + enum: + - Secret + - ConfigMap + type: string + name: + description: Name of the secret or configmap from which to + read the CA bundle. + minLength: 1 + type: string + required: + - key + - kind + - name + type: object type: object required: - audience diff --git a/generated/1.28/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml b/generated/1.28/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml index 4ccd53770..b3b024977 100644 --- a/generated/1.28/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml +++ b/generated/1.28/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml @@ -63,6 +63,33 @@ spec: description: X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. type: string + certificateAuthorityDataSource: + description: Reference to a CA bundle in a secret or a configmap. + properties: + key: + description: Key within the secret or configmap from which + to read the CA bundle. + minLength: 1 + type: string + kind: + description: |- + Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + enum: + - Secret + - ConfigMap + type: string + name: + description: Name of the secret or configmap from which to + read the CA bundle. + minLength: 1 + type: string + required: + - key + - kind + - name + type: object type: object required: - endpoint diff --git a/generated/1.28/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml b/generated/1.28/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml index 062251102..8940aebb9 100644 --- a/generated/1.28/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml +++ b/generated/1.28/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml @@ -170,6 +170,33 @@ spec: description: X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. type: string + certificateAuthorityDataSource: + description: Reference to a CA bundle in a secret or a configmap. + properties: + key: + description: Key within the secret or configmap from which + to read the CA bundle. + minLength: 1 + type: string + kind: + description: |- + Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + enum: + - Secret + - ConfigMap + type: string + name: + description: Name of the secret or configmap from which to + read the CA bundle. + minLength: 1 + type: string + required: + - key + - kind + - name + type: object type: object userSearch: description: UserSearch contains the configuration for searching for diff --git a/generated/1.28/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml b/generated/1.28/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml index f93108700..39db773a2 100644 --- a/generated/1.28/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml +++ b/generated/1.28/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml @@ -225,6 +225,33 @@ spec: bundle). If omitted, a default set of system roots will be trusted. type: string + certificateAuthorityDataSource: + description: Reference to a CA bundle in a secret or a configmap. + properties: + key: + description: Key within the secret or configmap from which + to read the CA bundle. + minLength: 1 + type: string + kind: + description: |- + Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + enum: + - Secret + - ConfigMap + type: string + name: + description: Name of the secret or configmap from which + to read the CA bundle. + minLength: 1 + type: string + required: + - key + - kind + - name + type: object type: object type: object required: diff --git a/generated/1.28/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml b/generated/1.28/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml index 711e9a754..34f0cb92a 100644 --- a/generated/1.28/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml +++ b/generated/1.28/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml @@ -161,6 +161,33 @@ spec: description: X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. type: string + certificateAuthorityDataSource: + description: Reference to a CA bundle in a secret or a configmap. + properties: + key: + description: Key within the secret or configmap from which + to read the CA bundle. + minLength: 1 + type: string + kind: + description: |- + Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + enum: + - Secret + - ConfigMap + type: string + name: + description: Name of the secret or configmap from which to + read the CA bundle. + minLength: 1 + type: string + required: + - key + - kind + - name + type: object type: object userSearch: description: UserSearch contains the configuration for searching for diff --git a/generated/1.28/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml b/generated/1.28/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml index acfca1573..ff69c2d8c 100644 --- a/generated/1.28/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml +++ b/generated/1.28/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml @@ -211,6 +211,33 @@ spec: description: X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. type: string + certificateAuthorityDataSource: + description: Reference to a CA bundle in a secret or a configmap. + properties: + key: + description: Key within the secret or configmap from which + to read the CA bundle. + minLength: 1 + type: string + kind: + description: |- + Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + enum: + - Secret + - ConfigMap + type: string + name: + description: Name of the secret or configmap from which to + read the CA bundle. + minLength: 1 + type: string + required: + - key + - kind + - name + type: object type: object required: - client diff --git a/generated/1.29/README.adoc b/generated/1.29/README.adoc index 120952689..c169c7ee8 100644 --- a/generated/1.29/README.adoc +++ b/generated/1.29/README.adoc @@ -23,6 +23,27 @@ Package v1alpha1 is the v1alpha1 version of the Pinniped concierge authenticatio +[id="{anchor_prefix}-go-pinniped-dev-generated-1-29-apis-concierge-authentication-v1alpha1-cabundlesource"] +==== CABundleSource + +CABundleSource provides a source for CA bundle used for client-side TLS verification. + +.Appears In: +**** +- xref:{anchor_prefix}-go-pinniped-dev-generated-1-29-apis-concierge-authentication-v1alpha1-tlsspec[$$TLSSpec$$] +**** + +[cols="25a,75a", options="header"] +|=== +| Field | Description +| *`kind`* __string__ | Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + +Secrets must be of type kubernetes.io/tls or Opaque. + +For configmaps, the value associated with the key is not expected to be base64 encoded. + +| *`name`* __string__ | Name of the secret or configmap from which to read the CA bundle. + +| *`key`* __string__ | Key within the secret or configmap from which to read the CA bundle. + +|=== + + [id="{anchor_prefix}-go-pinniped-dev-generated-1-29-apis-concierge-authentication-v1alpha1-jwtauthenticator"] ==== JWTAuthenticator @@ -137,6 +158,7 @@ Configuration for configuring TLS on various authenticators. |=== | Field | Description | *`certificateAuthorityData`* __string__ | X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. + +| *`certificateAuthorityDataSource`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-29-apis-concierge-authentication-v1alpha1-cabundlesource[$$CABundleSource$$]__ | Reference to a CA bundle in a secret or a configmap. + |=== @@ -1645,6 +1667,27 @@ Optional, when empty this defaults to "objectGUID". + |=== +[id="{anchor_prefix}-go-pinniped-dev-generated-1-29-apis-supervisor-idp-v1alpha1-cabundlesource"] +==== CABundleSource + +CABundleSource provides a source for CA bundle used for client-side TLS verification. + +.Appears In: +**** +- xref:{anchor_prefix}-go-pinniped-dev-generated-1-29-apis-supervisor-idp-v1alpha1-tlsspec[$$TLSSpec$$] +**** + +[cols="25a,75a", options="header"] +|=== +| Field | Description +| *`kind`* __string__ | Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + +Secrets must be of type kubernetes.io/tls or Opaque. + +For configmaps, the value associated with the key is not expected to be base64 encoded. + +| *`name`* __string__ | Name of the secret or configmap from which to read the CA bundle. + +| *`key`* __string__ | Key within the secret or configmap from which to read the CA bundle. + +|=== + + [id="{anchor_prefix}-go-pinniped-dev-generated-1-29-apis-supervisor-idp-v1alpha1-githubapiconfig"] ==== GitHubAPIConfig @@ -2401,6 +2444,7 @@ TLSSpec provides TLS configuration for identity provider integration. |=== | Field | Description | *`certificateAuthorityData`* __string__ | X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. + +| *`certificateAuthorityDataSource`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-29-apis-supervisor-idp-v1alpha1-cabundlesource[$$CABundleSource$$]__ | Reference to a CA bundle in a secret or a configmap. + |=== diff --git a/generated/1.29/apis/concierge/authentication/v1alpha1/types_tls.go b/generated/1.29/apis/concierge/authentication/v1alpha1/types_tls.go index 12231665d..e8916dfa5 100644 --- a/generated/1.29/apis/concierge/authentication/v1alpha1/types_tls.go +++ b/generated/1.29/apis/concierge/authentication/v1alpha1/types_tls.go @@ -3,9 +3,27 @@ package v1alpha1 +// CABundleSource provides a source for CA bundle used for client-side TLS verification. +type CABundleSource struct { + // Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + // Secrets must be of type kubernetes.io/tls or Opaque. + // For configmaps, the value associated with the key is not expected to be base64 encoded. + // +kubebuilder:validation:Enum=Secret;ConfigMap + Kind string `json:"kind"` + // Name of the secret or configmap from which to read the CA bundle. + // +kubebuilder:validation:MinLength=1 + Name string `json:"name"` + // Key within the secret or configmap from which to read the CA bundle. + // +kubebuilder:validation:MinLength=1 + Key string `json:"key"` +} + // Configuration for configuring TLS on various authenticators. type TLSSpec struct { // X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. // +optional CertificateAuthorityData string `json:"certificateAuthorityData,omitempty"` + // Reference to a CA bundle in a secret or a configmap. + // +optional + CertificateAuthorityDataSource *CABundleSource `json:"certificateAuthorityDataSource,omitempty"` } diff --git a/generated/1.29/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go b/generated/1.29/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go index 5d36cf81b..27cbcc844 100644 --- a/generated/1.29/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go +++ b/generated/1.29/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go @@ -13,6 +13,22 @@ import ( runtime "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CABundleSource) DeepCopyInto(out *CABundleSource) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CABundleSource. +func (in *CABundleSource) DeepCopy() *CABundleSource { + if in == nil { + return nil + } + out := new(CABundleSource) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *JWTAuthenticator) DeepCopyInto(out *JWTAuthenticator) { *out = *in @@ -81,7 +97,7 @@ func (in *JWTAuthenticatorSpec) DeepCopyInto(out *JWTAuthenticatorSpec) { if in.TLS != nil { in, out := &in.TLS, &out.TLS *out = new(TLSSpec) - **out = **in + (*in).DeepCopyInto(*out) } return } @@ -138,6 +154,11 @@ func (in *JWTTokenClaims) DeepCopy() *JWTTokenClaims { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TLSSpec) DeepCopyInto(out *TLSSpec) { *out = *in + if in.CertificateAuthorityDataSource != nil { + in, out := &in.CertificateAuthorityDataSource, &out.CertificateAuthorityDataSource + *out = new(CABundleSource) + **out = **in + } return } @@ -218,7 +239,7 @@ func (in *WebhookAuthenticatorSpec) DeepCopyInto(out *WebhookAuthenticatorSpec) if in.TLS != nil { in, out := &in.TLS, &out.TLS *out = new(TLSSpec) - **out = **in + (*in).DeepCopyInto(*out) } return } diff --git a/generated/1.29/apis/supervisor/idp/v1alpha1/types_tls.go b/generated/1.29/apis/supervisor/idp/v1alpha1/types_tls.go index 49b49373c..c0fc606f6 100644 --- a/generated/1.29/apis/supervisor/idp/v1alpha1/types_tls.go +++ b/generated/1.29/apis/supervisor/idp/v1alpha1/types_tls.go @@ -3,9 +3,28 @@ package v1alpha1 + +// CABundleSource provides a source for CA bundle used for client-side TLS verification. +type CABundleSource struct { + // Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + // Secrets must be of type kubernetes.io/tls or Opaque. + // For configmaps, the value associated with the key is not expected to be base64 encoded. + // +kubebuilder:validation:Enum=Secret;ConfigMap + Kind string `json:"kind"` + // Name of the secret or configmap from which to read the CA bundle. + // +kubebuilder:validation:MinLength=1 + Name string `json:"name"` + // Key within the secret or configmap from which to read the CA bundle. + // +kubebuilder:validation:MinLength=1 + Key string `json:"key"` +} + // TLSSpec provides TLS configuration for identity provider integration. type TLSSpec struct { // X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. // +optional CertificateAuthorityData string `json:"certificateAuthorityData,omitempty"` + // Reference to a CA bundle in a secret or a configmap. + // +optional + CertificateAuthorityDataSource *CABundleSource `json:"certificateAuthorityDataSource,omitempty"` } diff --git a/generated/1.29/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go b/generated/1.29/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go index e48860e82..41d44d226 100644 --- a/generated/1.29/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go +++ b/generated/1.29/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go @@ -129,7 +129,7 @@ func (in *ActiveDirectoryIdentityProviderSpec) DeepCopyInto(out *ActiveDirectory if in.TLS != nil { in, out := &in.TLS, &out.TLS *out = new(TLSSpec) - **out = **in + (*in).DeepCopyInto(*out) } out.Bind = in.Bind out.UserSearch = in.UserSearch @@ -203,6 +203,22 @@ func (in *ActiveDirectoryIdentityProviderUserSearchAttributes) DeepCopy() *Activ return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CABundleSource) DeepCopyInto(out *CABundleSource) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CABundleSource. +func (in *CABundleSource) DeepCopy() *CABundleSource { + if in == nil { + return nil + } + out := new(CABundleSource) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *GitHubAPIConfig) DeepCopyInto(out *GitHubAPIConfig) { *out = *in @@ -214,7 +230,7 @@ func (in *GitHubAPIConfig) DeepCopyInto(out *GitHubAPIConfig) { if in.TLS != nil { in, out := &in.TLS, &out.TLS *out = new(TLSSpec) - **out = **in + (*in).DeepCopyInto(*out) } return } @@ -534,7 +550,7 @@ func (in *LDAPIdentityProviderSpec) DeepCopyInto(out *LDAPIdentityProviderSpec) if in.TLS != nil { in, out := &in.TLS, &out.TLS *out = new(TLSSpec) - **out = **in + (*in).DeepCopyInto(*out) } out.Bind = in.Bind out.UserSearch = in.UserSearch @@ -740,7 +756,7 @@ func (in *OIDCIdentityProviderSpec) DeepCopyInto(out *OIDCIdentityProviderSpec) if in.TLS != nil { in, out := &in.TLS, &out.TLS *out = new(TLSSpec) - **out = **in + (*in).DeepCopyInto(*out) } in.AuthorizationConfig.DeepCopyInto(&out.AuthorizationConfig) in.Claims.DeepCopyInto(&out.Claims) @@ -800,6 +816,11 @@ func (in *Parameter) DeepCopy() *Parameter { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TLSSpec) DeepCopyInto(out *TLSSpec) { *out = *in + if in.CertificateAuthorityDataSource != nil { + in, out := &in.CertificateAuthorityDataSource, &out.CertificateAuthorityDataSource + *out = new(CABundleSource) + **out = **in + } return } diff --git a/generated/1.29/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml b/generated/1.29/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml index d59fcb783..1e503bda2 100644 --- a/generated/1.29/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml +++ b/generated/1.29/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml @@ -92,6 +92,33 @@ spec: description: X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. type: string + certificateAuthorityDataSource: + description: Reference to a CA bundle in a secret or a configmap. + properties: + key: + description: Key within the secret or configmap from which + to read the CA bundle. + minLength: 1 + type: string + kind: + description: |- + Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + enum: + - Secret + - ConfigMap + type: string + name: + description: Name of the secret or configmap from which to + read the CA bundle. + minLength: 1 + type: string + required: + - key + - kind + - name + type: object type: object required: - audience diff --git a/generated/1.29/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml b/generated/1.29/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml index 4ccd53770..b3b024977 100644 --- a/generated/1.29/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml +++ b/generated/1.29/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml @@ -63,6 +63,33 @@ spec: description: X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. type: string + certificateAuthorityDataSource: + description: Reference to a CA bundle in a secret or a configmap. + properties: + key: + description: Key within the secret or configmap from which + to read the CA bundle. + minLength: 1 + type: string + kind: + description: |- + Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + enum: + - Secret + - ConfigMap + type: string + name: + description: Name of the secret or configmap from which to + read the CA bundle. + minLength: 1 + type: string + required: + - key + - kind + - name + type: object type: object required: - endpoint diff --git a/generated/1.29/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml b/generated/1.29/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml index 062251102..8940aebb9 100644 --- a/generated/1.29/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml +++ b/generated/1.29/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml @@ -170,6 +170,33 @@ spec: description: X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. type: string + certificateAuthorityDataSource: + description: Reference to a CA bundle in a secret or a configmap. + properties: + key: + description: Key within the secret or configmap from which + to read the CA bundle. + minLength: 1 + type: string + kind: + description: |- + Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + enum: + - Secret + - ConfigMap + type: string + name: + description: Name of the secret or configmap from which to + read the CA bundle. + minLength: 1 + type: string + required: + - key + - kind + - name + type: object type: object userSearch: description: UserSearch contains the configuration for searching for diff --git a/generated/1.29/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml b/generated/1.29/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml index f93108700..39db773a2 100644 --- a/generated/1.29/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml +++ b/generated/1.29/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml @@ -225,6 +225,33 @@ spec: bundle). If omitted, a default set of system roots will be trusted. type: string + certificateAuthorityDataSource: + description: Reference to a CA bundle in a secret or a configmap. + properties: + key: + description: Key within the secret or configmap from which + to read the CA bundle. + minLength: 1 + type: string + kind: + description: |- + Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + enum: + - Secret + - ConfigMap + type: string + name: + description: Name of the secret or configmap from which + to read the CA bundle. + minLength: 1 + type: string + required: + - key + - kind + - name + type: object type: object type: object required: diff --git a/generated/1.29/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml b/generated/1.29/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml index 711e9a754..34f0cb92a 100644 --- a/generated/1.29/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml +++ b/generated/1.29/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml @@ -161,6 +161,33 @@ spec: description: X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. type: string + certificateAuthorityDataSource: + description: Reference to a CA bundle in a secret or a configmap. + properties: + key: + description: Key within the secret or configmap from which + to read the CA bundle. + minLength: 1 + type: string + kind: + description: |- + Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + enum: + - Secret + - ConfigMap + type: string + name: + description: Name of the secret or configmap from which to + read the CA bundle. + minLength: 1 + type: string + required: + - key + - kind + - name + type: object type: object userSearch: description: UserSearch contains the configuration for searching for diff --git a/generated/1.29/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml b/generated/1.29/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml index acfca1573..ff69c2d8c 100644 --- a/generated/1.29/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml +++ b/generated/1.29/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml @@ -211,6 +211,33 @@ spec: description: X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. type: string + certificateAuthorityDataSource: + description: Reference to a CA bundle in a secret or a configmap. + properties: + key: + description: Key within the secret or configmap from which + to read the CA bundle. + minLength: 1 + type: string + kind: + description: |- + Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + enum: + - Secret + - ConfigMap + type: string + name: + description: Name of the secret or configmap from which to + read the CA bundle. + minLength: 1 + type: string + required: + - key + - kind + - name + type: object type: object required: - client diff --git a/generated/1.30/README.adoc b/generated/1.30/README.adoc index 337aacd2a..8eb340a40 100644 --- a/generated/1.30/README.adoc +++ b/generated/1.30/README.adoc @@ -23,6 +23,27 @@ Package v1alpha1 is the v1alpha1 version of the Pinniped concierge authenticatio +[id="{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-concierge-authentication-v1alpha1-cabundlesource"] +==== CABundleSource + +CABundleSource provides a source for CA bundle used for client-side TLS verification. + +.Appears In: +**** +- xref:{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-concierge-authentication-v1alpha1-tlsspec[$$TLSSpec$$] +**** + +[cols="25a,75a", options="header"] +|=== +| Field | Description +| *`kind`* __string__ | Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + +Secrets must be of type kubernetes.io/tls or Opaque. + +For configmaps, the value associated with the key is not expected to be base64 encoded. + +| *`name`* __string__ | Name of the secret or configmap from which to read the CA bundle. + +| *`key`* __string__ | Key within the secret or configmap from which to read the CA bundle. + +|=== + + [id="{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-concierge-authentication-v1alpha1-jwtauthenticator"] ==== JWTAuthenticator @@ -137,6 +158,7 @@ Configuration for configuring TLS on various authenticators. |=== | Field | Description | *`certificateAuthorityData`* __string__ | X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. + +| *`certificateAuthorityDataSource`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-concierge-authentication-v1alpha1-cabundlesource[$$CABundleSource$$]__ | Reference to a CA bundle in a secret or a configmap. + |=== @@ -1645,6 +1667,27 @@ Optional, when empty this defaults to "objectGUID". + |=== +[id="{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-supervisor-idp-v1alpha1-cabundlesource"] +==== CABundleSource + +CABundleSource provides a source for CA bundle used for client-side TLS verification. + +.Appears In: +**** +- xref:{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-supervisor-idp-v1alpha1-tlsspec[$$TLSSpec$$] +**** + +[cols="25a,75a", options="header"] +|=== +| Field | Description +| *`kind`* __string__ | Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + +Secrets must be of type kubernetes.io/tls or Opaque. + +For configmaps, the value associated with the key is not expected to be base64 encoded. + +| *`name`* __string__ | Name of the secret or configmap from which to read the CA bundle. + +| *`key`* __string__ | Key within the secret or configmap from which to read the CA bundle. + +|=== + + [id="{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-supervisor-idp-v1alpha1-githubapiconfig"] ==== GitHubAPIConfig @@ -2401,6 +2444,7 @@ TLSSpec provides TLS configuration for identity provider integration. |=== | Field | Description | *`certificateAuthorityData`* __string__ | X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. + +| *`certificateAuthorityDataSource`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-supervisor-idp-v1alpha1-cabundlesource[$$CABundleSource$$]__ | Reference to a CA bundle in a secret or a configmap. + |=== diff --git a/generated/1.30/apis/concierge/authentication/v1alpha1/types_tls.go b/generated/1.30/apis/concierge/authentication/v1alpha1/types_tls.go index 12231665d..e8916dfa5 100644 --- a/generated/1.30/apis/concierge/authentication/v1alpha1/types_tls.go +++ b/generated/1.30/apis/concierge/authentication/v1alpha1/types_tls.go @@ -3,9 +3,27 @@ package v1alpha1 +// CABundleSource provides a source for CA bundle used for client-side TLS verification. +type CABundleSource struct { + // Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + // Secrets must be of type kubernetes.io/tls or Opaque. + // For configmaps, the value associated with the key is not expected to be base64 encoded. + // +kubebuilder:validation:Enum=Secret;ConfigMap + Kind string `json:"kind"` + // Name of the secret or configmap from which to read the CA bundle. + // +kubebuilder:validation:MinLength=1 + Name string `json:"name"` + // Key within the secret or configmap from which to read the CA bundle. + // +kubebuilder:validation:MinLength=1 + Key string `json:"key"` +} + // Configuration for configuring TLS on various authenticators. type TLSSpec struct { // X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. // +optional CertificateAuthorityData string `json:"certificateAuthorityData,omitempty"` + // Reference to a CA bundle in a secret or a configmap. + // +optional + CertificateAuthorityDataSource *CABundleSource `json:"certificateAuthorityDataSource,omitempty"` } diff --git a/generated/1.30/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go b/generated/1.30/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go index 5d36cf81b..27cbcc844 100644 --- a/generated/1.30/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go +++ b/generated/1.30/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go @@ -13,6 +13,22 @@ import ( runtime "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CABundleSource) DeepCopyInto(out *CABundleSource) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CABundleSource. +func (in *CABundleSource) DeepCopy() *CABundleSource { + if in == nil { + return nil + } + out := new(CABundleSource) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *JWTAuthenticator) DeepCopyInto(out *JWTAuthenticator) { *out = *in @@ -81,7 +97,7 @@ func (in *JWTAuthenticatorSpec) DeepCopyInto(out *JWTAuthenticatorSpec) { if in.TLS != nil { in, out := &in.TLS, &out.TLS *out = new(TLSSpec) - **out = **in + (*in).DeepCopyInto(*out) } return } @@ -138,6 +154,11 @@ func (in *JWTTokenClaims) DeepCopy() *JWTTokenClaims { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TLSSpec) DeepCopyInto(out *TLSSpec) { *out = *in + if in.CertificateAuthorityDataSource != nil { + in, out := &in.CertificateAuthorityDataSource, &out.CertificateAuthorityDataSource + *out = new(CABundleSource) + **out = **in + } return } @@ -218,7 +239,7 @@ func (in *WebhookAuthenticatorSpec) DeepCopyInto(out *WebhookAuthenticatorSpec) if in.TLS != nil { in, out := &in.TLS, &out.TLS *out = new(TLSSpec) - **out = **in + (*in).DeepCopyInto(*out) } return } diff --git a/generated/1.30/apis/supervisor/idp/v1alpha1/types_tls.go b/generated/1.30/apis/supervisor/idp/v1alpha1/types_tls.go index 49b49373c..c0fc606f6 100644 --- a/generated/1.30/apis/supervisor/idp/v1alpha1/types_tls.go +++ b/generated/1.30/apis/supervisor/idp/v1alpha1/types_tls.go @@ -3,9 +3,28 @@ package v1alpha1 + +// CABundleSource provides a source for CA bundle used for client-side TLS verification. +type CABundleSource struct { + // Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + // Secrets must be of type kubernetes.io/tls or Opaque. + // For configmaps, the value associated with the key is not expected to be base64 encoded. + // +kubebuilder:validation:Enum=Secret;ConfigMap + Kind string `json:"kind"` + // Name of the secret or configmap from which to read the CA bundle. + // +kubebuilder:validation:MinLength=1 + Name string `json:"name"` + // Key within the secret or configmap from which to read the CA bundle. + // +kubebuilder:validation:MinLength=1 + Key string `json:"key"` +} + // TLSSpec provides TLS configuration for identity provider integration. type TLSSpec struct { // X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. // +optional CertificateAuthorityData string `json:"certificateAuthorityData,omitempty"` + // Reference to a CA bundle in a secret or a configmap. + // +optional + CertificateAuthorityDataSource *CABundleSource `json:"certificateAuthorityDataSource,omitempty"` } diff --git a/generated/1.30/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go b/generated/1.30/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go index e48860e82..41d44d226 100644 --- a/generated/1.30/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go +++ b/generated/1.30/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go @@ -129,7 +129,7 @@ func (in *ActiveDirectoryIdentityProviderSpec) DeepCopyInto(out *ActiveDirectory if in.TLS != nil { in, out := &in.TLS, &out.TLS *out = new(TLSSpec) - **out = **in + (*in).DeepCopyInto(*out) } out.Bind = in.Bind out.UserSearch = in.UserSearch @@ -203,6 +203,22 @@ func (in *ActiveDirectoryIdentityProviderUserSearchAttributes) DeepCopy() *Activ return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CABundleSource) DeepCopyInto(out *CABundleSource) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CABundleSource. +func (in *CABundleSource) DeepCopy() *CABundleSource { + if in == nil { + return nil + } + out := new(CABundleSource) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *GitHubAPIConfig) DeepCopyInto(out *GitHubAPIConfig) { *out = *in @@ -214,7 +230,7 @@ func (in *GitHubAPIConfig) DeepCopyInto(out *GitHubAPIConfig) { if in.TLS != nil { in, out := &in.TLS, &out.TLS *out = new(TLSSpec) - **out = **in + (*in).DeepCopyInto(*out) } return } @@ -534,7 +550,7 @@ func (in *LDAPIdentityProviderSpec) DeepCopyInto(out *LDAPIdentityProviderSpec) if in.TLS != nil { in, out := &in.TLS, &out.TLS *out = new(TLSSpec) - **out = **in + (*in).DeepCopyInto(*out) } out.Bind = in.Bind out.UserSearch = in.UserSearch @@ -740,7 +756,7 @@ func (in *OIDCIdentityProviderSpec) DeepCopyInto(out *OIDCIdentityProviderSpec) if in.TLS != nil { in, out := &in.TLS, &out.TLS *out = new(TLSSpec) - **out = **in + (*in).DeepCopyInto(*out) } in.AuthorizationConfig.DeepCopyInto(&out.AuthorizationConfig) in.Claims.DeepCopyInto(&out.Claims) @@ -800,6 +816,11 @@ func (in *Parameter) DeepCopy() *Parameter { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TLSSpec) DeepCopyInto(out *TLSSpec) { *out = *in + if in.CertificateAuthorityDataSource != nil { + in, out := &in.CertificateAuthorityDataSource, &out.CertificateAuthorityDataSource + *out = new(CABundleSource) + **out = **in + } return } diff --git a/generated/1.30/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml b/generated/1.30/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml index d59fcb783..1e503bda2 100644 --- a/generated/1.30/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml +++ b/generated/1.30/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml @@ -92,6 +92,33 @@ spec: description: X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. type: string + certificateAuthorityDataSource: + description: Reference to a CA bundle in a secret or a configmap. + properties: + key: + description: Key within the secret or configmap from which + to read the CA bundle. + minLength: 1 + type: string + kind: + description: |- + Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + enum: + - Secret + - ConfigMap + type: string + name: + description: Name of the secret or configmap from which to + read the CA bundle. + minLength: 1 + type: string + required: + - key + - kind + - name + type: object type: object required: - audience diff --git a/generated/1.30/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml b/generated/1.30/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml index 4ccd53770..b3b024977 100644 --- a/generated/1.30/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml +++ b/generated/1.30/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml @@ -63,6 +63,33 @@ spec: description: X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. type: string + certificateAuthorityDataSource: + description: Reference to a CA bundle in a secret or a configmap. + properties: + key: + description: Key within the secret or configmap from which + to read the CA bundle. + minLength: 1 + type: string + kind: + description: |- + Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + enum: + - Secret + - ConfigMap + type: string + name: + description: Name of the secret or configmap from which to + read the CA bundle. + minLength: 1 + type: string + required: + - key + - kind + - name + type: object type: object required: - endpoint diff --git a/generated/1.30/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml b/generated/1.30/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml index 062251102..8940aebb9 100644 --- a/generated/1.30/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml +++ b/generated/1.30/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml @@ -170,6 +170,33 @@ spec: description: X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. type: string + certificateAuthorityDataSource: + description: Reference to a CA bundle in a secret or a configmap. + properties: + key: + description: Key within the secret or configmap from which + to read the CA bundle. + minLength: 1 + type: string + kind: + description: |- + Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + enum: + - Secret + - ConfigMap + type: string + name: + description: Name of the secret or configmap from which to + read the CA bundle. + minLength: 1 + type: string + required: + - key + - kind + - name + type: object type: object userSearch: description: UserSearch contains the configuration for searching for diff --git a/generated/1.30/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml b/generated/1.30/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml index f93108700..39db773a2 100644 --- a/generated/1.30/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml +++ b/generated/1.30/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml @@ -225,6 +225,33 @@ spec: bundle). If omitted, a default set of system roots will be trusted. type: string + certificateAuthorityDataSource: + description: Reference to a CA bundle in a secret or a configmap. + properties: + key: + description: Key within the secret or configmap from which + to read the CA bundle. + minLength: 1 + type: string + kind: + description: |- + Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + enum: + - Secret + - ConfigMap + type: string + name: + description: Name of the secret or configmap from which + to read the CA bundle. + minLength: 1 + type: string + required: + - key + - kind + - name + type: object type: object type: object required: diff --git a/generated/1.30/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml b/generated/1.30/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml index 711e9a754..34f0cb92a 100644 --- a/generated/1.30/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml +++ b/generated/1.30/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml @@ -161,6 +161,33 @@ spec: description: X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. type: string + certificateAuthorityDataSource: + description: Reference to a CA bundle in a secret or a configmap. + properties: + key: + description: Key within the secret or configmap from which + to read the CA bundle. + minLength: 1 + type: string + kind: + description: |- + Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + enum: + - Secret + - ConfigMap + type: string + name: + description: Name of the secret or configmap from which to + read the CA bundle. + minLength: 1 + type: string + required: + - key + - kind + - name + type: object type: object userSearch: description: UserSearch contains the configuration for searching for diff --git a/generated/1.30/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml b/generated/1.30/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml index acfca1573..ff69c2d8c 100644 --- a/generated/1.30/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml +++ b/generated/1.30/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml @@ -211,6 +211,33 @@ spec: description: X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. type: string + certificateAuthorityDataSource: + description: Reference to a CA bundle in a secret or a configmap. + properties: + key: + description: Key within the secret or configmap from which + to read the CA bundle. + minLength: 1 + type: string + kind: + description: |- + Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + enum: + - Secret + - ConfigMap + type: string + name: + description: Name of the secret or configmap from which to + read the CA bundle. + minLength: 1 + type: string + required: + - key + - kind + - name + type: object type: object required: - client diff --git a/generated/latest/README.adoc b/generated/latest/README.adoc index 337aacd2a..8eb340a40 100644 --- a/generated/latest/README.adoc +++ b/generated/latest/README.adoc @@ -23,6 +23,27 @@ Package v1alpha1 is the v1alpha1 version of the Pinniped concierge authenticatio +[id="{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-concierge-authentication-v1alpha1-cabundlesource"] +==== CABundleSource + +CABundleSource provides a source for CA bundle used for client-side TLS verification. + +.Appears In: +**** +- xref:{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-concierge-authentication-v1alpha1-tlsspec[$$TLSSpec$$] +**** + +[cols="25a,75a", options="header"] +|=== +| Field | Description +| *`kind`* __string__ | Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + +Secrets must be of type kubernetes.io/tls or Opaque. + +For configmaps, the value associated with the key is not expected to be base64 encoded. + +| *`name`* __string__ | Name of the secret or configmap from which to read the CA bundle. + +| *`key`* __string__ | Key within the secret or configmap from which to read the CA bundle. + +|=== + + [id="{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-concierge-authentication-v1alpha1-jwtauthenticator"] ==== JWTAuthenticator @@ -137,6 +158,7 @@ Configuration for configuring TLS on various authenticators. |=== | Field | Description | *`certificateAuthorityData`* __string__ | X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. + +| *`certificateAuthorityDataSource`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-concierge-authentication-v1alpha1-cabundlesource[$$CABundleSource$$]__ | Reference to a CA bundle in a secret or a configmap. + |=== @@ -1645,6 +1667,27 @@ Optional, when empty this defaults to "objectGUID". + |=== +[id="{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-supervisor-idp-v1alpha1-cabundlesource"] +==== CABundleSource + +CABundleSource provides a source for CA bundle used for client-side TLS verification. + +.Appears In: +**** +- xref:{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-supervisor-idp-v1alpha1-tlsspec[$$TLSSpec$$] +**** + +[cols="25a,75a", options="header"] +|=== +| Field | Description +| *`kind`* __string__ | Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + +Secrets must be of type kubernetes.io/tls or Opaque. + +For configmaps, the value associated with the key is not expected to be base64 encoded. + +| *`name`* __string__ | Name of the secret or configmap from which to read the CA bundle. + +| *`key`* __string__ | Key within the secret or configmap from which to read the CA bundle. + +|=== + + [id="{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-supervisor-idp-v1alpha1-githubapiconfig"] ==== GitHubAPIConfig @@ -2401,6 +2444,7 @@ TLSSpec provides TLS configuration for identity provider integration. |=== | Field | Description | *`certificateAuthorityData`* __string__ | X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. + +| *`certificateAuthorityDataSource`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-supervisor-idp-v1alpha1-cabundlesource[$$CABundleSource$$]__ | Reference to a CA bundle in a secret or a configmap. + |=== diff --git a/generated/latest/apis/concierge/authentication/v1alpha1/types_tls.go b/generated/latest/apis/concierge/authentication/v1alpha1/types_tls.go index 12231665d..e8916dfa5 100644 --- a/generated/latest/apis/concierge/authentication/v1alpha1/types_tls.go +++ b/generated/latest/apis/concierge/authentication/v1alpha1/types_tls.go @@ -3,9 +3,27 @@ package v1alpha1 +// CABundleSource provides a source for CA bundle used for client-side TLS verification. +type CABundleSource struct { + // Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + // Secrets must be of type kubernetes.io/tls or Opaque. + // For configmaps, the value associated with the key is not expected to be base64 encoded. + // +kubebuilder:validation:Enum=Secret;ConfigMap + Kind string `json:"kind"` + // Name of the secret or configmap from which to read the CA bundle. + // +kubebuilder:validation:MinLength=1 + Name string `json:"name"` + // Key within the secret or configmap from which to read the CA bundle. + // +kubebuilder:validation:MinLength=1 + Key string `json:"key"` +} + // Configuration for configuring TLS on various authenticators. type TLSSpec struct { // X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. // +optional CertificateAuthorityData string `json:"certificateAuthorityData,omitempty"` + // Reference to a CA bundle in a secret or a configmap. + // +optional + CertificateAuthorityDataSource *CABundleSource `json:"certificateAuthorityDataSource,omitempty"` } diff --git a/generated/latest/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go b/generated/latest/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go index 5d36cf81b..27cbcc844 100644 --- a/generated/latest/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go +++ b/generated/latest/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go @@ -13,6 +13,22 @@ import ( runtime "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CABundleSource) DeepCopyInto(out *CABundleSource) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CABundleSource. +func (in *CABundleSource) DeepCopy() *CABundleSource { + if in == nil { + return nil + } + out := new(CABundleSource) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *JWTAuthenticator) DeepCopyInto(out *JWTAuthenticator) { *out = *in @@ -81,7 +97,7 @@ func (in *JWTAuthenticatorSpec) DeepCopyInto(out *JWTAuthenticatorSpec) { if in.TLS != nil { in, out := &in.TLS, &out.TLS *out = new(TLSSpec) - **out = **in + (*in).DeepCopyInto(*out) } return } @@ -138,6 +154,11 @@ func (in *JWTTokenClaims) DeepCopy() *JWTTokenClaims { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TLSSpec) DeepCopyInto(out *TLSSpec) { *out = *in + if in.CertificateAuthorityDataSource != nil { + in, out := &in.CertificateAuthorityDataSource, &out.CertificateAuthorityDataSource + *out = new(CABundleSource) + **out = **in + } return } @@ -218,7 +239,7 @@ func (in *WebhookAuthenticatorSpec) DeepCopyInto(out *WebhookAuthenticatorSpec) if in.TLS != nil { in, out := &in.TLS, &out.TLS *out = new(TLSSpec) - **out = **in + (*in).DeepCopyInto(*out) } return } diff --git a/generated/latest/apis/supervisor/idp/v1alpha1/types_tls.go b/generated/latest/apis/supervisor/idp/v1alpha1/types_tls.go index 49b49373c..c0fc606f6 100644 --- a/generated/latest/apis/supervisor/idp/v1alpha1/types_tls.go +++ b/generated/latest/apis/supervisor/idp/v1alpha1/types_tls.go @@ -3,9 +3,28 @@ package v1alpha1 + +// CABundleSource provides a source for CA bundle used for client-side TLS verification. +type CABundleSource struct { + // Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + // Secrets must be of type kubernetes.io/tls or Opaque. + // For configmaps, the value associated with the key is not expected to be base64 encoded. + // +kubebuilder:validation:Enum=Secret;ConfigMap + Kind string `json:"kind"` + // Name of the secret or configmap from which to read the CA bundle. + // +kubebuilder:validation:MinLength=1 + Name string `json:"name"` + // Key within the secret or configmap from which to read the CA bundle. + // +kubebuilder:validation:MinLength=1 + Key string `json:"key"` +} + // TLSSpec provides TLS configuration for identity provider integration. type TLSSpec struct { // X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. // +optional CertificateAuthorityData string `json:"certificateAuthorityData,omitempty"` + // Reference to a CA bundle in a secret or a configmap. + // +optional + CertificateAuthorityDataSource *CABundleSource `json:"certificateAuthorityDataSource,omitempty"` } diff --git a/generated/latest/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go b/generated/latest/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go index e48860e82..41d44d226 100644 --- a/generated/latest/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go +++ b/generated/latest/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go @@ -129,7 +129,7 @@ func (in *ActiveDirectoryIdentityProviderSpec) DeepCopyInto(out *ActiveDirectory if in.TLS != nil { in, out := &in.TLS, &out.TLS *out = new(TLSSpec) - **out = **in + (*in).DeepCopyInto(*out) } out.Bind = in.Bind out.UserSearch = in.UserSearch @@ -203,6 +203,22 @@ func (in *ActiveDirectoryIdentityProviderUserSearchAttributes) DeepCopy() *Activ return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CABundleSource) DeepCopyInto(out *CABundleSource) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CABundleSource. +func (in *CABundleSource) DeepCopy() *CABundleSource { + if in == nil { + return nil + } + out := new(CABundleSource) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *GitHubAPIConfig) DeepCopyInto(out *GitHubAPIConfig) { *out = *in @@ -214,7 +230,7 @@ func (in *GitHubAPIConfig) DeepCopyInto(out *GitHubAPIConfig) { if in.TLS != nil { in, out := &in.TLS, &out.TLS *out = new(TLSSpec) - **out = **in + (*in).DeepCopyInto(*out) } return } @@ -534,7 +550,7 @@ func (in *LDAPIdentityProviderSpec) DeepCopyInto(out *LDAPIdentityProviderSpec) if in.TLS != nil { in, out := &in.TLS, &out.TLS *out = new(TLSSpec) - **out = **in + (*in).DeepCopyInto(*out) } out.Bind = in.Bind out.UserSearch = in.UserSearch @@ -740,7 +756,7 @@ func (in *OIDCIdentityProviderSpec) DeepCopyInto(out *OIDCIdentityProviderSpec) if in.TLS != nil { in, out := &in.TLS, &out.TLS *out = new(TLSSpec) - **out = **in + (*in).DeepCopyInto(*out) } in.AuthorizationConfig.DeepCopyInto(&out.AuthorizationConfig) in.Claims.DeepCopyInto(&out.Claims) @@ -800,6 +816,11 @@ func (in *Parameter) DeepCopy() *Parameter { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TLSSpec) DeepCopyInto(out *TLSSpec) { *out = *in + if in.CertificateAuthorityDataSource != nil { + in, out := &in.CertificateAuthorityDataSource, &out.CertificateAuthorityDataSource + *out = new(CABundleSource) + **out = **in + } return } From 7e6dadb508605795965d9760304103441587c9a9 Mon Sep 17 00:00:00 2001 From: Ashish Amarnath Date: Mon, 24 Jun 2024 11:03:40 -0700 Subject: [PATCH 03/99] add CRD validation integration tests Signed-off-by: Ashish Amarnath --- .../authentication/v1alpha1/types_tls.go.tmpl | 2 +- test/integration/concierge_tls_spec_test.go | 242 +++++++++++++++ test/integration/supervisor_tls_spec_test.go | 292 ++++++++++++++++++ 3 files changed, 535 insertions(+), 1 deletion(-) create mode 100644 test/integration/concierge_tls_spec_test.go create mode 100644 test/integration/supervisor_tls_spec_test.go diff --git a/apis/concierge/authentication/v1alpha1/types_tls.go.tmpl b/apis/concierge/authentication/v1alpha1/types_tls.go.tmpl index e8916dfa5..ad2278985 100644 --- a/apis/concierge/authentication/v1alpha1/types_tls.go.tmpl +++ b/apis/concierge/authentication/v1alpha1/types_tls.go.tmpl @@ -1,4 +1,4 @@ -// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved. +// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package v1alpha1 diff --git a/test/integration/concierge_tls_spec_test.go b/test/integration/concierge_tls_spec_test.go new file mode 100644 index 000000000..3453d0c19 --- /dev/null +++ b/test/integration/concierge_tls_spec_test.go @@ -0,0 +1,242 @@ +// Copyright 2024 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +package integration + +import ( + "bytes" + "context" + "fmt" + "os" + "os/exec" + "path/filepath" + "strings" + "testing" + + "github.com/stretchr/testify/require" + + "go.pinniped.dev/internal/here" + "go.pinniped.dev/test/testlib" +) + +// TestTLSSpecKubeBuilderValidationConcierge_Parallel tests kubebuilder validation on the TLSSpec +// in Pinniped concierge CRDs using WebhookAuthenticator as an example. +func TestTLSSpecKubeBuilderValidationConcierge_Parallel(t *testing.T) { + env := testlib.IntegrationEnv(t) + testCases := []struct { + name string + customResourceYaml string + customResourceName string + expectedError string + }{ + { + name: "should disallow certificate authority data source with missing name", + customResourceYaml: here.Doc(` + --- + apiVersion: authentication.concierge.%s/v1alpha1 + kind: WebhookAuthenticator + metadata: + name: %s + spec: + endpoint: "https://web-auth/token" + tls: + certificateAuthorityDataSource: + kind: Secret + key: bar + `), + customResourceName: "invalid-webhook-auth-missing-name", + expectedError: `The WebhookAuthenticator "%s" is invalid: spec.tls.certificateAuthorityDataSource.name: Required value`, + }, + { + name: "should disallow certificate authority data source with empty value for name", + customResourceYaml: here.Doc(` + --- + apiVersion: authentication.concierge.%s/v1alpha1 + kind: WebhookAuthenticator + metadata: + name: %s + spec: + endpoint: "https://web-auth/token" + tls: + certificateAuthorityDataSource: + kind: Secret + name: "" + key: bar + `), + customResourceName: "invalid-webhook-auth-empty-name", + expectedError: `The WebhookAuthenticator "%s" is invalid: spec.tls.certificateAuthorityDataSource.name: Invalid value: "": spec.tls.certificateAuthorityDataSource.name in body should be at least 1 chars long`, + }, + { + name: "should disallow certificate authority data source with missing key", + customResourceYaml: here.Doc(` + --- + apiVersion: authentication.concierge.%s/v1alpha1 + kind: WebhookAuthenticator + metadata: + name: %s + spec: + endpoint: "https://web-auth/token" + tls: + certificateAuthorityDataSource: + kind: Secret + name: foo + `), + customResourceName: "invalid-webhook-auth-missing-key", + expectedError: `The WebhookAuthenticator "%s" is invalid: spec.tls.certificateAuthorityDataSource.key: Required value`, + }, + { + name: "should disallow certificate authority data source with empty value for key", + customResourceYaml: here.Doc(` + --- + apiVersion: authentication.concierge.%s/v1alpha1 + kind: WebhookAuthenticator + metadata: + name: %s + spec: + endpoint: "https://web-auth/token" + tls: + certificateAuthorityDataSource: + kind: Secret + name: foo + key: "" + `), + customResourceName: "invalid-webhook-auth-empty-kind", + expectedError: `The WebhookAuthenticator "%s" is invalid: spec.tls.certificateAuthorityDataSource.key: Invalid value: "": spec.tls.certificateAuthorityDataSource.key in body should be at least 1 chars long`, + }, + { + name: "should disallow certificate authority data source with missing kind", + customResourceYaml: here.Doc(` + --- + apiVersion: authentication.concierge.%s/v1alpha1 + kind: WebhookAuthenticator + metadata: + name: %s + spec: + endpoint: "https://web-auth/token" + tls: + certificateAuthorityDataSource: + name: foo + key: bar + `), + customResourceName: "invalid-webhook-auth-missing-kind", + expectedError: `The WebhookAuthenticator "%s" is invalid: spec.tls.certificateAuthorityDataSource.kind: Required value`, + }, + { + name: "should disallow certificate authority data source with empty value for kind", + customResourceYaml: here.Doc(` + --- + apiVersion: authentication.concierge.%s/v1alpha1 + kind: WebhookAuthenticator + metadata: + name: %s + spec: + endpoint: "https://web-auth/token" + tls: + certificateAuthorityDataSource: + kind: "" + name: foo + key: bar + `), + customResourceName: "invalid-webhook-auth-invalid-kind", + expectedError: `The WebhookAuthenticator "%s" is invalid: spec.tls.certificateAuthorityDataSource.kind: Unsupported value: "": supported values: "Secret", "ConfigMap"`, + }, + { + name: "should disallow certificate authority data source with invalid kind", + customResourceYaml: here.Doc(` + --- + apiVersion: authentication.concierge.%s/v1alpha1 + kind: WebhookAuthenticator + metadata: + name: %s + spec: + endpoint: "https://web-auth/token" + tls: + certificateAuthorityDataSource: + kind: sorcery + name: foo + key: bar + `), + customResourceName: "invalid-webhook-auth-invalid-kind", + expectedError: `The WebhookAuthenticator "%s" is invalid: spec.tls.certificateAuthorityDataSource.kind: Unsupported value: "sorcery": supported values: "Secret", "ConfigMap"`, + }, + { + name: "should create a custom resource passing all validations using a Secret source", + customResourceYaml: here.Doc(` + --- + apiVersion: authentication.concierge.%s/v1alpha1 + kind: WebhookAuthenticator + metadata: + name: %s + spec: + endpoint: "https://web-auth/token" + tls: + certificateAuthorityDataSource: + kind: Secret + name: foo + key: bar + `), + customResourceName: "valid-webhook-auth-secret-kind", + expectedError: "", + }, + { + name: "should create a custom resource passing all validations using a ConfigMap source", + customResourceYaml: here.Doc(` + --- + apiVersion: authentication.concierge.%s/v1alpha1 + kind: WebhookAuthenticator + metadata: + name: %s + spec: + endpoint: "https://web-auth/token" + tls: + certificateAuthorityDataSource: + kind: ConfigMap + name: foo + key: bar + `), + customResourceName: "valid-webhook-auth-cm-kind", + expectedError: "", + }, + { + name: "should create a custom resource without any tls spec", + customResourceYaml: here.Doc(` + --- + apiVersion: authentication.concierge.%s/v1alpha1 + kind: WebhookAuthenticator + metadata: + name: %s + spec: + endpoint: "https://web-auth/token" + `), + customResourceName: "no-tls-spec", + expectedError: "", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + yamlFilepath := filepath.Join(t.TempDir(), fmt.Sprintf("tls-spec-validation-%s.yaml", tc.customResourceName)) + + resourceName := tc.customResourceName + "-" + testlib.RandHex(t, 7) + yamlBytes := []byte(fmt.Sprintf(tc.customResourceYaml, env.APIGroupSuffix, resourceName)) + + require.NoError(t, os.WriteFile(yamlFilepath, yamlBytes, 0600)) + cmd := exec.CommandContext(context.Background(), "kubectl", []string{"apply", "-f", yamlFilepath}...) + var stdOut, stdErr bytes.Buffer + cmd.Stdout = &stdOut + cmd.Stderr = &stdErr + err := cmd.Run() + t.Cleanup(func() { + t.Helper() + require.NoError(t, exec.Command("kubectl", []string{"delete", "--ignore-not-found", "-f", yamlFilepath}...).Run()) + }) + if tc.expectedError == "" { + require.NoError(t, err) + require.Equal(t, fmt.Sprintf("webhookauthenticator.authentication.concierge.pinniped.dev/%s created\n", resourceName), stdOut.String()) + require.Empty(t, stdErr.String()) + } else { + require.Equal(t, fmt.Sprintf(tc.expectedError, resourceName), strings.TrimSuffix(stdErr.String(), "\n")) + } + }) + } +} diff --git a/test/integration/supervisor_tls_spec_test.go b/test/integration/supervisor_tls_spec_test.go new file mode 100644 index 000000000..7c4304860 --- /dev/null +++ b/test/integration/supervisor_tls_spec_test.go @@ -0,0 +1,292 @@ +// Copyright 2024 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +package integration + +import ( + "bytes" + "context" + "fmt" + "os" + "os/exec" + "path/filepath" + "strings" + "testing" + + "github.com/stretchr/testify/require" + + "go.pinniped.dev/internal/here" + "go.pinniped.dev/test/testlib" +) + +// TestTLSSpecKubeBuilderValidationSupervisor_Parallel tests kubebuilder validation +// on the TLSSpec in Pinniped supervisor CRDs using OIDCIdentityProvider as an example. +func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { + env := testlib.IntegrationEnv(t) + testCases := []struct { + name string + customResourceYaml string + customResourceName string + expectedError string + }{ + { + name: "should disallow certificate authority data source with missing name", + customResourceYaml: here.Doc(` + --- + apiVersion: idp.supervisor.%s/v1alpha1 + kind: OIDCIdentityProvider + metadata: + name: %s + spec: + tls: + certificateAuthorityDataSource: + kind: Secret + key: bar + issuer: https://foo.bar.com/oauth2/default + authorizationConfig: + additionalScopes: [offline_access, email] + allowPasswordGrant: true + client: + secretName: foo-bar-client-credentials + `), + customResourceName: "invalid-oidc-idp-missing-name", + expectedError: `The OIDCIdentityProvider "%s" is invalid: spec.tls.certificateAuthorityDataSource.name: Required value`, + }, + { + name: "should disallow certificate authority data source with empty value for name", + customResourceYaml: here.Doc(` + --- + apiVersion: idp.supervisor.%s/v1alpha1 + kind: OIDCIdentityProvider + metadata: + name: %s + spec: + tls: + certificateAuthorityDataSource: + kind: Secret + name: "" + key: bar + issuer: https://foo.bar.com/oauth2/default + authorizationConfig: + additionalScopes: [offline_access, email] + allowPasswordGrant: true + client: + secretName: foo-bar-client-credentials + `), + customResourceName: "invalid-oidc-idp-empty-name", + expectedError: `The OIDCIdentityProvider "%s" is invalid: spec.tls.certificateAuthorityDataSource.name: Invalid value: "": spec.tls.certificateAuthorityDataSource.name in body should be at least 1 chars long`, + }, + { + name: "should disallow certificate authority data source with missing key", + customResourceYaml: here.Doc(` + --- + apiVersion: idp.supervisor.%s/v1alpha1 + kind: OIDCIdentityProvider + metadata: + name: %s + spec: + tls: + certificateAuthorityDataSource: + kind: Secret + name: foo + issuer: https://foo.bar.com/oauth2/default + authorizationConfig: + additionalScopes: [offline_access, email] + allowPasswordGrant: true + client: + secretName: foo-bar-client-credentials + `), + customResourceName: "invalid-oidc-idp-missing-key", + expectedError: `The OIDCIdentityProvider "%s" is invalid: spec.tls.certificateAuthorityDataSource.key: Required value`, + }, + { + name: "should disallow certificate authority data source with empty value for key", + customResourceYaml: here.Doc(` + --- + apiVersion: idp.supervisor.%s/v1alpha1 + kind: OIDCIdentityProvider + metadata: + name: %s + spec: + tls: + certificateAuthorityDataSource: + kind: Secret + name: foo + key: "" + issuer: https://foo.bar.com/oauth2/default + authorizationConfig: + additionalScopes: [offline_access, email] + allowPasswordGrant: true + client: + secretName: foo-bar-client-credentials + `), + customResourceName: "invalid-oidc-idp-empty-key", + expectedError: `The OIDCIdentityProvider "%s" is invalid: spec.tls.certificateAuthorityDataSource.key: Invalid value: "": spec.tls.certificateAuthorityDataSource.key in body should be at least 1 chars long`, + }, + { + name: "should disallow certificate authority data source with missing kind", + customResourceYaml: here.Doc(` + --- + apiVersion: idp.supervisor.%s/v1alpha1 + kind: OIDCIdentityProvider + metadata: + name: %s + spec: + tls: + certificateAuthorityDataSource: + name: foo + key: bar + issuer: https://foo.bar.com/oauth2/default + authorizationConfig: + additionalScopes: [offline_access, email] + allowPasswordGrant: true + client: + secretName: foo-bar-client-credentials + `), + customResourceName: "invalid-oidc-idp-missing-kind", + expectedError: `The OIDCIdentityProvider "%s" is invalid: spec.tls.certificateAuthorityDataSource.kind: Required value`, + }, + { + name: "should disallow certificate authority data source with empty value kind", + customResourceYaml: here.Doc(` + --- + apiVersion: idp.supervisor.%s/v1alpha1 + kind: OIDCIdentityProvider + metadata: + name: %s + spec: + tls: + certificateAuthorityDataSource: + kind: "" + name: foo + key: bar + issuer: https://foo.bar.com/oauth2/default + authorizationConfig: + additionalScopes: [offline_access, email] + allowPasswordGrant: true + client: + secretName: foo-bar-client-credentials + `), + customResourceName: "invalid-oidc-idp-invalid-kind", + expectedError: `The OIDCIdentityProvider "%s" is invalid: spec.tls.certificateAuthorityDataSource.kind: Unsupported value: "": supported values: "Secret", "ConfigMap"`, + }, + { + name: "should disallow certificate authority data source with invalid kind", + customResourceYaml: here.Doc(` + --- + apiVersion: idp.supervisor.%s/v1alpha1 + kind: OIDCIdentityProvider + metadata: + name: %s + spec: + tls: + certificateAuthorityDataSource: + kind: sorcery + name: foo + key: bar + issuer: https://foo.bar.com/oauth2/default + authorizationConfig: + additionalScopes: [offline_access, email] + allowPasswordGrant: true + client: + secretName: foo-bar-client-credentials + `), + customResourceName: "invalid-oidc-idp-invalid-kind", + expectedError: `The OIDCIdentityProvider "%s" is invalid: spec.tls.certificateAuthorityDataSource.kind: Unsupported value: "sorcery": supported values: "Secret", "ConfigMap"`, + }, + { + name: "should create a custom resource passing all validations using a Secret source", + customResourceYaml: here.Doc(` + --- + apiVersion: idp.supervisor.%s/v1alpha1 + kind: OIDCIdentityProvider + metadata: + name: %s + spec: + tls: + certificateAuthorityDataSource: + kind: Secret + name: foo + key: bar + issuer: https://foo.bar.com/oauth2/default + authorizationConfig: + additionalScopes: [offline_access, email] + allowPasswordGrant: true + client: + secretName: foo-bar-client-credentials + `), + customResourceName: "valid-oidc-idp-secret-kind", + expectedError: "", + }, + { + name: "should create a custom resource passing all validations using a ConfigMap source", + customResourceYaml: here.Doc(` + --- + apiVersion: idp.supervisor.%s/v1alpha1 + kind: OIDCIdentityProvider + metadata: + name: %s + spec: + tls: + certificateAuthorityDataSource: + kind: ConfigMap + name: foo + key: bar + issuer: https://foo.bar.com/oauth2/default + authorizationConfig: + additionalScopes: [offline_access, email] + allowPasswordGrant: true + client: + secretName: foo-bar-client-credentials + `), + customResourceName: "valid-oidc-idp-cm-kind", + expectedError: "", + }, + { + name: "should create a custom resource without any tls spec", + customResourceYaml: here.Doc(` + --- + apiVersion: idp.supervisor.%s/v1alpha1 + kind: OIDCIdentityProvider + metadata: + name: %s + spec: + issuer: https://foo.bar.com/oauth2/default + authorizationConfig: + additionalScopes: [offline_access, email] + allowPasswordGrant: true + client: + secretName: foo-bar-client-credentials + `), + customResourceName: "no-tls-spec", + expectedError: "", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + yamlFilepath := filepath.Join(t.TempDir(), fmt.Sprintf("tls-spec-validation-%s.yaml", tc.customResourceName)) + + resourceName := tc.customResourceName + "-" + testlib.RandHex(t, 7) + yamlBytes := []byte(fmt.Sprintf(tc.customResourceYaml, env.APIGroupSuffix, resourceName)) + + require.NoError(t, os.WriteFile(yamlFilepath, yamlBytes, 0600)) + cmd := exec.CommandContext(context.Background(), "kubectl", []string{"apply", "-f", yamlFilepath}...) + var stdOut, stdErr bytes.Buffer + cmd.Stdout = &stdOut + cmd.Stderr = &stdErr + err := cmd.Run() + t.Cleanup(func() { + t.Helper() + require.NoError(t, exec.Command("kubectl", []string{"delete", "--ignore-not-found", "-f", yamlFilepath}...).Run()) + }) + if tc.expectedError == "" { + require.NoError(t, err) + require.Equal(t, fmt.Sprintf("oidcidentityprovider.idp.supervisor.pinniped.dev/%s created\n", resourceName), stdOut.String()) + require.Empty(t, stdErr.String()) + } else { + require.Equal(t, fmt.Sprintf(tc.expectedError, resourceName), strings.TrimSuffix(stdErr.String(), "\n")) + } + }) + } +} From 080c75efe6548a49bfb5b31967b4878694f5d925 Mon Sep 17 00:00:00 2001 From: Ashish Amarnath Date: Wed, 26 Jun 2024 00:23:15 -0700 Subject: [PATCH 04/99] refactor tls spec validation into its own package Signed-off-by: Ashish Amarnath --- .../conditionsutil/conditions_util.go | 4 + .../active_directory_upstream_watcher.go | 9 +- .../active_directory_upstream_watcher_test.go | 7 +- .../ldap_upstream_watcher.go | 3 +- .../upstreamwatchers/upstream_watchers.go | 76 +-- .../tlsconfigutil/tls_config_util.go | 181 ++++++++ .../tlsconfigutil/tls_config_util_test.go | 432 ++++++++++++++++++ internal/supervisor/server/server.go | 2 + 8 files changed, 648 insertions(+), 66 deletions(-) create mode 100644 internal/controller/tlsconfigutil/tls_config_util.go create mode 100644 internal/controller/tlsconfigutil/tls_config_util_test.go diff --git a/internal/controller/conditionsutil/conditions_util.go b/internal/controller/conditionsutil/conditions_util.go index 38004b907..383ba20dd 100644 --- a/internal/controller/conditionsutil/conditions_util.go +++ b/internal/controller/conditionsutil/conditions_util.go @@ -12,6 +12,10 @@ import ( "go.pinniped.dev/internal/plog" ) +const ( + ReasonSuccess = "Success" +) + // MergeConditions merges conditions into conditionsToUpdate. // Note that LastTransitionTime refers to the time when the status changed, // but ObservedGeneration should be the current generation for all conditions, since Pinniped should always check every condition. diff --git a/internal/controller/supervisorconfig/activedirectoryupstreamwatcher/active_directory_upstream_watcher.go b/internal/controller/supervisorconfig/activedirectoryupstreamwatcher/active_directory_upstream_watcher.go index efe64d055..b7813a9fb 100644 --- a/internal/controller/supervisorconfig/activedirectoryupstreamwatcher/active_directory_upstream_watcher.go +++ b/internal/controller/supervisorconfig/activedirectoryupstreamwatcher/active_directory_upstream_watcher.go @@ -155,7 +155,7 @@ func (s *activeDirectoryUpstreamGenericLDAPSpec) DetectAndSetSearchBase(ctx cont return &metav1.Condition{ Type: upstreamwatchers.TypeSearchBaseFound, Status: metav1.ConditionTrue, - Reason: upstreamwatchers.ReasonSuccess, + Reason: conditionsutil.ReasonSuccess, Message: "Successfully fetched defaultNamingContext to use as default search base from RootDSE.", } } @@ -235,6 +235,7 @@ type activeDirectoryWatcherController struct { client supervisorclientset.Interface activeDirectoryIdentityProviderInformer idpinformers.ActiveDirectoryIdentityProviderInformer secretInformer corev1informers.SecretInformer + configMapInformer corev1informers.ConfigMapInformer } // New instantiates a new controllerlib.Controller which will populate the provided UpstreamActiveDirectoryIdentityProviderICache. @@ -243,6 +244,7 @@ func New( client supervisorclientset.Interface, activeDirectoryIdentityProviderInformer idpinformers.ActiveDirectoryIdentityProviderInformer, secretInformer corev1informers.SecretInformer, + configMapInformer corev1informers.ConfigMapInformer, withInformer pinnipedcontroller.WithInformerOptionFunc, ) controllerlib.Controller { return newInternal( @@ -254,6 +256,7 @@ func New( client, activeDirectoryIdentityProviderInformer, secretInformer, + configMapInformer, withInformer, ) } @@ -266,6 +269,7 @@ func newInternal( client supervisorclientset.Interface, activeDirectoryIdentityProviderInformer idpinformers.ActiveDirectoryIdentityProviderInformer, secretInformer corev1informers.SecretInformer, + configMapInformer corev1informers.ConfigMapInformer, withInformer pinnipedcontroller.WithInformerOptionFunc, ) controllerlib.Controller { c := activeDirectoryWatcherController{ @@ -275,6 +279,7 @@ func newInternal( client: client, activeDirectoryIdentityProviderInformer: activeDirectoryIdentityProviderInformer, secretInformer: secretInformer, + configMapInformer: configMapInformer, } return controllerlib.New( controllerlib.Config{Name: activeDirectoryControllerName, Syncer: &c}, @@ -357,7 +362,7 @@ func (c *activeDirectoryWatcherController) validateUpstream(ctx context.Context, } } - conditions := upstreamwatchers.ValidateGenericLDAP(ctx, adUpstreamImpl, c.secretInformer, c.validatedSettingsCache, config) + conditions := upstreamwatchers.ValidateGenericLDAP(ctx, adUpstreamImpl, c.secretInformer, c.configMapInformer, c.validatedSettingsCache, config) c.updateStatus(ctx, upstream, conditions.Conditions()) diff --git a/internal/controller/supervisorconfig/activedirectoryupstreamwatcher/active_directory_upstream_watcher_test.go b/internal/controller/supervisorconfig/activedirectoryupstreamwatcher/active_directory_upstream_watcher_test.go index 80e47d7ca..78fd5380c 100644 --- a/internal/controller/supervisorconfig/activedirectoryupstreamwatcher/active_directory_upstream_watcher_test.go +++ b/internal/controller/supervisorconfig/activedirectoryupstreamwatcher/active_directory_upstream_watcher_test.go @@ -80,9 +80,10 @@ func TestActiveDirectoryUpstreamWatcherControllerFilterSecrets(t *testing.T) { fakeKubeClient := fake.NewSimpleClientset() kubeInformers := informers.NewSharedInformerFactory(fakeKubeClient, 0) secretInformer := kubeInformers.Core().V1().Secrets() + configMapInformer := kubeInformers.Core().V1().ConfigMaps() withInformer := testutil.NewObservableWithInformerOption() - New(nil, nil, activeDirectoryIDPInformer, secretInformer, withInformer.WithInformer) + New(nil, nil, activeDirectoryIDPInformer, secretInformer, configMapInformer, withInformer.WithInformer) unrelated := corev1.Secret{} filter := withInformer.GetFilterForInformer(secretInformer) @@ -124,9 +125,10 @@ func TestActiveDirectoryUpstreamWatcherControllerFilterActiveDirectoryIdentityPr fakeKubeClient := fake.NewSimpleClientset() kubeInformers := informers.NewSharedInformerFactory(fakeKubeClient, 0) secretInformer := kubeInformers.Core().V1().Secrets() + configMapInformer := kubeInformers.Core().V1().ConfigMaps() withInformer := testutil.NewObservableWithInformerOption() - New(nil, nil, activeDirectoryIDPInformer, secretInformer, withInformer.WithInformer) + New(nil, nil, activeDirectoryIDPInformer, secretInformer, configMapInformer, withInformer.WithInformer) unrelated := corev1.Secret{} filter := withInformer.GetFilterForInformer(activeDirectoryIDPInformer) @@ -2048,6 +2050,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { fakePinnipedClient, pinnipedInformers.IDP().V1alpha1().ActiveDirectoryIdentityProviders(), kubeInformers.Core().V1().Secrets(), + kubeInformers.Core().V1().ConfigMaps(), controllerlib.WithInformer, ) diff --git a/internal/controller/supervisorconfig/ldapupstreamwatcher/ldap_upstream_watcher.go b/internal/controller/supervisorconfig/ldapupstreamwatcher/ldap_upstream_watcher.go index 820de4b48..bae4dc8b3 100644 --- a/internal/controller/supervisorconfig/ldapupstreamwatcher/ldap_upstream_watcher.go +++ b/internal/controller/supervisorconfig/ldapupstreamwatcher/ldap_upstream_watcher.go @@ -143,6 +143,7 @@ type ldapWatcherController struct { client supervisorclientset.Interface ldapIdentityProviderInformer idpinformers.LDAPIdentityProviderInformer secretInformer corev1informers.SecretInformer + configMapInformer corev1informers.ConfigMapInformer } // New instantiates a new controllerlib.Controller which will populate the provided UpstreamLDAPIdentityProviderICache. @@ -249,7 +250,7 @@ func (c *ldapWatcherController) validateUpstream(ctx context.Context, upstream * Dialer: c.ldapDialer, } - conditions := upstreamwatchers.ValidateGenericLDAP(ctx, &ldapUpstreamGenericLDAPImpl{*upstream}, c.secretInformer, c.validatedSettingsCache, config) + conditions := upstreamwatchers.ValidateGenericLDAP(ctx, &ldapUpstreamGenericLDAPImpl{*upstream}, c.secretInformer, c.configMapInformer, c.validatedSettingsCache, config) c.updateStatus(ctx, upstream, conditions.Conditions()) diff --git a/internal/controller/supervisorconfig/upstreamwatchers/upstream_watchers.go b/internal/controller/supervisorconfig/upstreamwatchers/upstream_watchers.go index 7d3d4f738..d9fa8cf62 100644 --- a/internal/controller/supervisorconfig/upstreamwatchers/upstream_watchers.go +++ b/internal/controller/supervisorconfig/upstreamwatchers/upstream_watchers.go @@ -5,8 +5,6 @@ package upstreamwatchers import ( "context" - "crypto/x509" - "encoding/base64" "fmt" "time" @@ -15,32 +13,27 @@ import ( corev1informers "k8s.io/client-go/informers/core/v1" idpv1alpha1 "go.pinniped.dev/generated/latest/apis/supervisor/idp/v1alpha1" - "go.pinniped.dev/internal/constable" + "go.pinniped.dev/internal/controller/conditionsutil" + "go.pinniped.dev/internal/controller/tlsconfigutil" "go.pinniped.dev/internal/federationdomain/upstreamprovider" "go.pinniped.dev/internal/plog" "go.pinniped.dev/internal/upstreamldap" ) const ( - ReasonNotFound = "SecretNotFound" - ReasonWrongType = "SecretWrongType" - ReasonMissingKeys = "SecretMissingKeys" - ReasonSuccess = "Success" - ReasonInvalidTLSConfig = "InvalidTLSConfig" - - ErrNoCertificates = constable.Error("no certificates found") + ReasonNotFound = "SecretNotFound" + ReasonWrongType = "SecretWrongType" + ReasonMissingKeys = "SecretMissingKeys" LDAPBindAccountSecretType = corev1.SecretTypeBasicAuth probeLDAPTimeout = 90 * time.Second // Constants related to conditions. - typeBindSecretValid = "BindSecretValid" - typeTLSConfigurationValid = "TLSConfigurationValid" - typeLDAPConnectionValid = "LDAPConnectionValid" - TypeSearchBaseFound = "SearchBaseFound" - reasonLDAPConnectionError = "LDAPConnectionError" - noTLSConfigurationMessage = "no TLS configuration provided" - loadedTLSConfigurationMessage = "loaded TLS configuration" + typeBindSecretValid = "BindSecretValid" + typeLDAPConnectionValid = "LDAPConnectionValid" + TypeSearchBaseFound = "SearchBaseFound" + reasonLDAPConnectionError = "LDAPConnectionError" + ReasonUsingConfigurationFromSpec = "UsingConfigurationFromSpec" ReasonErrorFetchingSearchBase = "ErrorFetchingSearchBase" ) @@ -135,29 +128,6 @@ type UpstreamGenericLDAPStatus interface { Conditions() []metav1.Condition } -func ValidateTLSConfig(tlsSpec *idpv1alpha1.TLSSpec, config *upstreamldap.ProviderConfig) *metav1.Condition { - if tlsSpec == nil { - return validTLSCondition(noTLSConfigurationMessage) - } - if len(tlsSpec.CertificateAuthorityData) == 0 { - return validTLSCondition(loadedTLSConfigurationMessage) - } - - bundle, err := base64.StdEncoding.DecodeString(tlsSpec.CertificateAuthorityData) - if err != nil { - return invalidTLSCondition(fmt.Sprintf("certificateAuthorityData is invalid: %s", err.Error())) - } - - ca := x509.NewCertPool() - ok := ca.AppendCertsFromPEM(bundle) - if !ok { - return invalidTLSCondition(fmt.Sprintf("certificateAuthorityData is invalid: %s", ErrNoCertificates)) - } - - config.CABundle = bundle - return validTLSCondition(loadedTLSConfigurationMessage) -} - func TestConnection( ctx context.Context, bindSecretName string, @@ -200,30 +170,12 @@ func TestConnection( return &metav1.Condition{ Type: typeLDAPConnectionValid, Status: metav1.ConditionTrue, - Reason: ReasonSuccess, + Reason: conditionsutil.ReasonSuccess, Message: fmt.Sprintf(`successfully able to connect to "%s" and bind as user "%s" [validated with Secret "%s" at version "%s"]`, config.Host, config.BindUsername, bindSecretName, currentSecretVersion), } } -func validTLSCondition(message string) *metav1.Condition { - return &metav1.Condition{ - Type: typeTLSConfigurationValid, - Status: metav1.ConditionTrue, - Reason: ReasonSuccess, - Message: message, - } -} - -func invalidTLSCondition(message string) *metav1.Condition { - return &metav1.Condition{ - Type: typeTLSConfigurationValid, - Status: metav1.ConditionFalse, - Reason: ReasonInvalidTLSConfig, - Message: message, - } -} - func ValidateSecret(secretInformer corev1informers.SecretInformer, secretName string, secretNamespace string, config *upstreamldap.ProviderConfig) (*metav1.Condition, string) { secret, err := secretInformer.Lister().Secrets(secretNamespace).Get(secretName) if err != nil { @@ -260,7 +212,7 @@ func ValidateSecret(secretInformer corev1informers.SecretInformer, secretName st return &metav1.Condition{ Type: typeBindSecretValid, Status: metav1.ConditionTrue, - Reason: ReasonSuccess, + Reason: conditionsutil.ReasonSuccess, Message: "loaded bind secret", }, secret.ResourceVersion } @@ -292,6 +244,7 @@ func ValidateGenericLDAP( ctx context.Context, upstream UpstreamGenericLDAPIDP, secretInformer corev1informers.SecretInformer, + configMapInformer corev1informers.ConfigMapInformer, validatedSettingsCache ValidatedSettingsCacheI, config *upstreamldap.ProviderConfig, ) GradatedConditions { @@ -300,8 +253,9 @@ func ValidateGenericLDAP( secretValidCondition, currentSecretVersion := ValidateSecret(secretInformer, upstream.Spec().BindSecretName(), upstream.Namespace(), config) conditions.Append(secretValidCondition, true) - tlsValidCondition := ValidateTLSConfig(upstream.Spec().TLSSpec(), config) + tlsValidCondition, caBundle, _, _ := tlsconfigutil.ValidateTLSConfig(upstream.Spec().TLSSpec(), "", upstream.Namespace(), secretInformer, configMapInformer) conditions.Append(tlsValidCondition, true) + config.CABundle = caBundle var ldapConnectionValidCondition, searchBaseFoundCondition *metav1.Condition // No point in trying to connect to the server if the config was already determined to be invalid. diff --git a/internal/controller/tlsconfigutil/tls_config_util.go b/internal/controller/tlsconfigutil/tls_config_util.go new file mode 100644 index 000000000..8c380579d --- /dev/null +++ b/internal/controller/tlsconfigutil/tls_config_util.go @@ -0,0 +1,181 @@ +// Copyright 2024 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package tlsconfigutil + +import ( + "crypto/x509" + "encoding/base64" + "fmt" + + "github.com/pkg/errors" + v12 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/informers/core/v1" + + "go.pinniped.dev/generated/latest/apis/supervisor/idp/v1alpha1" + "go.pinniped.dev/internal/constable" + "go.pinniped.dev/internal/controller/conditionsutil" +) + +const ( + ReasonInvalidTLSConfig = "InvalidTLSConfig" + + noTLSConfigurationMessage = "no TLS configuration provided" + loadedTLSConfigurationMessage = "loaded TLS configuration" + typeTLSConfigurationValid = "TLSConfigurationValid" + + ErrNoCertificates = constable.Error("no certificates found") +) + +// BuildCertPoolIDP reads the tlsSpec of the IDP and returns an X509 cert pool with the CA data that is read either from +// the inline tls.certificateAuthorityData or from a kubernetes secret or a config map as specified in the +// tls.certificateAuthorityDataSource. +// If the provided tlsSpec is nil, a nil CA bundle will be returned. +// If the provided spec contains a CA bundle that is not properly encoded, an error will be returned. +// TODO: should this function be exposed outside this package? +func BuildCertPoolIDP( + tlsSpec *v1alpha1.TLSSpec, + conditionPrefix string, + namespace string, + secretInformer v1.SecretInformer, + configMapInformer v1.ConfigMapInformer, +) (*x509.CertPool, []byte, error) { + // if tlsSpec is nil, we return a nil cert pool and cert bundle. A nil error is also returned to indicate that + // a nil tlsSpec is nevertheless a valid one resulting in a valid TLS condition. + if tlsSpec == nil { + return nil, nil, nil + } + + // it is a configuration error to specify a ca bundle inline using the tls.certificateAuthorityDataSource field + // and also specifying a kubernetes secret or a config map to serve as the source for the ca bundle. + if len(tlsSpec.CertificateAuthorityData) > 0 && tlsSpec.CertificateAuthorityDataSource != nil { + return nil, nil, fmt.Errorf("%s is invalid: both tls.certificateAuthorityDataSource and tls.certificateAuthorityData provided", conditionPrefix) + } + + var err error + caBundle := tlsSpec.CertificateAuthorityData + field := fmt.Sprintf("%s.%s", conditionPrefix, "certificateAuthorityData") + // currently, the ca data supplied inline in the CRDs is expected to be base64 encoded. + // However, the ca data read from kubernetes secrets or config map will not be base64 encoded. + // For kubernetes secrets, secret data read using the client-go code automatically decodes base64 encoded values. + // So a base64 decode is required only when fetching ca bundle from the tls.certificateAuthorityData field. + decodeRequired := true + if tlsSpec.CertificateAuthorityDataSource != nil { + decodeRequired = false + // track the path of the field in the tlsSpec from which the CA data is sourced. + // this will be used to report in the condition status in case an invalid TLS condition is encountered. + field = fmt.Sprintf("%s.%s", conditionPrefix, "certificateAuthorityDataSource") + caBundle, err = readCABundleFromSource(tlsSpec.CertificateAuthorityDataSource, namespace, secretInformer, configMapInformer) + if err != nil { + return nil, nil, fmt.Errorf("%s is invalid: failed to read CA bundle from source %s/%s/%s: %s", + field, tlsSpec.CertificateAuthorityDataSource.Kind, tlsSpec.CertificateAuthorityDataSource.Name, + tlsSpec.CertificateAuthorityDataSource.Key, err.Error()) + } + } + + if len(caBundle) == 0 { + return nil, nil, nil + } + + bundleBytes := []byte(caBundle) + if decodeRequired { + bundleBytes, err = base64.StdEncoding.DecodeString(caBundle) + if err != nil { + return nil, nil, fmt.Errorf("%s is invalid: %s", conditionPrefix, err.Error()) + } + } + + // try to create a cert pool with the read ca data to determine validity of the ca bundle read from the tlsSpec. + ca := x509.NewCertPool() + ok := ca.AppendCertsFromPEM(bundleBytes) + if !ok { + return nil, nil, fmt.Errorf("%s is invalid: %s", field, ErrNoCertificates) + } + + return ca, bundleBytes, nil +} + +// ValidateTLSConfig reads ca bundle in the tlsSpec, supplied either inline using the CertificateAuthorityDate +// or as a reference to a kubernetes secret or configmap using the CertificateAuthorityDataSource, and returns +// a condition of type TLSConfigurationValid based on the validity of the ca bundle, +// a pem encoded ca bundle +// a X509 cert pool with the ca bundle +// any error encountered. +// TODO: it should suffice that this function returns a TLSConfigurationValid condition, and perhaps we could skip +// returning the error. This can be done once all controllers are able to use this function. +func ValidateTLSConfig( + tlsSpec *v1alpha1.TLSSpec, + conditionPrefix string, + namespace string, + secretInformer v1.SecretInformer, + configMapInformer v1.ConfigMapInformer, +) (*v12.Condition, []byte, *x509.CertPool, error) { + // try to build a x509 cert pool using the ca data specified in the tlsSpec. + certPool, bundle, err := BuildCertPoolIDP(tlsSpec, conditionPrefix, namespace, secretInformer, configMapInformer) + if err != nil { + // an error encountered during building a certpool using the ca data from the tlsSpec results in an invalid + // TLS condition. + return invalidTLSCondition(err.Error()), nil, nil, err + } + // for us, an empty or nil ca bundle read is results in a valid TLS condition, but we do want to convey that + // no ca data was supplied. + if bundle == nil { + return validTLSCondition(fmt.Sprintf("%s is valid: %s", conditionPrefix, noTLSConfigurationMessage)), bundle, certPool, err + } + + return validTLSCondition(fmt.Sprintf("%s is valid: %s", conditionPrefix, loadedTLSConfigurationMessage)), bundle, certPool, err +} + +func readCABundleFromSource(source *v1alpha1.CABundleSource, namespace string, secretInformer v1.SecretInformer, configMapInformer v1.ConfigMapInformer) (string, error) { + switch source.Kind { + case "Secret": + return readCABundleFromK8sSecret(namespace, source.Name, source.Key, secretInformer) + case "ConfigMap": + return readCABundleFromK8sConfigMap(namespace, source.Name, source.Key, configMapInformer) + default: + return "", fmt.Errorf("unsupported CA bundle source: %s", source.Kind) + } +} + +func readCABundleFromK8sSecret(namespace string, name string, key string, secretInformer v1.SecretInformer) (string, error) { + s, err := secretInformer.Lister().Secrets(namespace).Get(name) + if err != nil { + return "", errors.Wrapf(err, "failed to get secret %s/%s", namespace, name) + } + // ca bundle in the secret is expected to exist in a specific key, if that key does not exist, then it is an error + if val, exists := s.Data[key]; exists { + return string(val), nil + } + return "", fmt.Errorf("key %s not found in secret %s/%s", key, namespace, name) +} + +func readCABundleFromK8sConfigMap(namespace string, name string, key string, configMapInformer v1.ConfigMapInformer) (string, error) { + c, err := configMapInformer.Lister().ConfigMaps(namespace).Get(name) + if err != nil { + return "", errors.Wrapf(err, "failed to get configmap %s/%s", namespace, name) + } + + // ca bundle in the secret is expected to exist in a specific key, if that key does not exist, then it is an error + if val, exists := c.Data[key]; exists { + return val, nil + } + return "", fmt.Errorf("key %s not found in configmap %s/%s", key, namespace, name) +} + +func validTLSCondition(message string) *v12.Condition { + return &v12.Condition{ + Type: typeTLSConfigurationValid, + Status: v12.ConditionTrue, + Reason: conditionsutil.ReasonSuccess, + Message: message, + } +} + +func invalidTLSCondition(message string) *v12.Condition { + return &v12.Condition{ + Type: typeTLSConfigurationValid, + Status: v12.ConditionFalse, + Reason: ReasonInvalidTLSConfig, + Message: message, + } +} diff --git a/internal/controller/tlsconfigutil/tls_config_util_test.go b/internal/controller/tlsconfigutil/tls_config_util_test.go new file mode 100644 index 000000000..f776d1e65 --- /dev/null +++ b/internal/controller/tlsconfigutil/tls_config_util_test.go @@ -0,0 +1,432 @@ +// Copyright 2024 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package tlsconfigutil + +import ( + "encoding/base64" + "fmt" + "testing" + "time" + + "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/informers" + corev1informers "k8s.io/client-go/informers/core/v1" + "k8s.io/client-go/kubernetes/fake" + + idpv1alpha1 "go.pinniped.dev/generated/latest/apis/supervisor/idp/v1alpha1" + "go.pinniped.dev/internal/certauthority" +) + +func TestValidateTLSConfig(t *testing.T) { + testCA, err := certauthority.New("Test CA", 1*time.Hour) + require.NoError(t, err) + bundle := testCA.Bundle() + base64EncodedBundle := base64.StdEncoding.EncodeToString(bundle) + tests := []struct { + name string + tlsSpec *idpv1alpha1.TLSSpec + namespace string + k8sObjects []runtime.Object + expectedCondition *metav1.Condition + expectError bool + }{ + { + name: "nil TLSSpec should generate a noTLSConfigurationMessage condition", + tlsSpec: nil, + expectedCondition: validTLSCondition(noTLSConfigurationMessage), + expectError: false, + }, + { + name: "empty inline ca data should generate a loadedTLSConfigurationMessage condition", + tlsSpec: &idpv1alpha1.TLSSpec{}, + expectedCondition: validTLSCondition(loadedTLSConfigurationMessage), + expectError: false, + }, + { + name: "valid base64 encode ca data should generate a loadedTLSConfigurationMessage condition", + tlsSpec: &idpv1alpha1.TLSSpec{ + CertificateAuthorityData: base64EncodedBundle, + }, + expectedCondition: validTLSCondition(loadedTLSConfigurationMessage), + expectError: false, + }, + { + name: "valid base64 encoded non cert data should generate a invalidTLSCondition condition", + tlsSpec: &idpv1alpha1.TLSSpec{ + CertificateAuthorityData: "dGhpcyBpcyBzb21lIHRlc3QgZGF0YSB0aGF0IGlzIGJhc2U2NCBlbmNvZGVkIHRoYXQgaXMgbm90IGEgY2VydAo=", + }, + expectedCondition: invalidTLSCondition(fmt.Sprintf("certificateAuthorityData is invalid: %s", ErrNoCertificates)), + expectError: true, + }, + { + name: "non-base64 encoded string as ca data should generate an invalidTLSCondition condition", + tlsSpec: &idpv1alpha1.TLSSpec{ + CertificateAuthorityData: "non base64 encoded string", + }, + expectedCondition: invalidTLSCondition("certificateAuthorityData is invalid: illegal base64 data"), + expectError: true, + }, + { + name: "supplying certificateAuthorityDataSource and certificateAuthorityData should generate an invalid condition", + tlsSpec: &idpv1alpha1.TLSSpec{ + CertificateAuthorityData: base64EncodedBundle, + CertificateAuthorityDataSource: &idpv1alpha1.CABundleSource{ + Kind: "Secret", + Name: "super-secret", + Key: "ca-base64EncodedBundle", + }, + }, + expectedCondition: invalidTLSCondition("tls spec config error: both tls.certificateAuthorityDataSource and tls.certificateAuthorityData provided."), + expectError: true, + }, + { + name: "should return ca bundle from kubernetes secret", + tlsSpec: &idpv1alpha1.TLSSpec{ + CertificateAuthorityDataSource: &idpv1alpha1.CABundleSource{ + Kind: "Secret", + Name: "awesome-secret", + Key: "ca-bundle", + }, + }, + namespace: "awesome-namespace", + k8sObjects: []runtime.Object{ + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "awesome-secret", + Namespace: "awesome-namespace", + }, + Data: map[string][]byte{ + "ca-bundle": []byte(bundle), + }, + }, + }, + expectedCondition: validTLSCondition(fmt.Sprintf("tls is valid: %s", loadedTLSConfigurationMessage)), + expectError: false, + }, + { + name: "should return ca bundle from kubernetes configMap", + tlsSpec: &idpv1alpha1.TLSSpec{ + CertificateAuthorityDataSource: &idpv1alpha1.CABundleSource{ + Kind: "ConfigMap", + Name: "awesome-cm", + Key: "ca-bundle", + }, + }, + namespace: "awesome-namespace", + k8sObjects: []runtime.Object{ + &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "awesome-cm", + Namespace: "awesome-namespace", + }, + Data: map[string]string{ + "ca-bundle": string(bundle), + }, + }, + }, + expectedCondition: validTLSCondition(fmt.Sprintf("tls is valid: %s", loadedTLSConfigurationMessage)), + expectError: false, + }, + { + name: "should return invalid condition when failing to read ca bundle from kubernetes secret that does not exist", + tlsSpec: &idpv1alpha1.TLSSpec{ + CertificateAuthorityDataSource: &idpv1alpha1.CABundleSource{ + Kind: "Secret", + Name: "does-not-exist", + Key: "does-not-matter", + }, + }, + namespace: "awesome-namespace", + k8sObjects: []runtime.Object{ + &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "awesome-cm", + Namespace: "awesome-namespace", + }, + Data: map[string]string{ + "ca-bundle": string(bundle), + }, + }, + }, + expectedCondition: invalidTLSCondition("tls.certificateAuthorityDataSource is invalid: failed to read from source"), + expectError: true, + }, + { + name: "should return invalid condition when failing to read ca bundle from kubernetes configMap that does not exist", + tlsSpec: &idpv1alpha1.TLSSpec{ + CertificateAuthorityDataSource: &idpv1alpha1.CABundleSource{ + Kind: "ConfigMap", + Name: "does-not-exist", + Key: "does-not-matter", + }, + }, + namespace: "awesome-namespace", + k8sObjects: []runtime.Object{ + &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "awesome-cm", + Namespace: "awesome-namespace", + }, + Data: map[string]string{ + "ca-bundle": string(bundle), + }, + }, + }, + expectedCondition: invalidTLSCondition("tls.certificateAuthorityDataSource is invalid: failed to read from source"), + expectError: true, + }, + { + name: "should return invalid condition when using an invalid certificate authority data source", + tlsSpec: &idpv1alpha1.TLSSpec{ + CertificateAuthorityDataSource: &idpv1alpha1.CABundleSource{ + Kind: "SomethingElse", + Name: "does-not-exist", + Key: "does-not-matter", + }, + }, + namespace: "awesome-namespace", + k8sObjects: []runtime.Object{ + &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "awesome-cm", + Namespace: "awesome-namespace", + }, + Data: map[string]string{ + "ca-bundle": string(bundle), + }, + }, + }, + expectedCondition: invalidTLSCondition("tls.certificateAuthorityDataSource is invalid: unsupported CA bundle source"), + expectError: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + var secretsInformer corev1informers.SecretInformer + var configMapInformer corev1informers.ConfigMapInformer + + if len(tt.k8sObjects) > 0 { + stopSecretInformer := make(chan struct{}) + stopConfigMapInformer := make(chan struct{}) + fakeClient := fake.NewSimpleClientset(tt.k8sObjects...) + sharedInformers := informers.NewSharedInformerFactory(fakeClient, time.Second) + configMapInformer = sharedInformers.Core().V1().ConfigMaps() + secretsInformer = sharedInformers.Core().V1().Secrets() + + // Run the informer so that it can sync the objects from kubernetes into its cache. + // run as a go routine so that we can stop the informer and continue with our tests. + go secretsInformer.Informer().Run(stopSecretInformer) + // wait 1s before stopping the informer. 1s because, that's the resync duration of the informer. + time.Sleep(time.Second) + close(stopSecretInformer) + // TODO: can we avoid calling Run on both informers? + go configMapInformer.Informer().Run(stopConfigMapInformer) + time.Sleep(time.Second) + close(stopConfigMapInformer) + // now the objects from kubernetes should be sync'd into the informer cache. + } + actualCondition, _, _, err := ValidateTLSConfig(tt.tlsSpec, "tls", tt.namespace, secretsInformer, configMapInformer) + if tt.expectError { + require.Error(t, err) + } else { + require.NoError(t, err) + require.Equal(t, tt.expectedCondition.Type, actualCondition.Type) + require.Equal(t, tt.expectedCondition.Status, actualCondition.Status) + require.Equal(t, tt.expectedCondition.Reason, actualCondition.Reason) + } + }) + } +} + +func TestReadCABundleFromK8sSecret(t *testing.T) { + tests := []struct { + name string + secretNamespace string + secretName string + secretKey string + k8sObjects []runtime.Object + expectedData string + expectError bool + }{ + { + name: "should return error reading a non-existent secret", + secretNamespace: "awesome-namespace", + secretName: "does-not-exist", + secretKey: "does-not-matter", + k8sObjects: []runtime.Object{ + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "awesome-secret", + Namespace: "awesome-namespace", + }, + Data: map[string][]byte{ + "awesome": []byte("pinniped-is-awesome"), + }, + }, + }, + expectedData: "", + expectError: true, + }, + { + name: "should return error reading a non-existing key in an existing secret", + secretNamespace: "awesome-namespace", + secretName: "awesome-secret", + secretKey: "something-else", + k8sObjects: []runtime.Object{ + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "awesome-secret", + Namespace: "awesome-namespace", + }, + Data: map[string][]byte{ + "awesome": []byte("pinniped-is-awesome"), + }, + }, + }, + expectedData: "", + expectError: true, + }, + { + name: "should return data from existing secret and existing key", + secretNamespace: "awesome-namespace", + secretName: "awesome-secret", + secretKey: "awesome", + k8sObjects: []runtime.Object{ + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "awesome-secret", + Namespace: "awesome-namespace", + }, + Data: map[string][]byte{ + "awesome": []byte("pinniped-is-awesome"), + }, + }, + }, + expectedData: "pinniped-is-awesome", + expectError: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + stop := make(chan struct{}) + fakeClient := fake.NewSimpleClientset(tt.k8sObjects...) + secretsInformer := informers.NewSharedInformerFactory(fakeClient, time.Second).Core().V1().Secrets() + // Run the informer so that it can sync the objects from kubernetes into its cache. + // run as a go routine so that we can stop the informer and continue with our tests. + go secretsInformer.Informer().Run(stop) + // wait 1s before stopping the informer. 1s because, that's the resync duration of the informer. + time.Sleep(time.Second) + close(stop) + // now the objects from kubernetes should be sync'd into the informer cache. + actualData, actualError := readCABundleFromK8sSecret(tt.secretNamespace, tt.secretName, tt.secretKey, secretsInformer) + if tt.expectError { + require.Error(t, actualError) + } else { + require.NoError(t, actualError) + } + require.Equal(t, tt.expectedData, actualData) + }) + } +} + +func TestReadCABundleFromK8sConfigMap(t *testing.T) { + tests := []struct { + name string + configMapNamespace string + configMapName string + configMapKey string + k8sObjects []runtime.Object + expectedData string + expectError bool + }{ + { + name: "should return error reading a non-existent configMap", + configMapNamespace: "awesome-namespace", + configMapName: "does-not-exist", + configMapKey: "does-not-matter", + k8sObjects: []runtime.Object{ + &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "awesome-configmap", + Namespace: "awesome-namespace", + }, + Data: map[string]string{ + "awesome": "pinniped-is-awesome", + }, + }, + }, + expectedData: "", + expectError: true, + }, + { + name: "should return error reading a non-existing key in an existing configMap", + configMapNamespace: "awesome-namespace", + configMapName: "awesome-configmap", + configMapKey: "does-not-exist", + k8sObjects: []runtime.Object{ + &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "awesome-configmap", + Namespace: "awesome-namespace", + }, + Data: map[string]string{ + "awesome": "pinniped-is-awesome", + }, + }, + }, + expectedData: "", + expectError: true, + }, + { + name: "should return expected data from an existing key in an existing configMap", + configMapNamespace: "awesome-namespace", + configMapName: "awesome-configmap", + configMapKey: "awesome", + k8sObjects: []runtime.Object{ + &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "awesome-configmap", + Namespace: "awesome-namespace", + }, + Data: map[string]string{ + "awesome": "pinniped-is-awesome", + }, + }, + }, + expectedData: "pinniped-is-awesome", + expectError: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + stop := make(chan struct{}) + fakeClient := fake.NewSimpleClientset(tt.k8sObjects...) + configMapInformer := informers.NewSharedInformerFactory(fakeClient, time.Second).Core().V1().ConfigMaps() + + // Run the informer so that it can sync the objects from kubernetes into its cache. + // run as a go routine so that we can stop the informer and continue with our tests. + go configMapInformer.Informer().Run(stop) + // wait 1s before stopping the informer. 1s because, that's the resync duration of the informer. + time.Sleep(time.Second) + close(stop) + // now the objects from kubernetes should be sync'd into the informer cache. + actualData, actualError := readCABundleFromK8sConfigMap(tt.configMapNamespace, tt.configMapName, tt.configMapKey, configMapInformer) + if tt.expectError { + require.Error(t, actualError) + } else { + require.NoError(t, actualError) + } + require.Equal(t, tt.expectedData, actualData) + }) + } +} diff --git a/internal/supervisor/server/server.go b/internal/supervisor/server/server.go index e3d796127..4dbf79245 100644 --- a/internal/supervisor/server/server.go +++ b/internal/supervisor/server/server.go @@ -154,6 +154,7 @@ func prepareControllers( federationDomainInformer := pinnipedInformers.Config().V1alpha1().FederationDomains() oidcClientInformer := pinnipedInformers.Config().V1alpha1().OIDCClients() secretInformer := kubeInformers.Core().V1().Secrets() + configMapInformer := kubeInformers.Core().V1().ConfigMaps() // Create controller manager. controllerManager := controllerlib. @@ -322,6 +323,7 @@ func prepareControllers( pinnipedClient, pinnipedInformers.IDP().V1alpha1().ActiveDirectoryIdentityProviders(), secretInformer, + configMapInformer, controllerlib.WithInformer, ), singletonWorker). From aab1ee9edcd3cae660d44e20d757d46201b7b405 Mon Sep 17 00:00:00 2001 From: Ashish Amarnath Date: Wed, 26 Jun 2024 13:14:42 -0700 Subject: [PATCH 05/99] unify TLS Spec between supervisor and concierge Signed-off-by: Ashish Amarnath --- .../upstreamwatchers/upstream_watchers.go | 3 +- .../tlsconfigutil/tls_config_util.go | 101 ++++-- .../tlsconfigutil/tls_config_util_test.go | 322 ++++++++++++++---- 3 files changed, 345 insertions(+), 81 deletions(-) diff --git a/internal/controller/supervisorconfig/upstreamwatchers/upstream_watchers.go b/internal/controller/supervisorconfig/upstreamwatchers/upstream_watchers.go index d9fa8cf62..35cdc05b0 100644 --- a/internal/controller/supervisorconfig/upstreamwatchers/upstream_watchers.go +++ b/internal/controller/supervisorconfig/upstreamwatchers/upstream_watchers.go @@ -253,7 +253,8 @@ func ValidateGenericLDAP( secretValidCondition, currentSecretVersion := ValidateSecret(secretInformer, upstream.Spec().BindSecretName(), upstream.Namespace(), config) conditions.Append(secretValidCondition, true) - tlsValidCondition, caBundle, _, _ := tlsconfigutil.ValidateTLSConfig(upstream.Spec().TLSSpec(), "", upstream.Namespace(), secretInformer, configMapInformer) + tlsSpec := tlsconfigutil.TLSSpecForSupervisor(upstream.Spec().TLSSpec()) + tlsValidCondition, caBundle, _, _ := tlsconfigutil.ValidateTLSConfig(tlsSpec, "", upstream.Namespace(), secretInformer, configMapInformer) conditions.Append(tlsValidCondition, true) config.CABundle = caBundle diff --git a/internal/controller/tlsconfigutil/tls_config_util.go b/internal/controller/tlsconfigutil/tls_config_util.go index 8c380579d..2f1883757 100644 --- a/internal/controller/tlsconfigutil/tls_config_util.go +++ b/internal/controller/tlsconfigutil/tls_config_util.go @@ -9,10 +9,12 @@ import ( "fmt" "github.com/pkg/errors" - v12 "k8s.io/apimachinery/pkg/apis/meta/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/informers/core/v1" - "go.pinniped.dev/generated/latest/apis/supervisor/idp/v1alpha1" + conciergev1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/authentication/v1alpha1" + supervisorv1alpha1 "go.pinniped.dev/generated/latest/apis/supervisor/idp/v1alpha1" "go.pinniped.dev/internal/constable" "go.pinniped.dev/internal/controller/conditionsutil" ) @@ -27,14 +29,66 @@ const ( ErrNoCertificates = constable.Error("no certificates found") ) -// BuildCertPoolIDP reads the tlsSpec of the IDP and returns an X509 cert pool with the CA data that is read either from +type caBundleSource struct { + Kind string + Name string + Key string +} + +// TLSSpec unifies the TLSSpec type that Supervisor and Concierge both individually define. +// unifying these two definitions to allow sharing code that will read the spec and translate it into a CA bundle +type TLSSpec struct { + // X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. + CertificateAuthorityData string + // Reference to a CA bundle in a secret or a configmap. + CertificateAuthorityDataSource *caBundleSource +} + +// TLSSpecForSupervisor is a helper function to convert the Supervisor's TLSSpec to the unified TLSSpec +func TLSSpecForSupervisor(source *supervisorv1alpha1.TLSSpec) *TLSSpec { + if source == nil { + return nil + } + dest := &TLSSpec{ + CertificateAuthorityData: source.CertificateAuthorityData, + } + + if source.CertificateAuthorityDataSource != nil { + dest.CertificateAuthorityDataSource = &caBundleSource{ + Kind: source.CertificateAuthorityDataSource.Kind, + Name: source.CertificateAuthorityDataSource.Name, + Key: source.CertificateAuthorityDataSource.Key, + } + } + + return dest +} + +// TlsSpecForConcierge is a helper function to convert the Concierge's TLSSpec to the unified TLSSpec +func TlsSpecForConcierge(source *conciergev1alpha1.TLSSpec) *TLSSpec { + if source == nil { + return nil + } + dest := &TLSSpec{ + CertificateAuthorityData: source.CertificateAuthorityData, + } + if source.CertificateAuthorityDataSource != nil { + dest.CertificateAuthorityDataSource = &caBundleSource{ + Kind: source.CertificateAuthorityDataSource.Kind, + Name: source.CertificateAuthorityDataSource.Name, + Key: source.CertificateAuthorityDataSource.Key, + } + } + return dest +} + +// getCertPool reads the unified tlsSpec and returns an X509 cert pool with the CA data that is read either from // the inline tls.certificateAuthorityData or from a kubernetes secret or a config map as specified in the // tls.certificateAuthorityDataSource. // If the provided tlsSpec is nil, a nil CA bundle will be returned. // If the provided spec contains a CA bundle that is not properly encoded, an error will be returned. -// TODO: should this function be exposed outside this package? -func BuildCertPoolIDP( - tlsSpec *v1alpha1.TLSSpec, +func getCertPool( + tlsSpec *TLSSpec, conditionPrefix string, namespace string, secretInformer v1.SecretInformer, @@ -55,7 +109,7 @@ func BuildCertPoolIDP( var err error caBundle := tlsSpec.CertificateAuthorityData field := fmt.Sprintf("%s.%s", conditionPrefix, "certificateAuthorityData") - // currently, the ca data supplied inline in the CRDs is expected to be base64 encoded. + // the ca data supplied inline in the CRDs is expected to be base64 encoded. // However, the ca data read from kubernetes secrets or config map will not be base64 encoded. // For kubernetes secrets, secret data read using the client-go code automatically decodes base64 encoded values. // So a base64 decode is required only when fetching ca bundle from the tls.certificateAuthorityData field. @@ -67,9 +121,7 @@ func BuildCertPoolIDP( field = fmt.Sprintf("%s.%s", conditionPrefix, "certificateAuthorityDataSource") caBundle, err = readCABundleFromSource(tlsSpec.CertificateAuthorityDataSource, namespace, secretInformer, configMapInformer) if err != nil { - return nil, nil, fmt.Errorf("%s is invalid: failed to read CA bundle from source %s/%s/%s: %s", - field, tlsSpec.CertificateAuthorityDataSource.Kind, tlsSpec.CertificateAuthorityDataSource.Name, - tlsSpec.CertificateAuthorityDataSource.Key, err.Error()) + return nil, nil, fmt.Errorf("%s is invalid: %s", field, err.Error()) } } @@ -81,7 +133,7 @@ func BuildCertPoolIDP( if decodeRequired { bundleBytes, err = base64.StdEncoding.DecodeString(caBundle) if err != nil { - return nil, nil, fmt.Errorf("%s is invalid: %s", conditionPrefix, err.Error()) + return nil, nil, fmt.Errorf("%s is invalid: %s", field, err.Error()) } } @@ -104,14 +156,14 @@ func BuildCertPoolIDP( // TODO: it should suffice that this function returns a TLSConfigurationValid condition, and perhaps we could skip // returning the error. This can be done once all controllers are able to use this function. func ValidateTLSConfig( - tlsSpec *v1alpha1.TLSSpec, + tlsSpec *TLSSpec, conditionPrefix string, namespace string, secretInformer v1.SecretInformer, configMapInformer v1.ConfigMapInformer, -) (*v12.Condition, []byte, *x509.CertPool, error) { +) (*metav1.Condition, []byte, *x509.CertPool, error) { // try to build a x509 cert pool using the ca data specified in the tlsSpec. - certPool, bundle, err := BuildCertPoolIDP(tlsSpec, conditionPrefix, namespace, secretInformer, configMapInformer) + certPool, bundle, err := getCertPool(tlsSpec, conditionPrefix, namespace, secretInformer, configMapInformer) if err != nil { // an error encountered during building a certpool using the ca data from the tlsSpec results in an invalid // TLS condition. @@ -126,14 +178,14 @@ func ValidateTLSConfig( return validTLSCondition(fmt.Sprintf("%s is valid: %s", conditionPrefix, loadedTLSConfigurationMessage)), bundle, certPool, err } -func readCABundleFromSource(source *v1alpha1.CABundleSource, namespace string, secretInformer v1.SecretInformer, configMapInformer v1.ConfigMapInformer) (string, error) { +func readCABundleFromSource(source *caBundleSource, namespace string, secretInformer v1.SecretInformer, configMapInformer v1.ConfigMapInformer) (string, error) { switch source.Kind { case "Secret": return readCABundleFromK8sSecret(namespace, source.Name, source.Key, secretInformer) case "ConfigMap": return readCABundleFromK8sConfigMap(namespace, source.Name, source.Key, configMapInformer) default: - return "", fmt.Errorf("unsupported CA bundle source: %s", source.Kind) + return "", fmt.Errorf("unsupported CA bundle source kind: %s", source.Kind) } } @@ -142,6 +194,11 @@ func readCABundleFromK8sSecret(namespace string, name string, key string, secret if err != nil { return "", errors.Wrapf(err, "failed to get secret %s/%s", namespace, name) } + // for kubernetes secrets to be used as a certificate authority data source, the secret should be of type + // kubernetes.io/tls or Opaque. It is an error to use a secret that is of any other type. + if s.Type != corev1.SecretTypeTLS && s.Type != corev1.SecretTypeOpaque { + return "", fmt.Errorf("secret %s/%s of type %s cannot be used as a certificate authority data source", namespace, name, s.Type) + } // ca bundle in the secret is expected to exist in a specific key, if that key does not exist, then it is an error if val, exists := s.Data[key]; exists { return string(val), nil @@ -162,19 +219,19 @@ func readCABundleFromK8sConfigMap(namespace string, name string, key string, con return "", fmt.Errorf("key %s not found in configmap %s/%s", key, namespace, name) } -func validTLSCondition(message string) *v12.Condition { - return &v12.Condition{ +func validTLSCondition(message string) *metav1.Condition { + return &metav1.Condition{ Type: typeTLSConfigurationValid, - Status: v12.ConditionTrue, + Status: metav1.ConditionTrue, Reason: conditionsutil.ReasonSuccess, Message: message, } } -func invalidTLSCondition(message string) *v12.Condition { - return &v12.Condition{ +func invalidTLSCondition(message string) *metav1.Condition { + return &metav1.Condition{ Type: typeTLSConfigurationValid, - Status: v12.ConditionFalse, + Status: metav1.ConditionFalse, Reason: ReasonInvalidTLSConfig, Message: message, } diff --git a/internal/controller/tlsconfigutil/tls_config_util_test.go b/internal/controller/tlsconfigutil/tls_config_util_test.go index f776d1e65..72f459688 100644 --- a/internal/controller/tlsconfigutil/tls_config_util_test.go +++ b/internal/controller/tlsconfigutil/tls_config_util_test.go @@ -5,7 +5,6 @@ package tlsconfigutil import ( "encoding/base64" - "fmt" "testing" "time" @@ -17,8 +16,10 @@ import ( corev1informers "k8s.io/client-go/informers/core/v1" "k8s.io/client-go/kubernetes/fake" - idpv1alpha1 "go.pinniped.dev/generated/latest/apis/supervisor/idp/v1alpha1" + conciergev1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/authentication/v1alpha1" + supervisorv1alpha1 "go.pinniped.dev/generated/latest/apis/supervisor/idp/v1alpha1" "go.pinniped.dev/internal/certauthority" + "go.pinniped.dev/internal/controller/conditionsutil" ) func TestValidateTLSConfig(t *testing.T) { @@ -28,67 +29,90 @@ func TestValidateTLSConfig(t *testing.T) { base64EncodedBundle := base64.StdEncoding.EncodeToString(bundle) tests := []struct { name string - tlsSpec *idpv1alpha1.TLSSpec + tlsSpec *TLSSpec namespace string k8sObjects []runtime.Object expectedCondition *metav1.Condition - expectError bool }{ { - name: "nil TLSSpec should generate a noTLSConfigurationMessage condition", - tlsSpec: nil, - expectedCondition: validTLSCondition(noTLSConfigurationMessage), - expectError: false, + name: "nil TLSSpec should generate a noTLSConfigurationMessage condition", + tlsSpec: nil, + expectedCondition: &metav1.Condition{ + Type: typeTLSConfigurationValid, + Status: metav1.ConditionTrue, + Reason: conditionsutil.ReasonSuccess, + Message: "tls is valid: " + noTLSConfigurationMessage, + }, }, { - name: "empty inline ca data should generate a loadedTLSConfigurationMessage condition", - tlsSpec: &idpv1alpha1.TLSSpec{}, - expectedCondition: validTLSCondition(loadedTLSConfigurationMessage), - expectError: false, + name: "empty inline ca data should generate a loadedTLSConfigurationMessage condition", + tlsSpec: &TLSSpec{}, + expectedCondition: &metav1.Condition{ + Type: typeTLSConfigurationValid, + Status: metav1.ConditionTrue, + Reason: conditionsutil.ReasonSuccess, + Message: "tls is valid: " + noTLSConfigurationMessage, + }, }, { name: "valid base64 encode ca data should generate a loadedTLSConfigurationMessage condition", - tlsSpec: &idpv1alpha1.TLSSpec{ + tlsSpec: &TLSSpec{ CertificateAuthorityData: base64EncodedBundle, }, - expectedCondition: validTLSCondition(loadedTLSConfigurationMessage), - expectError: false, + expectedCondition: &metav1.Condition{ + Type: typeTLSConfigurationValid, + Status: metav1.ConditionTrue, + Reason: conditionsutil.ReasonSuccess, + Message: "tls is valid: " + loadedTLSConfigurationMessage, + }, }, { name: "valid base64 encoded non cert data should generate a invalidTLSCondition condition", - tlsSpec: &idpv1alpha1.TLSSpec{ + tlsSpec: &TLSSpec{ CertificateAuthorityData: "dGhpcyBpcyBzb21lIHRlc3QgZGF0YSB0aGF0IGlzIGJhc2U2NCBlbmNvZGVkIHRoYXQgaXMgbm90IGEgY2VydAo=", }, - expectedCondition: invalidTLSCondition(fmt.Sprintf("certificateAuthorityData is invalid: %s", ErrNoCertificates)), - expectError: true, + expectedCondition: &metav1.Condition{ + Type: typeTLSConfigurationValid, + Status: metav1.ConditionFalse, + Reason: ReasonInvalidTLSConfig, + Message: "tls.certificateAuthorityData is invalid: " + ErrNoCertificates.Error(), + }, }, { name: "non-base64 encoded string as ca data should generate an invalidTLSCondition condition", - tlsSpec: &idpv1alpha1.TLSSpec{ + tlsSpec: &TLSSpec{ CertificateAuthorityData: "non base64 encoded string", }, - expectedCondition: invalidTLSCondition("certificateAuthorityData is invalid: illegal base64 data"), - expectError: true, + expectedCondition: &metav1.Condition{ + Type: typeTLSConfigurationValid, + Status: metav1.ConditionFalse, + Reason: ReasonInvalidTLSConfig, + Message: "tls.certificateAuthorityData is invalid: illegal base64 data at input byte 3", + }, }, { name: "supplying certificateAuthorityDataSource and certificateAuthorityData should generate an invalid condition", - tlsSpec: &idpv1alpha1.TLSSpec{ + tlsSpec: &TLSSpec{ CertificateAuthorityData: base64EncodedBundle, - CertificateAuthorityDataSource: &idpv1alpha1.CABundleSource{ + CertificateAuthorityDataSource: &caBundleSource{ Kind: "Secret", Name: "super-secret", Key: "ca-base64EncodedBundle", }, }, - expectedCondition: invalidTLSCondition("tls spec config error: both tls.certificateAuthorityDataSource and tls.certificateAuthorityData provided."), - expectError: true, + expectedCondition: &metav1.Condition{ + Type: typeTLSConfigurationValid, + Status: metav1.ConditionFalse, + Reason: ReasonInvalidTLSConfig, + Message: "tls is invalid: both tls.certificateAuthorityDataSource and tls.certificateAuthorityData provided", + }, }, { - name: "should return ca bundle from kubernetes secret", - tlsSpec: &idpv1alpha1.TLSSpec{ - CertificateAuthorityDataSource: &idpv1alpha1.CABundleSource{ + name: "should return ca bundle from kubernetes secret of type tls", + tlsSpec: &TLSSpec{ + CertificateAuthorityDataSource: &caBundleSource{ Kind: "Secret", - Name: "awesome-secret", + Name: "awesome-secret-tls", Key: "ca-bundle", }, }, @@ -96,21 +120,86 @@ func TestValidateTLSConfig(t *testing.T) { k8sObjects: []runtime.Object{ &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ - Name: "awesome-secret", + Name: "awesome-secret-tls", Namespace: "awesome-namespace", }, + Type: corev1.SecretTypeTLS, Data: map[string][]byte{ - "ca-bundle": []byte(bundle), + "ca-bundle": bundle, }, }, }, - expectedCondition: validTLSCondition(fmt.Sprintf("tls is valid: %s", loadedTLSConfigurationMessage)), - expectError: false, + expectedCondition: &metav1.Condition{ + Type: typeTLSConfigurationValid, + Status: metav1.ConditionTrue, + Reason: conditionsutil.ReasonSuccess, + Message: "tls is valid: loaded TLS configuration", + }, }, + { + name: "should return ca bundle from kubernetes secret of type opaque", + tlsSpec: &TLSSpec{ + CertificateAuthorityDataSource: &caBundleSource{ + Kind: "Secret", + Name: "awesome-secret-opaque", + Key: "ca-bundle", + }, + }, + namespace: "awesome-namespace", + k8sObjects: []runtime.Object{ + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "awesome-secret-opaque", + Namespace: "awesome-namespace", + }, + Type: corev1.SecretTypeOpaque, + Data: map[string][]byte{ + "ca-bundle": bundle, + }, + }, + }, + expectedCondition: &metav1.Condition{ + Type: typeTLSConfigurationValid, + Status: metav1.ConditionTrue, + Reason: conditionsutil.ReasonSuccess, + Message: "tls is valid: loaded TLS configuration", + }, + }, + + { + name: "should return invalid condition when a secrets not of type tls or opaque are used as ca data source", + tlsSpec: &TLSSpec{ + CertificateAuthorityDataSource: &caBundleSource{ + Kind: "Secret", + Name: "awesome-secret-ba", + Key: "ca-bundle", + }, + }, + namespace: "awesome-namespace", + k8sObjects: []runtime.Object{ + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "awesome-secret-ba", + Namespace: "awesome-namespace", + }, + Type: corev1.SecretTypeBasicAuth, + Data: map[string][]byte{ + "ca-bundle": bundle, + }, + }, + }, + expectedCondition: &metav1.Condition{ + Type: typeTLSConfigurationValid, + Status: metav1.ConditionFalse, + Reason: ReasonInvalidTLSConfig, + Message: "tls.certificateAuthorityDataSource is invalid: secret awesome-namespace/awesome-secret-ba of type kubernetes.io/basic-auth cannot be used as a certificate authority data source", + }, + }, + { name: "should return ca bundle from kubernetes configMap", - tlsSpec: &idpv1alpha1.TLSSpec{ - CertificateAuthorityDataSource: &idpv1alpha1.CABundleSource{ + tlsSpec: &TLSSpec{ + CertificateAuthorityDataSource: &caBundleSource{ Kind: "ConfigMap", Name: "awesome-cm", Key: "ca-bundle", @@ -128,13 +217,17 @@ func TestValidateTLSConfig(t *testing.T) { }, }, }, - expectedCondition: validTLSCondition(fmt.Sprintf("tls is valid: %s", loadedTLSConfigurationMessage)), - expectError: false, + expectedCondition: &metav1.Condition{ + Type: typeTLSConfigurationValid, + Status: metav1.ConditionTrue, + Reason: conditionsutil.ReasonSuccess, + Message: "tls is valid: loaded TLS configuration", + }, }, { name: "should return invalid condition when failing to read ca bundle from kubernetes secret that does not exist", - tlsSpec: &idpv1alpha1.TLSSpec{ - CertificateAuthorityDataSource: &idpv1alpha1.CABundleSource{ + tlsSpec: &TLSSpec{ + CertificateAuthorityDataSource: &caBundleSource{ Kind: "Secret", Name: "does-not-exist", Key: "does-not-matter", @@ -152,13 +245,17 @@ func TestValidateTLSConfig(t *testing.T) { }, }, }, - expectedCondition: invalidTLSCondition("tls.certificateAuthorityDataSource is invalid: failed to read from source"), - expectError: true, + expectedCondition: &metav1.Condition{ + Type: typeTLSConfigurationValid, + Status: metav1.ConditionFalse, + Reason: ReasonInvalidTLSConfig, + Message: "tls.certificateAuthorityDataSource is invalid: failed to get secret awesome-namespace/does-not-exist: secret \"does-not-exist\" not found", + }, }, { name: "should return invalid condition when failing to read ca bundle from kubernetes configMap that does not exist", - tlsSpec: &idpv1alpha1.TLSSpec{ - CertificateAuthorityDataSource: &idpv1alpha1.CABundleSource{ + tlsSpec: &TLSSpec{ + CertificateAuthorityDataSource: &caBundleSource{ Kind: "ConfigMap", Name: "does-not-exist", Key: "does-not-matter", @@ -176,13 +273,17 @@ func TestValidateTLSConfig(t *testing.T) { }, }, }, - expectedCondition: invalidTLSCondition("tls.certificateAuthorityDataSource is invalid: failed to read from source"), - expectError: true, + expectedCondition: &metav1.Condition{ + Type: typeTLSConfigurationValid, + Status: metav1.ConditionFalse, + Reason: ReasonInvalidTLSConfig, + Message: "tls.certificateAuthorityDataSource is invalid: failed to get configmap awesome-namespace/does-not-exist: configmap \"does-not-exist\" not found", + }, }, { name: "should return invalid condition when using an invalid certificate authority data source", - tlsSpec: &idpv1alpha1.TLSSpec{ - CertificateAuthorityDataSource: &idpv1alpha1.CABundleSource{ + tlsSpec: &TLSSpec{ + CertificateAuthorityDataSource: &caBundleSource{ Kind: "SomethingElse", Name: "does-not-exist", Key: "does-not-matter", @@ -200,8 +301,12 @@ func TestValidateTLSConfig(t *testing.T) { }, }, }, - expectedCondition: invalidTLSCondition("tls.certificateAuthorityDataSource is invalid: unsupported CA bundle source"), - expectError: true, + expectedCondition: &metav1.Condition{ + Type: typeTLSConfigurationValid, + Status: metav1.ConditionFalse, + Reason: ReasonInvalidTLSConfig, + Message: "tls.certificateAuthorityDataSource is invalid: unsupported CA bundle source kind: SomethingElse", + }, }, } @@ -231,15 +336,8 @@ func TestValidateTLSConfig(t *testing.T) { close(stopConfigMapInformer) // now the objects from kubernetes should be sync'd into the informer cache. } - actualCondition, _, _, err := ValidateTLSConfig(tt.tlsSpec, "tls", tt.namespace, secretsInformer, configMapInformer) - if tt.expectError { - require.Error(t, err) - } else { - require.NoError(t, err) - require.Equal(t, tt.expectedCondition.Type, actualCondition.Type) - require.Equal(t, tt.expectedCondition.Status, actualCondition.Status) - require.Equal(t, tt.expectedCondition.Reason, actualCondition.Reason) - } + actualCondition, _, _, _ := ValidateTLSConfig(tt.tlsSpec, "tls", tt.namespace, secretsInformer, configMapInformer) + require.Equal(t, tt.expectedCondition, actualCondition) }) } } @@ -430,3 +528,111 @@ func TestReadCABundleFromK8sConfigMap(t *testing.T) { }) } } + +func TestNewCommonTLSSpecForSupervisor(t *testing.T) { + testCA, err := certauthority.New("Test CA", 1*time.Hour) + require.NoError(t, err) + bundle := testCA.Bundle() + base64EncodedBundle := base64.StdEncoding.EncodeToString(bundle) + tests := []struct { + name string + supervisorTLSSpec *supervisorv1alpha1.TLSSpec + expected *TLSSpec + }{ + { + name: "should return nil spec when supervisorTLSSpec is nil", + supervisorTLSSpec: nil, + expected: nil, + }, + { + name: "should return tls spec with non-empty certificateAuthorityData", + supervisorTLSSpec: &supervisorv1alpha1.TLSSpec{ + CertificateAuthorityData: base64EncodedBundle, + CertificateAuthorityDataSource: nil, + }, + expected: &TLSSpec{ + CertificateAuthorityData: base64EncodedBundle, + CertificateAuthorityDataSource: nil, + }, + }, + { + name: "should return tls spec with certificateAuthorityDataSource", + supervisorTLSSpec: &supervisorv1alpha1.TLSSpec{ + CertificateAuthorityDataSource: &supervisorv1alpha1.CABundleSource{ + Kind: "Secret", + Name: "awesome-secret", + Key: "ca-bundle", + }, + }, + expected: &TLSSpec{ + CertificateAuthorityDataSource: &caBundleSource{ + Kind: "Secret", + Name: "awesome-secret", + Key: "ca-bundle", + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + actual := TLSSpecForSupervisor(tt.supervisorTLSSpec) + require.Equal(t, tt.expected, actual) + }) + } +} + +func TestNewCommonTlsSpecForConcierge(t *testing.T) { + testCA, err := certauthority.New("Test CA", 1*time.Hour) + require.NoError(t, err) + bundle := testCA.Bundle() + base64EncodedBundle := base64.StdEncoding.EncodeToString(bundle) + tests := []struct { + name string + conciergeTLSSpec *conciergev1alpha1.TLSSpec + expected *TLSSpec + }{ + { + name: "should return nil spec when supervisorTLSSpec is nil", + conciergeTLSSpec: nil, + expected: nil, + }, + { + name: "should return tls spec with non-empty certificateAuthorityData", + conciergeTLSSpec: &conciergev1alpha1.TLSSpec{ + CertificateAuthorityData: base64EncodedBundle, + CertificateAuthorityDataSource: nil, + }, + expected: &TLSSpec{ + CertificateAuthorityData: base64EncodedBundle, + CertificateAuthorityDataSource: nil, + }, + }, + { + name: "should return tls spec with certificateAuthorityDataSource", + conciergeTLSSpec: &conciergev1alpha1.TLSSpec{ + CertificateAuthorityDataSource: &conciergev1alpha1.CABundleSource{ + Kind: "Secret", + Name: "awesome-secret", + Key: "ca-bundle", + }, + }, + expected: &TLSSpec{ + CertificateAuthorityDataSource: &caBundleSource{ + Kind: "Secret", + Name: "awesome-secret", + Key: "ca-bundle", + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + actual := TlsSpecForConcierge(tt.conciergeTLSSpec) + require.Equal(t, tt.expected, actual) + }) + } +} From 3a969a83b728903e0123f6276b6e5d0f7c78fbbe Mon Sep 17 00:00:00 2001 From: Ashish Amarnath Date: Thu, 27 Jun 2024 15:38:40 -0700 Subject: [PATCH 06/99] update supervisor controllers Signed-off-by: Ashish Amarnath --- .../active_directory_upstream_watcher.go | 14 +- .../active_directory_upstream_watcher_test.go | 71 ++++++++- .../github_upstream_watcher.go | 57 +++---- .../github_upstream_watcher_test.go | 141 +++++++++++++----- .../ldap_upstream_watcher.go | 17 ++- .../ldap_upstream_watcher_test.go | 78 +++++++++- .../oidc_upstream_watcher.go | 99 ++++++++---- .../oidc_upstream_watcher_test.go | 88 ++++++++++- internal/controller/utils.go | 24 +++ internal/supervisor/server/server.go | 3 + 10 files changed, 481 insertions(+), 111 deletions(-) diff --git a/internal/controller/supervisorconfig/activedirectoryupstreamwatcher/active_directory_upstream_watcher.go b/internal/controller/supervisorconfig/activedirectoryupstreamwatcher/active_directory_upstream_watcher.go index b7813a9fb..1a4865eed 100644 --- a/internal/controller/supervisorconfig/activedirectoryupstreamwatcher/active_directory_upstream_watcher.go +++ b/internal/controller/supervisorconfig/activedirectoryupstreamwatcher/active_directory_upstream_watcher.go @@ -14,6 +14,7 @@ import ( "github.com/go-ldap/ldap/v3" "github.com/google/uuid" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/equality" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" @@ -290,7 +291,18 @@ func newInternal( ), withInformer( secretInformer, - pinnipedcontroller.MatchAnySecretOfTypeFilter(upstreamwatchers.LDAPBindAccountSecretType, pinnipedcontroller.SingletonQueue()), + pinnipedcontroller.MatchAnySecretOfTypesFilter( + []corev1.SecretType{ + upstreamwatchers.LDAPBindAccountSecretType, + corev1.SecretTypeOpaque, + corev1.SecretTypeTLS, + }, + pinnipedcontroller.SingletonQueue()), + controllerlib.InformerOption{}, + ), + withInformer( + configMapInformer, + pinnipedcontroller.MatchAnythingFilter(pinnipedcontroller.SingletonQueue()), controllerlib.InformerOption{}, ), ) diff --git a/internal/controller/supervisorconfig/activedirectoryupstreamwatcher/active_directory_upstream_watcher_test.go b/internal/controller/supervisorconfig/activedirectoryupstreamwatcher/active_directory_upstream_watcher_test.go index 78fd5380c..e6007c65e 100644 --- a/internal/controller/supervisorconfig/activedirectoryupstreamwatcher/active_directory_upstream_watcher_test.go +++ b/internal/controller/supervisorconfig/activedirectoryupstreamwatcher/active_directory_upstream_watcher_test.go @@ -47,7 +47,7 @@ func TestActiveDirectoryUpstreamWatcherControllerFilterSecrets(t *testing.T) { wantDelete bool }{ { - name: "a secret of the right type", + name: "should return true for a secret of type BasicAuth", secret: &corev1.Secret{ Type: corev1.SecretTypeBasicAuth, ObjectMeta: metav1.ObjectMeta{Name: "some-name", Namespace: "some-namespace"}, @@ -57,14 +57,34 @@ func TestActiveDirectoryUpstreamWatcherControllerFilterSecrets(t *testing.T) { wantDelete: true, }, { - name: "a secret of the wrong type", + name: "should return true for a secret of type TLS", + secret: &corev1.Secret{ + Type: corev1.SecretTypeTLS, + ObjectMeta: metav1.ObjectMeta{Name: "some-name", Namespace: "some-namespace"}, + }, + wantAdd: true, + wantUpdate: true, + wantDelete: true, + }, + { + name: "should return true for a secret of type Opaque", + secret: &corev1.Secret{ + Type: corev1.SecretTypeOpaque, + ObjectMeta: metav1.ObjectMeta{Name: "some-name", Namespace: "some-namespace"}, + }, + wantAdd: true, + wantUpdate: true, + wantDelete: true, + }, + { + name: "should return false for a secret of the wrong type", secret: &corev1.Secret{ Type: "this-is-the-wrong-type", ObjectMeta: metav1.ObjectMeta{Name: "some-name", Namespace: "some-namespace"}, }, }, { - name: "resource of a data type which is not watched by this controller", + name: "should return false for a resource of a data type which is not watched by this controller", secret: &corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{Name: "some-name", Namespace: "some-namespace"}, }, @@ -95,6 +115,51 @@ func TestActiveDirectoryUpstreamWatcherControllerFilterSecrets(t *testing.T) { } } +func TestActiveDirectoryUpstreamWatcherControllerFilterConfigMaps(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + cm metav1.Object + wantAdd bool + wantUpdate bool + wantDelete bool + }{ + { + name: "any configmap", + cm: &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{Name: "some-name", Namespace: "some-namespace"}, + }, + wantAdd: true, + wantUpdate: true, + wantDelete: true, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + t.Parallel() + + fakePinnipedClient := supervisorfake.NewSimpleClientset() + pinnipedInformers := supervisorinformers.NewSharedInformerFactory(fakePinnipedClient, 0) + activeDirectoryIDPInformer := pinnipedInformers.IDP().V1alpha1().ActiveDirectoryIdentityProviders() + fakeKubeClient := fake.NewSimpleClientset() + kubeInformers := informers.NewSharedInformerFactory(fakeKubeClient, 0) + secretInformer := kubeInformers.Core().V1().Secrets() + configMapInformer := kubeInformers.Core().V1().ConfigMaps() + withInformer := testutil.NewObservableWithInformerOption() + + New(nil, nil, activeDirectoryIDPInformer, secretInformer, configMapInformer, withInformer.WithInformer) + + unrelated := corev1.Secret{} + filter := withInformer.GetFilterForInformer(configMapInformer) + require.Equal(t, test.wantAdd, filter.Add(test.cm)) + require.Equal(t, test.wantUpdate, filter.Update(&unrelated, test.cm)) + require.Equal(t, test.wantUpdate, filter.Update(test.cm, &unrelated)) + require.Equal(t, test.wantDelete, filter.Delete(test.cm)) + }) + } +} + func TestActiveDirectoryUpstreamWatcherControllerFilterActiveDirectoryIdentityProviders(t *testing.T) { t.Parallel() diff --git a/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher.go b/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher.go index c9580d98b..48e812d99 100644 --- a/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher.go +++ b/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher.go @@ -31,6 +31,7 @@ import ( pinnipedcontroller "go.pinniped.dev/internal/controller" "go.pinniped.dev/internal/controller/conditionsutil" "go.pinniped.dev/internal/controller/supervisorconfig/upstreamwatchers" + "go.pinniped.dev/internal/controller/tlsconfigutil" "go.pinniped.dev/internal/controllerlib" "go.pinniped.dev/internal/crypto/ptls" "go.pinniped.dev/internal/endpointaddr" @@ -73,6 +74,7 @@ type gitHubWatcherController struct { client supervisorclientset.Interface gitHubIdentityProviderInformer idpinformers.GitHubIdentityProviderInformer secretInformer corev1informers.SecretInformer + configMapInformer corev1informers.ConfigMapInformer clock clock.Clock dialFunc func(network, addr string, config *tls.Config) (*tls.Conn, error) } @@ -84,6 +86,7 @@ func New( client supervisorclientset.Interface, gitHubIdentityProviderInformer idpinformers.GitHubIdentityProviderInformer, secretInformer corev1informers.SecretInformer, + configMapInformer corev1informers.ConfigMapInformer, log plog.Logger, withInformer pinnipedcontroller.WithInformerOptionFunc, clock clock.Clock, @@ -96,6 +99,7 @@ func New( log: log.WithName(controllerName), gitHubIdentityProviderInformer: gitHubIdentityProviderInformer, secretInformer: secretInformer, + configMapInformer: configMapInformer, clock: clock, dialFunc: dialFunc, } @@ -104,15 +108,24 @@ func New( controllerlib.Config{Name: controllerName, Syncer: &c}, withInformer( gitHubIdentityProviderInformer, - pinnipedcontroller.SimpleFilter(func(obj metav1.Object) bool { - gitHubIDP, ok := obj.(*idpv1alpha1.GitHubIdentityProvider) - return ok && gitHubIDP.Namespace == namespace - }, pinnipedcontroller.SingletonQueue()), + pinnipedcontroller.MatchAnythingFilter(pinnipedcontroller.SingletonQueue()), controllerlib.InformerOption{}, ), withInformer( secretInformer, - pinnipedcontroller.MatchAnySecretOfTypeFilter(gitHubClientSecretType, pinnipedcontroller.SingletonQueue(), namespace), + pinnipedcontroller.MatchAnySecretOfTypesFilter( + []corev1.SecretType{ + gitHubClientSecretType, + corev1.SecretTypeOpaque, + corev1.SecretTypeTLS, + }, + pinnipedcontroller.SingletonQueue(), + ), + controllerlib.InformerOption{}, + ), + withInformer( + configMapInformer, + pinnipedcontroller.MatchAnythingFilter(pinnipedcontroller.SingletonQueue()), controllerlib.InformerOption{}, ), ) @@ -201,7 +214,7 @@ func (c *gitHubWatcherController) validateClientSecret(secretName string) (*meta return &metav1.Condition{ Type: ClientCredentialsSecretValid, Status: metav1.ConditionTrue, - Reason: upstreamwatchers.ReasonSuccess, + Reason: conditionsutil.ReasonSuccess, Message: fmt.Sprintf("clientID and clientSecret have been read from spec.client.SecretName (%q)", secretName), }, clientID, clientSecret, nil } @@ -219,7 +232,7 @@ func validateOrganizationsPolicy(organizationsSpec *idpv1alpha1.GitHubOrganizati return &metav1.Condition{ Type: OrganizationsPolicyValid, Status: metav1.ConditionTrue, - Reason: upstreamwatchers.ReasonSuccess, + Reason: conditionsutil.ReasonSuccess, Message: fmt.Sprintf("spec.allowAuthentication.organizations.policy (%q) is valid", policy), } } @@ -354,30 +367,20 @@ func validateHost(gitHubAPIConfig idpv1alpha1.GitHubAPIConfig) (*metav1.Conditio return &metav1.Condition{ Type: HostValid, Status: metav1.ConditionTrue, - Reason: upstreamwatchers.ReasonSuccess, + Reason: conditionsutil.ReasonSuccess, Message: fmt.Sprintf("spec.githubAPI.host (%q) is valid", host), }, &hostPort } func (c *gitHubWatcherController) validateTLSConfiguration(tlsSpec *idpv1alpha1.TLSSpec) (*metav1.Condition, *x509.CertPool) { - certPool, _, buildCertPoolErr := pinnipedcontroller.BuildCertPoolIDP(tlsSpec) - if buildCertPoolErr != nil { - // buildCertPoolErr is not recoverable with a resync. - // It requires user interaction, so do not return the error. - return &metav1.Condition{ - Type: TLSConfigurationValid, - Status: metav1.ConditionFalse, - Reason: "InvalidTLSConfig", - Message: fmt.Sprintf("spec.githubAPI.tls.certificateAuthorityData is not valid: %s", buildCertPoolErr), - }, nil - } + tlsCondition, _, certPool, _ := tlsconfigutil.ValidateTLSConfig( + tlsconfigutil.TLSSpecForSupervisor(tlsSpec), + "spec.githubAPI.tls", + c.namespace, + c.secretInformer, + c.configMapInformer) - return &metav1.Condition{ - Type: TLSConfigurationValid, - Status: metav1.ConditionTrue, - Reason: upstreamwatchers.ReasonSuccess, - Message: "spec.githubAPI.tls.certificateAuthorityData is valid", - }, certPool + return tlsCondition, certPool } func (c *gitHubWatcherController) validateGitHubConnection( @@ -407,7 +410,7 @@ func (c *gitHubWatcherController) validateGitHubConnection( return &metav1.Condition{ Type: GitHubConnectionValid, Status: metav1.ConditionTrue, - Reason: upstreamwatchers.ReasonSuccess, + Reason: conditionsutil.ReasonSuccess, Message: fmt.Sprintf("spec.githubAPI.host (%q) is reachable and TLS verification succeeds", hostPort.Endpoint()), }, fmt.Sprintf("https://%s", hostPort.Endpoint()), phttp.Default(certPool), conn.Close() } @@ -471,7 +474,7 @@ func validateUserAndGroupAttributes(upstream *idpv1alpha1.GitHubIdentityProvider return &metav1.Condition{ Type: ClaimsValid, Status: metav1.ConditionTrue, - Reason: upstreamwatchers.ReasonSuccess, + Reason: conditionsutil.ReasonSuccess, Message: "spec.claims are valid", }, groupNameAttribute, usernameAttribute } diff --git a/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher_test.go b/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher_test.go index 5b9df963f..68266001c 100644 --- a/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher_test.go +++ b/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher_test.go @@ -35,6 +35,7 @@ import ( supervisorinformers "go.pinniped.dev/generated/latest/client/supervisor/informers/externalversions" "go.pinniped.dev/internal/certauthority" pinnipedcontroller "go.pinniped.dev/internal/controller" + "go.pinniped.dev/internal/controller/conditionsutil" "go.pinniped.dev/internal/controller/supervisorconfig/upstreamwatchers" "go.pinniped.dev/internal/controllerlib" "go.pinniped.dev/internal/federationdomain/dynamicupstreamprovider" @@ -167,7 +168,7 @@ func TestController(t *testing.T) { Status: metav1.ConditionTrue, ObservedGeneration: wantObservedGeneration, LastTransitionTime: wantLastTransitionTime, - Reason: upstreamwatchers.ReasonSuccess, + Reason: conditionsutil.ReasonSuccess, Message: fmt.Sprintf("spec.githubAPI.host (%q) is valid", host), } } @@ -193,7 +194,7 @@ func TestController(t *testing.T) { Status: metav1.ConditionTrue, ObservedGeneration: wantObservedGeneration, LastTransitionTime: wantLastTransitionTime, - Reason: upstreamwatchers.ReasonSuccess, + Reason: conditionsutil.ReasonSuccess, Message: "spec.githubAPI.tls.certificateAuthorityData is valid", } } @@ -219,7 +220,7 @@ func TestController(t *testing.T) { Status: metav1.ConditionTrue, ObservedGeneration: wantObservedGeneration, LastTransitionTime: wantLastTransitionTime, - Reason: upstreamwatchers.ReasonSuccess, + Reason: conditionsutil.ReasonSuccess, Message: fmt.Sprintf("spec.allowAuthentication.organizations.policy (%q) is valid", policy), } } @@ -245,7 +246,7 @@ func TestController(t *testing.T) { Status: metav1.ConditionTrue, ObservedGeneration: wantObservedGeneration, LastTransitionTime: wantLastTransitionTime, - Reason: upstreamwatchers.ReasonSuccess, + Reason: conditionsutil.ReasonSuccess, Message: fmt.Sprintf("clientID and clientSecret have been read from spec.client.SecretName (%q)", secretName), } } @@ -276,7 +277,7 @@ func TestController(t *testing.T) { Status: metav1.ConditionTrue, ObservedGeneration: wantObservedGeneration, LastTransitionTime: wantLastTransitionTime, - Reason: upstreamwatchers.ReasonSuccess, + Reason: conditionsutil.ReasonSuccess, Message: "spec.claims are valid", } } @@ -302,7 +303,7 @@ func TestController(t *testing.T) { Status: metav1.ConditionTrue, ObservedGeneration: wantObservedGeneration, LastTransitionTime: wantLastTransitionTime, - Reason: upstreamwatchers.ReasonSuccess, + Reason: conditionsutil.ReasonSuccess, Message: fmt.Sprintf("spec.githubAPI.host (%q) is reachable and TLS verification succeeds", host), } } @@ -1853,6 +1854,7 @@ func TestController(t *testing.T) { fakeSupervisorClient, gitHubIdentityProviderInformer, kubeInformers.Core().V1().Secrets(), + kubeInformers.Core().V1().ConfigMaps(), logger, controllerlib.WithInformer, frozenClockForLastTransitionTime, @@ -2058,7 +2060,7 @@ func TestController_OnlyWantActions(t *testing.T) { Status: metav1.ConditionTrue, ObservedGeneration: 333, LastTransitionTime: oneHourAgo, - Reason: upstreamwatchers.ReasonSuccess, + Reason: conditionsutil.ReasonSuccess, Message: fmt.Sprintf("spec.githubAPI.host (%q) is reachable and TLS verification succeeds", goodServerDomain), }, { @@ -2066,7 +2068,7 @@ func TestController_OnlyWantActions(t *testing.T) { Status: metav1.ConditionTrue, ObservedGeneration: 333, LastTransitionTime: oneHourAgo, - Reason: upstreamwatchers.ReasonSuccess, + Reason: conditionsutil.ReasonSuccess, Message: fmt.Sprintf("spec.githubAPI.host (%q) is valid", goodServerDomain), }, { @@ -2074,7 +2076,7 @@ func TestController_OnlyWantActions(t *testing.T) { Status: metav1.ConditionTrue, ObservedGeneration: 333, LastTransitionTime: oneHourAgo, - Reason: upstreamwatchers.ReasonSuccess, + Reason: conditionsutil.ReasonSuccess, Message: `spec.allowAuthentication.organizations.policy ("AllGitHubUsers") is valid`, }, { @@ -2082,7 +2084,7 @@ func TestController_OnlyWantActions(t *testing.T) { Status: metav1.ConditionTrue, ObservedGeneration: 333, LastTransitionTime: oneHourAgo, - Reason: upstreamwatchers.ReasonSuccess, + Reason: conditionsutil.ReasonSuccess, Message: "spec.githubAPI.tls.certificateAuthorityData is valid", }, }, @@ -2154,7 +2156,7 @@ func TestController_OnlyWantActions(t *testing.T) { Status: metav1.ConditionTrue, ObservedGeneration: 1234, LastTransitionTime: wantLastTransitionTime, - Reason: upstreamwatchers.ReasonSuccess, + Reason: conditionsutil.ReasonSuccess, Message: "spec.claims are valid", }, { @@ -2162,7 +2164,7 @@ func TestController_OnlyWantActions(t *testing.T) { Status: metav1.ConditionTrue, ObservedGeneration: 1234, LastTransitionTime: wantLastTransitionTime, - Reason: upstreamwatchers.ReasonSuccess, + Reason: conditionsutil.ReasonSuccess, Message: `clientID and clientSecret have been read from spec.client.SecretName ("some-secret-name")`, }, { @@ -2170,7 +2172,7 @@ func TestController_OnlyWantActions(t *testing.T) { Status: metav1.ConditionTrue, ObservedGeneration: 1234, LastTransitionTime: wantLastTransitionTime, - Reason: upstreamwatchers.ReasonSuccess, + Reason: conditionsutil.ReasonSuccess, Message: fmt.Sprintf("spec.githubAPI.host (%q) is reachable and TLS verification succeeds", goodServerDomain), }, { @@ -2178,7 +2180,7 @@ func TestController_OnlyWantActions(t *testing.T) { Status: metav1.ConditionTrue, ObservedGeneration: 1234, LastTransitionTime: wantLastTransitionTime, - Reason: upstreamwatchers.ReasonSuccess, + Reason: conditionsutil.ReasonSuccess, Message: fmt.Sprintf("spec.githubAPI.host (%q) is valid", goodServerDomain), }, { @@ -2186,7 +2188,7 @@ func TestController_OnlyWantActions(t *testing.T) { Status: metav1.ConditionTrue, ObservedGeneration: 1234, LastTransitionTime: wantLastTransitionTime, - Reason: upstreamwatchers.ReasonSuccess, + Reason: conditionsutil.ReasonSuccess, Message: `spec.allowAuthentication.organizations.policy ("AllGitHubUsers") is valid`, }, { @@ -2194,7 +2196,7 @@ func TestController_OnlyWantActions(t *testing.T) { Status: metav1.ConditionTrue, ObservedGeneration: 1234, LastTransitionTime: wantLastTransitionTime, - Reason: upstreamwatchers.ReasonSuccess, + Reason: conditionsutil.ReasonSuccess, Message: "spec.githubAPI.tls.certificateAuthorityData is valid", }, }, @@ -2227,6 +2229,7 @@ func TestController_OnlyWantActions(t *testing.T) { fakeSupervisorClient, supervisorInformers.IDP().V1alpha1().GitHubIdentityProviders(), kubeInformers.Core().V1().Secrets(), + kubeInformers.Core().V1().ConfigMaps(), logger, controllerlib.WithInformer, frozenClockForLastTransitionTime, @@ -2273,12 +2276,10 @@ func compareTLSClientConfigWithinHttpClients(t *testing.T, expected *http.Client } func TestGitHubUpstreamWatcherControllerFilterSecret(t *testing.T) { - namespace := "some-namespace" goodSecret := &corev1.Secret{ Type: "secrets.pinniped.dev/github-client", ObjectMeta: metav1.ObjectMeta{ - Name: "some-name", - Namespace: namespace, + Name: "some-name", }, } @@ -2290,22 +2291,36 @@ func TestGitHubUpstreamWatcherControllerFilterSecret(t *testing.T) { wantDelete bool }{ { - name: "a secret of the right type", + name: "should return true for a secret of the type secrets.pinniped.dev/github-client", secret: goodSecret, wantAdd: true, wantUpdate: true, wantDelete: true, }, { - name: "a secret of the right type, but in the wrong namespace", + name: "should return false for a secret of the type Opaque", secret: func() *corev1.Secret { otherSecret := goodSecret.DeepCopy() - otherSecret.Namespace = "other-namespace" + otherSecret.Type = corev1.SecretTypeOpaque return otherSecret }(), + wantAdd: true, + wantUpdate: true, + wantDelete: true, }, { - name: "a secret of the wrong type", + name: "should return false for a secret of the type TLS", + secret: func() *corev1.Secret { + otherSecret := goodSecret.DeepCopy() + otherSecret.Type = corev1.SecretTypeTLS + return otherSecret + }(), + wantAdd: true, + wantUpdate: true, + wantDelete: true, + }, + { + name: "should return false for a secret of the wrong type", secret: func() *corev1.Secret { otherSecret := goodSecret.DeepCopy() otherSecret.Type = "other-type" @@ -2313,7 +2328,7 @@ func TestGitHubUpstreamWatcherControllerFilterSecret(t *testing.T) { }(), }, { - name: "resource of wrong data type", + name: "should return false for a resource of wrong data type", secret: &corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{Name: "some-name", Namespace: "some-namespace"}, }, @@ -2329,14 +2344,16 @@ func TestGitHubUpstreamWatcherControllerFilterSecret(t *testing.T) { logger := plog.TestLogger(t, &log) secretInformer := kubeInformers.Core().V1().Secrets() + configMapInformer := kubeInformers.Core().V1().ConfigMaps() observableInformers := testutil.NewObservableWithInformerOption() _ = New( - namespace, + "some-namespace", dynamicupstreamprovider.NewDynamicUpstreamIDPProvider(), supervisorfake.NewSimpleClientset(), supervisorinformers.NewSharedInformerFactory(supervisorfake.NewSimpleClientset(), 0).IDP().V1alpha1().GitHubIdentityProviders(), secretInformer, + configMapInformer, logger, observableInformers.WithInformer, clock.RealClock{}, @@ -2353,6 +2370,65 @@ func TestGitHubUpstreamWatcherControllerFilterSecret(t *testing.T) { } } +// TODO: make this test pass +func TestGitHubUpstreamWatcherControllerFilterConfigMaps(t *testing.T) { + namespace := "some-namespace" + goodCM := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + }, + } + + tests := []struct { + name string + cm metav1.Object + wantAdd bool + wantUpdate bool + wantDelete bool + }{ + { + name: "a configMap in the right namespace", + cm: goodCM, + wantAdd: true, + wantUpdate: true, + wantDelete: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + var log bytes.Buffer + logger := plog.TestLogger(t, &log) + + gitHubIdentityProviderInformer := supervisorinformers.NewSharedInformerFactory(supervisorfake.NewSimpleClientset(), 0).IDP().V1alpha1().GitHubIdentityProviders() + observableInformers := testutil.NewObservableWithInformerOption() + + configMapInformer := k8sinformers.NewSharedInformerFactoryWithOptions(kubernetesfake.NewSimpleClientset(), 0).Core().V1().ConfigMaps() + + _ = New( + namespace, + dynamicupstreamprovider.NewDynamicUpstreamIDPProvider(), + supervisorfake.NewSimpleClientset(), + gitHubIdentityProviderInformer, + k8sinformers.NewSharedInformerFactoryWithOptions(kubernetesfake.NewSimpleClientset(), 0).Core().V1().Secrets(), + configMapInformer, + logger, + observableInformers.WithInformer, + clock.RealClock{}, + tls.Dial, + ) + + unrelated := &corev1.ConfigMap{} + filter := observableInformers.GetFilterForInformer(configMapInformer) + require.Equal(t, tt.wantAdd, filter.Add(tt.cm)) + require.Equal(t, tt.wantUpdate, filter.Update(unrelated, tt.cm)) + require.Equal(t, tt.wantUpdate, filter.Update(tt.cm, unrelated)) + require.Equal(t, tt.wantDelete, filter.Delete(tt.cm)) + }) + } +} + func TestGitHubUpstreamWatcherControllerFilterGitHubIDP(t *testing.T) { namespace := "some-namespace" goodIDP := &idpv1alpha1.GitHubIdentityProvider{ @@ -2375,20 +2451,6 @@ func TestGitHubUpstreamWatcherControllerFilterGitHubIDP(t *testing.T) { wantUpdate: true, wantDelete: true, }, - { - name: "IDP in the wrong namespace", - idp: func() metav1.Object { - badIDP := goodIDP.DeepCopy() - badIDP.Namespace = "other-namespace" - return badIDP - }(), - }, - { - name: "resource of wrong data type", - idp: &corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{Name: "some-name"}, - }, - }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -2406,6 +2468,7 @@ func TestGitHubUpstreamWatcherControllerFilterGitHubIDP(t *testing.T) { supervisorfake.NewSimpleClientset(), gitHubIdentityProviderInformer, k8sinformers.NewSharedInformerFactoryWithOptions(kubernetesfake.NewSimpleClientset(), 0).Core().V1().Secrets(), + k8sinformers.NewSharedInformerFactoryWithOptions(kubernetesfake.NewSimpleClientset(), 0).Core().V1().ConfigMaps(), logger, observableInformers.WithInformer, clock.RealClock{}, diff --git a/internal/controller/supervisorconfig/ldapupstreamwatcher/ldap_upstream_watcher.go b/internal/controller/supervisorconfig/ldapupstreamwatcher/ldap_upstream_watcher.go index bae4dc8b3..02476cbfb 100644 --- a/internal/controller/supervisorconfig/ldapupstreamwatcher/ldap_upstream_watcher.go +++ b/internal/controller/supervisorconfig/ldapupstreamwatcher/ldap_upstream_watcher.go @@ -7,6 +7,7 @@ package ldapupstreamwatcher import ( "context" "fmt" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/equality" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -152,6 +153,7 @@ func New( client supervisorclientset.Interface, ldapIdentityProviderInformer idpinformers.LDAPIdentityProviderInformer, secretInformer corev1informers.SecretInformer, + configMapInformer corev1informers.ConfigMapInformer, withInformer pinnipedcontroller.WithInformerOptionFunc, ) controllerlib.Controller { return newInternal( @@ -163,6 +165,7 @@ func New( client, ldapIdentityProviderInformer, secretInformer, + configMapInformer, withInformer, ) } @@ -175,6 +178,7 @@ func newInternal( client supervisorclientset.Interface, ldapIdentityProviderInformer idpinformers.LDAPIdentityProviderInformer, secretInformer corev1informers.SecretInformer, + configMapInformer corev1informers.ConfigMapInformer, withInformer pinnipedcontroller.WithInformerOptionFunc, ) controllerlib.Controller { c := ldapWatcherController{ @@ -184,6 +188,7 @@ func newInternal( client: client, ldapIdentityProviderInformer: ldapIdentityProviderInformer, secretInformer: secretInformer, + configMapInformer: configMapInformer, } return controllerlib.New( controllerlib.Config{Name: ldapControllerName, Syncer: &c}, @@ -194,7 +199,17 @@ func newInternal( ), withInformer( secretInformer, - pinnipedcontroller.MatchAnySecretOfTypeFilter(upstreamwatchers.LDAPBindAccountSecretType, pinnipedcontroller.SingletonQueue()), + pinnipedcontroller.MatchAnySecretOfTypesFilter( + []corev1.SecretType{ + upstreamwatchers.LDAPBindAccountSecretType, + corev1.SecretTypeOpaque, + corev1.SecretTypeTLS, + }, pinnipedcontroller.SingletonQueue()), + controllerlib.InformerOption{}, + ), + withInformer( + configMapInformer, + pinnipedcontroller.MatchAnythingFilter(pinnipedcontroller.SingletonQueue()), controllerlib.InformerOption{}, ), ) diff --git a/internal/controller/supervisorconfig/ldapupstreamwatcher/ldap_upstream_watcher_test.go b/internal/controller/supervisorconfig/ldapupstreamwatcher/ldap_upstream_watcher_test.go index c5f313d4a..05dfdaf62 100644 --- a/internal/controller/supervisorconfig/ldapupstreamwatcher/ldap_upstream_watcher_test.go +++ b/internal/controller/supervisorconfig/ldapupstreamwatcher/ldap_upstream_watcher_test.go @@ -46,7 +46,7 @@ func TestLDAPUpstreamWatcherControllerFilterSecrets(t *testing.T) { wantDelete bool }{ { - name: "a secret of the right type", + name: "should return true for a secret of type BasicAuth", secret: &corev1.Secret{ Type: corev1.SecretTypeBasicAuth, ObjectMeta: metav1.ObjectMeta{Name: "some-name", Namespace: "some-namespace"}, @@ -56,14 +56,34 @@ func TestLDAPUpstreamWatcherControllerFilterSecrets(t *testing.T) { wantDelete: true, }, { - name: "a secret of the wrong type", + name: "should return true for a secret of type Opaque", + secret: &corev1.Secret{ + Type: corev1.SecretTypeOpaque, + ObjectMeta: metav1.ObjectMeta{Name: "some-name", Namespace: "some-namespace"}, + }, + wantAdd: true, + wantUpdate: true, + wantDelete: true, + }, + { + name: "should return true for a secret of type TLS", + secret: &corev1.Secret{ + Type: corev1.SecretTypeTLS, + ObjectMeta: metav1.ObjectMeta{Name: "some-name", Namespace: "some-namespace"}, + }, + wantAdd: true, + wantUpdate: true, + wantDelete: true, + }, + { + name: "should return false for a secret of the wrong type", secret: &corev1.Secret{ Type: "this-is-the-wrong-type", ObjectMeta: metav1.ObjectMeta{Name: "some-name", Namespace: "some-namespace"}, }, }, { - name: "resource of a data type which is not watched by this controller", + name: "should return false for a resource of a data type which is not watched by this controller", secret: &corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{Name: "some-name", Namespace: "some-namespace"}, }, @@ -79,9 +99,10 @@ func TestLDAPUpstreamWatcherControllerFilterSecrets(t *testing.T) { fakeKubeClient := fake.NewSimpleClientset() kubeInformers := informers.NewSharedInformerFactory(fakeKubeClient, 0) secretInformer := kubeInformers.Core().V1().Secrets() + configMapInformer := kubeInformers.Core().V1().ConfigMaps() withInformer := testutil.NewObservableWithInformerOption() - New(nil, nil, ldapIDPInformer, secretInformer, withInformer.WithInformer) + New(nil, nil, ldapIDPInformer, secretInformer, configMapInformer, withInformer.WithInformer) unrelated := corev1.Secret{} filter := withInformer.GetFilterForInformer(secretInformer) @@ -93,6 +114,51 @@ func TestLDAPUpstreamWatcherControllerFilterSecrets(t *testing.T) { } } +func TestLDAPUpstreamWatcherControllerFilterConfigMaps(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + cm metav1.Object + wantAdd bool + wantUpdate bool + wantDelete bool + }{ + { + name: "any configmap", + cm: &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{Name: "some-name", Namespace: "some-namespace"}, + }, + wantAdd: true, + wantUpdate: true, + wantDelete: true, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + t.Parallel() + + fakePinnipedClient := supervisorfake.NewSimpleClientset() + pinnipedInformers := supervisorinformers.NewSharedInformerFactory(fakePinnipedClient, 0) + ldapIDPInformer := pinnipedInformers.IDP().V1alpha1().LDAPIdentityProviders() + fakeKubeClient := fake.NewSimpleClientset() + kubeInformers := informers.NewSharedInformerFactory(fakeKubeClient, 0) + secretInformer := kubeInformers.Core().V1().Secrets() + configMapInformer := kubeInformers.Core().V1().ConfigMaps() + withInformer := testutil.NewObservableWithInformerOption() + + New(nil, nil, ldapIDPInformer, secretInformer, configMapInformer, withInformer.WithInformer) + + unrelated := corev1.ConfigMap{} + filter := withInformer.GetFilterForInformer(configMapInformer) + require.Equal(t, test.wantAdd, filter.Add(test.cm)) + require.Equal(t, test.wantUpdate, filter.Update(&unrelated, test.cm)) + require.Equal(t, test.wantUpdate, filter.Update(test.cm, &unrelated)) + require.Equal(t, test.wantDelete, filter.Delete(test.cm)) + }) + } +} + func TestLDAPUpstreamWatcherControllerFilterLDAPIdentityProviders(t *testing.T) { t.Parallel() @@ -123,9 +189,10 @@ func TestLDAPUpstreamWatcherControllerFilterLDAPIdentityProviders(t *testing.T) fakeKubeClient := fake.NewSimpleClientset() kubeInformers := informers.NewSharedInformerFactory(fakeKubeClient, 0) secretInformer := kubeInformers.Core().V1().Secrets() + configMapInformer := kubeInformers.Core().V1().ConfigMaps() withInformer := testutil.NewObservableWithInformerOption() - New(nil, nil, ldapIDPInformer, secretInformer, withInformer.WithInformer) + New(nil, nil, ldapIDPInformer, secretInformer, configMapInformer, withInformer.WithInformer) unrelated := corev1.Secret{} filter := withInformer.GetFilterForInformer(ldapIDPInformer) @@ -1177,6 +1244,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { fakePinnipedClient, pinnipedInformers.IDP().V1alpha1().LDAPIdentityProviders(), kubeInformers.Core().V1().Secrets(), + kubeInformers.Core().V1().ConfigMaps(), controllerlib.WithInformer, ) diff --git a/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher.go b/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher.go index cda75c49f..c711257b5 100644 --- a/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher.go +++ b/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher.go @@ -32,6 +32,7 @@ import ( pinnipedcontroller "go.pinniped.dev/internal/controller" "go.pinniped.dev/internal/controller/conditionsutil" "go.pinniped.dev/internal/controller/supervisorconfig/upstreamwatchers" + "go.pinniped.dev/internal/controller/tlsconfigutil" "go.pinniped.dev/internal/controllerlib" "go.pinniped.dev/internal/federationdomain/upstreamprovider" "go.pinniped.dev/internal/net/phttp" @@ -128,6 +129,7 @@ type oidcWatcherController struct { client supervisorclientset.Interface oidcIdentityProviderInformer idpinformers.OIDCIdentityProviderInformer secretInformer corev1informers.SecretInformer + configMapInformer corev1informers.ConfigMapInformer validatorCache interface { getProvider(*idpv1alpha1.OIDCIdentityProviderSpec) (*coreosoidc.Provider, *http.Client) putProvider(*idpv1alpha1.OIDCIdentityProviderSpec, *coreosoidc.Provider, *http.Client) @@ -140,6 +142,7 @@ func New( client supervisorclientset.Interface, oidcIdentityProviderInformer idpinformers.OIDCIdentityProviderInformer, secretInformer corev1informers.SecretInformer, + configMapInformer corev1informers.ConfigMapInformer, log plog.Logger, withInformer pinnipedcontroller.WithInformerOptionFunc, ) controllerlib.Controller { @@ -149,6 +152,7 @@ func New( client: client, oidcIdentityProviderInformer: oidcIdentityProviderInformer, secretInformer: secretInformer, + configMapInformer: configMapInformer, validatorCache: &lruValidatorCache{cache: cache.NewExpiring()}, } return controllerlib.New( @@ -160,7 +164,18 @@ func New( ), withInformer( secretInformer, - pinnipedcontroller.MatchAnySecretOfTypeFilter(oidcClientSecretType, pinnipedcontroller.SingletonQueue()), + pinnipedcontroller.MatchAnySecretOfTypesFilter( + []corev1.SecretType{ + oidcClientSecretType, + corev1.SecretTypeOpaque, + corev1.SecretTypeTLS, + }, + pinnipedcontroller.SingletonQueue()), + controllerlib.InformerOption{}, + ), + withInformer( + configMapInformer, + pinnipedcontroller.MatchAnythingFilter(pinnipedcontroller.SingletonQueue()), controllerlib.InformerOption{}, ), ) @@ -220,8 +235,9 @@ func (c *oidcWatcherController) validateUpstream(ctx controllerlib.Context, upst conditions := []*metav1.Condition{ c.validateSecret(upstream, &result), - c.validateIssuer(ctx.Context, upstream, &result), } + conditions = append(conditions, c.validateIssuer(ctx.Context, upstream, &result)...) + if len(rejectedAuthcodeAuthorizeParameters) > 0 { conditions = append(conditions, &metav1.Condition{ Type: typeAdditionalAuthorizeParametersValid, @@ -234,7 +250,7 @@ func (c *oidcWatcherController) validateUpstream(ctx controllerlib.Context, upst conditions = append(conditions, &metav1.Condition{ Type: typeAdditionalAuthorizeParametersValid, Status: metav1.ConditionTrue, - Reason: upstreamwatchers.ReasonSuccess, + Reason: conditionsutil.ReasonSuccess, Message: allParamNamesAllowedMsg, }) } @@ -302,32 +318,42 @@ func (c *oidcWatcherController) validateSecret(upstream *idpv1alpha1.OIDCIdentit return &metav1.Condition{ Type: typeClientCredentialsSecretValid, Status: metav1.ConditionTrue, - Reason: upstreamwatchers.ReasonSuccess, + Reason: conditionsutil.ReasonSuccess, Message: "loaded client credentials", } } // validateIssuer validates the .spec.issuer field, performs OIDC discovery, and returns the appropriate OIDCDiscoverySucceeded condition. -func (c *oidcWatcherController) validateIssuer(ctx context.Context, upstream *idpv1alpha1.OIDCIdentityProvider, result *upstreamoidc.ProviderConfig) *metav1.Condition { +func (c *oidcWatcherController) validateIssuer(ctx context.Context, upstream *idpv1alpha1.OIDCIdentityProvider, result *upstreamoidc.ProviderConfig) []*metav1.Condition { // Get the provider and HTTP Client from cache if possible. discoveredProvider, httpClient := c.validatorCache.getProvider(&upstream.Spec) + tlsCondition, _, certPool, _ := tlsconfigutil.ValidateTLSConfig( + tlsconfigutil.TLSSpecForSupervisor(upstream.Spec.TLS), + "oidcIdentityProvider.spec.tls", + upstream.Namespace, + c.secretInformer, + c.configMapInformer) // If the provider does not exist in the cache, do a fresh discovery lookup and save to the cache. if discoveredProvider == nil { var err error - httpClient, err = getClient(upstream) - if err != nil { - return &metav1.Condition{ - Type: typeOIDCDiscoverySucceeded, - Status: metav1.ConditionFalse, - Reason: upstreamwatchers.ReasonInvalidTLSConfig, - Message: err.Error(), + if tlsCondition.Reason != conditionsutil.ReasonSuccess { + return []*metav1.Condition{ + { + Type: typeOIDCDiscoverySucceeded, + Status: metav1.ConditionFalse, + Reason: tlsconfigutil.ReasonInvalidTLSConfig, + Message: tlsCondition.Message, + }, + tlsCondition, } } + httpClient = defaultClientShortTimeout(certPool) + _, issuerURLCondition := validateHTTPSURL(upstream.Spec.Issuer, "issuer", reasonUnreachable) if issuerURLCondition != nil { - return issuerURLCondition + return []*metav1.Condition{issuerURLCondition, tlsCondition} } discoveredProvider, err = coreosoidc.NewProvider(coreosoidc.ClientContext(ctx, httpClient), upstream.Spec.Issuer) @@ -337,11 +363,14 @@ func (c *oidcWatcherController) validateIssuer(ctx context.Context, upstream *id "name", upstream.Name, "issuer", upstream.Spec.Issuer, ).Error("failed to perform OIDC discovery", err) - return &metav1.Condition{ - Type: typeOIDCDiscoverySucceeded, - Status: metav1.ConditionFalse, - Reason: reasonUnreachable, - Message: fmt.Sprintf("failed to perform OIDC discovery against %q:\n%s", upstream.Spec.Issuer, pinnipedcontroller.TruncateMostLongErr(err)), + return []*metav1.Condition{ + { + Type: typeOIDCDiscoverySucceeded, + Status: metav1.ConditionFalse, + Reason: reasonUnreachable, + Message: fmt.Sprintf("failed to perform OIDC discovery against %q:\n%s", upstream.Spec.Issuer, pinnipedcontroller.TruncateMostLongErr(err)), + }, + tlsCondition, } } @@ -356,11 +385,14 @@ func (c *oidcWatcherController) validateIssuer(ctx context.Context, upstream *id } if err := discoveredProvider.Claims(&additionalDiscoveryClaims); err != nil { // This shouldn't actually happen because the above call to NewProvider() would have already returned this error. - return &metav1.Condition{ - Type: typeOIDCDiscoverySucceeded, - Status: metav1.ConditionFalse, - Reason: reasonInvalidResponse, - Message: fmt.Sprintf("failed to unmarshal OIDC discovery response from %q:\n%s", upstream.Spec.Issuer, pinnipedcontroller.TruncateMostLongErr(err)), + return []*metav1.Condition{ + { + Type: typeOIDCDiscoverySucceeded, + Status: metav1.ConditionFalse, + Reason: reasonInvalidResponse, + Message: fmt.Sprintf("failed to unmarshal OIDC discovery response from %q:\n%s", upstream.Spec.Issuer, pinnipedcontroller.TruncateMostLongErr(err)), + }, + tlsCondition, } } if additionalDiscoveryClaims.RevocationEndpoint != "" { @@ -371,7 +403,7 @@ func (c *oidcWatcherController) validateIssuer(ctx context.Context, upstream *id reasonInvalidResponse, ) if revocationURLCondition != nil { - return revocationURLCondition + return []*metav1.Condition{revocationURLCondition, tlsCondition} } // Remember the URL for later use. result.RevocationURL = revocationURL @@ -383,7 +415,7 @@ func (c *oidcWatcherController) validateIssuer(ctx context.Context, upstream *id reasonInvalidResponse, ) if authorizeURLCondition != nil { - return authorizeURLCondition + return []*metav1.Condition{authorizeURLCondition, tlsCondition} } _, tokenURLCondition := validateHTTPSURL( @@ -392,18 +424,21 @@ func (c *oidcWatcherController) validateIssuer(ctx context.Context, upstream *id reasonInvalidResponse, ) if tokenURLCondition != nil { - return tokenURLCondition + return []*metav1.Condition{tokenURLCondition, tlsCondition} } // If everything is valid, update the result and set the condition to true. result.Config.Endpoint = discoveredProvider.Endpoint() result.Provider = discoveredProvider result.Client = httpClient - return &metav1.Condition{ - Type: typeOIDCDiscoverySucceeded, - Status: metav1.ConditionTrue, - Reason: upstreamwatchers.ReasonSuccess, - Message: "discovered issuer configuration", + return []*metav1.Condition{ + { + Type: typeOIDCDiscoverySucceeded, + Status: metav1.ConditionTrue, + Reason: conditionsutil.ReasonSuccess, + Message: "discovered issuer configuration", + }, + tlsCondition, } } @@ -443,7 +478,7 @@ func getClient(upstream *idpv1alpha1.OIDCIdentityProvider) (*http.Client, error) rootCAs := x509.NewCertPool() if !rootCAs.AppendCertsFromPEM(bundle) { - return nil, fmt.Errorf("spec.certificateAuthorityData is invalid: %w", upstreamwatchers.ErrNoCertificates) + return nil, fmt.Errorf("spec.certificateAuthorityData is invalid: %w", tlsconfigutil.ErrNoCertificates) } return defaultClientShortTimeout(rootCAs), nil diff --git a/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher_test.go b/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher_test.go index 1f8889434..376b72fea 100644 --- a/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher_test.go +++ b/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher_test.go @@ -49,7 +49,7 @@ func TestOIDCUpstreamWatcherControllerFilterSecret(t *testing.T) { wantDelete bool }{ { - name: "a secret of the right type", + name: "should return true for a secret of the type secrets.pinniped.dev/oidc-client", secret: &corev1.Secret{ Type: "secrets.pinniped.dev/oidc-client", ObjectMeta: metav1.ObjectMeta{Name: "some-name", Namespace: "some-namespace"}, @@ -59,14 +59,34 @@ func TestOIDCUpstreamWatcherControllerFilterSecret(t *testing.T) { wantDelete: true, }, { - name: "a secret of the wrong type", + name: "should return true for a secret of the type Opaque", + secret: &corev1.Secret{ + Type: corev1.SecretTypeOpaque, + ObjectMeta: metav1.ObjectMeta{Name: "some-name", Namespace: "some-namespace"}, + }, + wantAdd: true, + wantUpdate: true, + wantDelete: true, + }, + { + name: "should return true for a secret of the type TLS", + secret: &corev1.Secret{ + Type: corev1.SecretTypeTLS, + ObjectMeta: metav1.ObjectMeta{Name: "some-name", Namespace: "some-namespace"}, + }, + wantAdd: true, + wantUpdate: true, + wantDelete: true, + }, + { + name: "should return false for a secret of the wrong type", secret: &corev1.Secret{ Type: "secrets.pinniped.dev/not-the-oidc-client-type", ObjectMeta: metav1.ObjectMeta{Name: "some-name", Namespace: "some-namespace"}, }, }, { - name: "resource of wrong data type", + name: "should return false for resource of wrong data type", secret: &corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{Name: "some-name", Namespace: "some-namespace"}, }, @@ -85,6 +105,7 @@ func TestOIDCUpstreamWatcherControllerFilterSecret(t *testing.T) { &upstreamoidc.ProviderConfig{Name: "initial-entry"}, }) secretInformer := kubeInformers.Core().V1().Secrets() + configMapInformer := kubeInformers.Core().V1().ConfigMaps() withInformer := testutil.NewObservableWithInformerOption() var log bytes.Buffer @@ -95,6 +116,7 @@ func TestOIDCUpstreamWatcherControllerFilterSecret(t *testing.T) { nil, pinnipedInformers.IDP().V1alpha1().OIDCIdentityProviders(), secretInformer, + configMapInformer, logger, withInformer.WithInformer, ) @@ -109,6 +131,65 @@ func TestOIDCUpstreamWatcherControllerFilterSecret(t *testing.T) { } } +func TestOIDCUpstreamWatcherControllerFilterConfigMaps(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + cm metav1.Object + wantAdd bool + wantUpdate bool + wantDelete bool + }{ + { + name: "any configmap", + cm: &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{Name: "some-name", Namespace: "some-namespace"}, + }, + wantAdd: true, + wantUpdate: true, + wantDelete: true, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + t.Parallel() + + fakePinnipedClient := supervisorfake.NewSimpleClientset() + pinnipedInformers := supervisorinformers.NewSharedInformerFactory(fakePinnipedClient, 0) + fakeKubeClient := fake.NewSimpleClientset() + kubeInformers := informers.NewSharedInformerFactory(fakeKubeClient, 0) + cache := dynamicupstreamprovider.NewDynamicUpstreamIDPProvider() + cache.SetOIDCIdentityProviders([]upstreamprovider.UpstreamOIDCIdentityProviderI{ + &upstreamoidc.ProviderConfig{Name: "initial-entry"}, + }) + secretInformer := kubeInformers.Core().V1().Secrets() + configMapInformer := kubeInformers.Core().V1().ConfigMaps() + withInformer := testutil.NewObservableWithInformerOption() + + var log bytes.Buffer + logger := plog.TestLogger(t, &log) + + New( + cache, + nil, + pinnipedInformers.IDP().V1alpha1().OIDCIdentityProviders(), + secretInformer, + configMapInformer, + logger, + withInformer.WithInformer, + ) + + unrelated := corev1.ConfigMap{} + filter := withInformer.GetFilterForInformer(configMapInformer) + require.Equal(t, test.wantAdd, filter.Add(test.cm)) + require.Equal(t, test.wantUpdate, filter.Update(&unrelated, test.cm)) + require.Equal(t, test.wantUpdate, filter.Update(test.cm, &unrelated)) + require.Equal(t, test.wantDelete, filter.Delete(test.cm)) + }) + } +} + func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { t.Parallel() now := metav1.NewTime(time.Now().UTC()) @@ -1429,6 +1510,7 @@ oidc: issuer did not match the issuer returned by provider, expected "` + testIs fakePinnipedClient, pinnipedInformers.IDP().V1alpha1().OIDCIdentityProviders(), kubeInformers.Core().V1().Secrets(), + kubeInformers.Core().V1().ConfigMaps(), logger, controllerlib.WithInformer, ) diff --git a/internal/controller/utils.go b/internal/controller/utils.go index e280cce68..2202c92b5 100644 --- a/internal/controller/utils.go +++ b/internal/controller/utils.go @@ -66,6 +66,30 @@ func MatchAnySecretOfTypeFilter(secretType corev1.SecretType, parentFunc control return SimpleFilter(isSecretOfType, parentFunc) } +func containsSecretType(filter []corev1.SecretType, secretType corev1.SecretType) bool { + for _, filter := range filter { + if filter == secretType { + return true + } + } + return false +} + +func MatchAnySecretOfTypesFilter(secretTypes []corev1.SecretType, parentFunc controllerlib.ParentFunc, namespaces ...string) controllerlib.Filter { + isSecretOfType := func(obj metav1.Object) bool { + secret, ok := obj.(*corev1.Secret) + if !ok { + return false + } + // Only match on namespace if namespaces are provided + if len(namespaces) > 0 && !slices.Contains(namespaces, secret.Namespace) { + return false + } + return slices.Contains(secretTypes, secret.Type) + } + return SimpleFilter(isSecretOfType, parentFunc) +} + func SecretIsControlledByParentFunc(matchFunc func(obj metav1.Object) bool) func(obj metav1.Object) controllerlib.Key { return func(obj metav1.Object) controllerlib.Key { if matchFunc(obj) { diff --git a/internal/supervisor/server/server.go b/internal/supervisor/server/server.go index 4dbf79245..41fa312ae 100644 --- a/internal/supervisor/server/server.go +++ b/internal/supervisor/server/server.go @@ -304,6 +304,7 @@ func prepareControllers( pinnipedClient, pinnipedInformers.IDP().V1alpha1().OIDCIdentityProviders(), secretInformer, + configMapInformer, plog.New(), controllerlib.WithInformer, ), @@ -314,6 +315,7 @@ func prepareControllers( pinnipedClient, pinnipedInformers.IDP().V1alpha1().LDAPIdentityProviders(), secretInformer, + configMapInformer, controllerlib.WithInformer, ), singletonWorker). @@ -334,6 +336,7 @@ func prepareControllers( pinnipedClient, pinnipedInformers.IDP().V1alpha1().GitHubIdentityProviders(), secretInformer, + configMapInformer, plog.New(), controllerlib.WithInformer, clock.RealClock{}, From 199562fd05862fe721a8e7e8133c796e2da420a8 Mon Sep 17 00:00:00 2001 From: Ashish Amarnath Date: Fri, 28 Jun 2024 14:57:03 -0700 Subject: [PATCH 07/99] get all supervisor unit tests to pass Signed-off-by: Ashish Amarnath --- .../active_directory_upstream_watcher_test.go | 63 ++++--- .../github_upstream_watcher_test.go | 81 ++++---- .../ldap_upstream_watcher.go | 2 +- .../ldap_upstream_watcher_test.go | 36 ++-- .../oidc_upstream_watcher.go | 21 +-- .../oidc_upstream_watcher_test.go | 178 +++++++++++++++++- .../upstreamwatchers/upstream_watchers.go | 2 +- .../tlsconfigutil/tls_config_util.go | 30 +-- .../tlsconfigutil/tls_config_util_test.go | 107 ++++++----- internal/controller/utils.go | 9 - test/integration/concierge_tls_spec_test.go | 2 + test/integration/supervisor_tls_spec_test.go | 2 + 12 files changed, 355 insertions(+), 178 deletions(-) diff --git a/internal/controller/supervisorconfig/activedirectoryupstreamwatcher/active_directory_upstream_watcher_test.go b/internal/controller/supervisorconfig/activedirectoryupstreamwatcher/active_directory_upstream_watcher_test.go index e6007c65e..f94cc70f8 100644 --- a/internal/controller/supervisorconfig/activedirectoryupstreamwatcher/active_directory_upstream_watcher_test.go +++ b/internal/controller/supervisorconfig/activedirectoryupstreamwatcher/active_directory_upstream_watcher_test.go @@ -342,13 +342,13 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { c.LastTransitionTime = metav1.Time{} return c } - tlsConfigurationValidLoadedTrueCondition := func(gen int64) metav1.Condition { + tlsConfigurationValidLoadedTrueCondition := func(gen int64, msg string) metav1.Condition { return metav1.Condition{ Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", - Message: "loaded TLS configuration", + Message: fmt.Sprintf("spec.tls is valid: %s", msg), ObservedGeneration: gen, } } @@ -391,7 +391,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { bindSecretValidTrueCondition(gen), activeDirectoryConnectionValidTrueCondition(gen, secretVersion), searchBaseFoundInConfigCondition(gen), - tlsConfigurationValidLoadedTrueCondition(gen), + tlsConfigurationValidLoadedTrueCondition(gen, "loaded TLS configuration"), } } @@ -493,7 +493,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { Message: fmt.Sprintf(`secret "%s" not found`, testBindSecretName), ObservedGeneration: 1234, }, - tlsConfigurationValidLoadedTrueCondition(1234), + tlsConfigurationValidLoadedTrueCondition(1234, "loaded TLS configuration"), }, }, }}, @@ -521,7 +521,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { Message: fmt.Sprintf(`referenced Secret "%s" has wrong type "some-other-type" (should be "kubernetes.io/basic-auth")`, testBindSecretName), ObservedGeneration: 1234, }, - tlsConfigurationValidLoadedTrueCondition(1234), + tlsConfigurationValidLoadedTrueCondition(1234, "loaded TLS configuration"), }, }, }}, @@ -548,7 +548,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { Message: fmt.Sprintf(`referenced Secret "%s" is missing required keys ["username" "password"]`, testBindSecretName), ObservedGeneration: 1234, }, - tlsConfigurationValidLoadedTrueCondition(1234), + tlsConfigurationValidLoadedTrueCondition(1234, "loaded TLS configuration"), }, }, }}, @@ -572,7 +572,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { Status: "False", LastTransitionTime: now, Reason: "InvalidTLSConfig", - Message: "certificateAuthorityData is invalid: illegal base64 data at input byte 4", + Message: "spec.tls.certificateAuthorityData is invalid: illegal base64 data at input byte 4", ObservedGeneration: 1234, }, }, @@ -598,7 +598,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { Status: "False", LastTransitionTime: now, Reason: "InvalidTLSConfig", - Message: "certificateAuthorityData is invalid: no certificates found", + Message: "spec.tls.certificateAuthorityData is invalid: no certificates found", ObservedGeneration: 1234, }, }, @@ -658,7 +658,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { Status: "True", LastTransitionTime: now, Reason: "Success", - Message: "no TLS configuration provided", + Message: "spec.tls is valid: no TLS configuration provided", ObservedGeneration: 1234, }, }, @@ -728,7 +728,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { Status: "True", LastTransitionTime: now, Reason: "Success", - Message: "no TLS configuration provided", + Message: "spec.tls is valid: no TLS configuration provided", ObservedGeneration: 1234, }, }, @@ -805,7 +805,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { ObservedGeneration: 1234, }, searchBaseFoundInConfigCondition(1234), - tlsConfigurationValidLoadedTrueCondition(1234), + tlsConfigurationValidLoadedTrueCondition(1234, "loaded TLS configuration"), }, }, }}, @@ -886,7 +886,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { ObservedGeneration: 1234, }, searchBaseFoundInConfigCondition(1234), - tlsConfigurationValidLoadedTrueCondition(1234), + tlsConfigurationValidLoadedTrueCondition(1234, "loaded TLS configuration"), }, }, }}, @@ -935,8 +935,13 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { wantResultingUpstreams: []idpv1alpha1.ActiveDirectoryIdentityProvider{{ ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testName, UID: testResourceUID, Generation: 1234}, Status: idpv1alpha1.ActiveDirectoryIdentityProviderStatus{ - Phase: "Ready", - Conditions: allConditionsTrue(1234, "4242"), + Phase: "Ready", + Conditions: []metav1.Condition{ + bindSecretValidTrueCondition(1234), + activeDirectoryConnectionValidTrueCondition(1234, "4242"), + searchBaseFoundInConfigCondition(1234), + tlsConfigurationValidLoadedTrueCondition(1234, "no TLS configuration provided"), + }, }, }}, wantValidatedSettings: map[string]upstreamwatchers.ValidatedSettings{testName: { @@ -979,7 +984,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { Message: fmt.Sprintf(`secret "%s" not found`, "non-existent-secret"), ObservedGeneration: 42, }, - tlsConfigurationValidLoadedTrueCondition(42), + tlsConfigurationValidLoadedTrueCondition(42, "loaded TLS configuration"), }, }, }, @@ -1032,7 +1037,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { ObservedGeneration: 1234, }, searchBaseFoundInConfigCondition(1234), - tlsConfigurationValidLoadedTrueCondition(1234), + tlsConfigurationValidLoadedTrueCondition(1234, "loaded TLS configuration"), }, }, }}, @@ -1100,7 +1105,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { ObservedGeneration: 1234, }, searchBaseFoundInRootDSECondition(1234), - tlsConfigurationValidLoadedTrueCondition(1234), + tlsConfigurationValidLoadedTrueCondition(1234, "loaded TLS configuration"), }, }, }}, @@ -1136,7 +1141,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { ObservedGeneration: 1234, }, searchBaseFoundErrorCondition(1234, "Error finding search base: error binding as \"test-bind-username\" before querying for defaultNamingContext: some bind error"), - tlsConfigurationValidLoadedTrueCondition(1234), + tlsConfigurationValidLoadedTrueCondition(1234, "loaded TLS configuration"), }, }, }}, @@ -1240,7 +1245,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { bindSecretValidTrueCondition(1234), activeDirectoryConnectionValidTrueCondition(1234, "4242"), searchBaseFoundInRootDSECondition(1234), - tlsConfigurationValidLoadedTrueCondition(1234), + tlsConfigurationValidLoadedTrueCondition(1234, "loaded TLS configuration"), }, }, }}, @@ -1313,7 +1318,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { bindSecretValidTrueCondition(1234), activeDirectoryConnectionValidTrueCondition(1234, "4242"), searchBaseFoundInRootDSECondition(1234), - tlsConfigurationValidLoadedTrueCondition(1234), + tlsConfigurationValidLoadedTrueCondition(1234, "loaded TLS configuration"), }, }, }}, @@ -1640,7 +1645,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { bindSecretValidTrueCondition(1234), activeDirectoryConnectionValidTrueCondition(1234, "4242"), searchBaseFoundInRootDSECondition(1234), - tlsConfigurationValidLoadedTrueCondition(1234), + tlsConfigurationValidLoadedTrueCondition(1234, "loaded TLS configuration"), }, }, }}, @@ -1704,7 +1709,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { bindSecretValidTrueCondition(1234), activeDirectoryConnectionValidTrueCondition(1234, "4242"), searchBaseFoundInRootDSECondition(1234), - tlsConfigurationValidLoadedTrueCondition(1234), + tlsConfigurationValidLoadedTrueCondition(1234, "loaded TLS configuration"), }, }, }}, @@ -1768,7 +1773,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { bindSecretValidTrueCondition(1234), activeDirectoryConnectionValidTrueCondition(1234, "4242"), searchBaseFoundInRootDSECondition(1234), - tlsConfigurationValidLoadedTrueCondition(1234), + tlsConfigurationValidLoadedTrueCondition(1234, "loaded TLS configuration"), }, }, }}, @@ -1804,7 +1809,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { bindSecretValidTrueCondition(1234), activeDirectoryConnectionValidTrueCondition(1234, "4242"), searchBaseFoundErrorCondition(1234, "Error finding search base: error querying RootDSE for defaultNamingContext: some error"), - tlsConfigurationValidLoadedTrueCondition(1234), + tlsConfigurationValidLoadedTrueCondition(1234, "loaded TLS configuration"), }, }, }}, @@ -1840,7 +1845,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { bindSecretValidTrueCondition(1234), activeDirectoryConnectionValidTrueCondition(1234, "4242"), searchBaseFoundErrorCondition(1234, "Error finding search base: error querying RootDSE for defaultNamingContext: empty search base DN found"), - tlsConfigurationValidLoadedTrueCondition(1234), + tlsConfigurationValidLoadedTrueCondition(1234, "loaded TLS configuration"), }, }, }}, @@ -1882,7 +1887,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { bindSecretValidTrueCondition(1234), activeDirectoryConnectionValidTrueCondition(1234, "4242"), searchBaseFoundErrorCondition(1234, "Error finding search base: error querying RootDSE for defaultNamingContext: expected to find 1 entry but found 2"), - tlsConfigurationValidLoadedTrueCondition(1234), + tlsConfigurationValidLoadedTrueCondition(1234, "loaded TLS configuration"), }, }, }}, @@ -1911,7 +1916,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { bindSecretValidTrueCondition(1234), activeDirectoryConnectionValidTrueCondition(1234, "4242"), searchBaseFoundErrorCondition(1234, "Error finding search base: error querying RootDSE for defaultNamingContext: expected to find 1 entry but found 0"), - tlsConfigurationValidLoadedTrueCondition(1234), + tlsConfigurationValidLoadedTrueCondition(1234, "loaded TLS configuration"), }, }, }}, @@ -1980,7 +1985,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { bindSecretValidTrueCondition(1234), activeDirectoryConnectionValidTrueCondition(1234, "4242"), searchBaseFoundInRootDSECondition(1234), - tlsConfigurationValidLoadedTrueCondition(1234), + tlsConfigurationValidLoadedTrueCondition(1234, "loaded TLS configuration"), }, }, }}, @@ -2048,7 +2053,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { Status: "True", LastTransitionTime: now, Reason: "Success", - Message: "loaded TLS configuration", + Message: "spec.tls is valid: loaded TLS configuration", ObservedGeneration: 1234, }, }, diff --git a/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher_test.go b/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher_test.go index 68266001c..250124a2e 100644 --- a/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher_test.go +++ b/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher_test.go @@ -185,8 +185,7 @@ func TestController(t *testing.T) { Message: fmt.Sprintf(`spec.githubAPI.host (%q) is not valid: %s`, host, message), } } - - buildTLSConfigurationValidTrue := func(t *testing.T) metav1.Condition { + buildTLSConfigurationValidTrueWithMsg := func(t *testing.T, msg string) metav1.Condition { t.Helper() return metav1.Condition{ @@ -195,10 +194,15 @@ func TestController(t *testing.T) { ObservedGeneration: wantObservedGeneration, LastTransitionTime: wantLastTransitionTime, Reason: conditionsutil.ReasonSuccess, - Message: "spec.githubAPI.tls.certificateAuthorityData is valid", + Message: fmt.Sprintf("spec.githubAPI.tls is valid: %s", msg), } } + buildTLSConfigurationValidTrue := func(t *testing.T) metav1.Condition { + t.Helper() + return buildTLSConfigurationValidTrueWithMsg(t, "loaded TLS configuration") + } + buildTLSConfigurationValidFalse := func(t *testing.T, message string) metav1.Condition { t.Helper() @@ -437,7 +441,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidTrue("some-idp-name"), buildLogForUpdatingOrganizationPolicyValid("some-idp-name", "True", "Success", fmt.Sprintf(`spec.allowAuthentication.organizations.policy (\"%s\") is valid`, string(*validFilledOutIDP.Spec.AllowAuthentication.Organizations.Policy))), buildLogForUpdatingHostValid("some-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is valid`, *validFilledOutIDP.Spec.GitHubAPI.Host), - buildLogForUpdatingTLSConfigurationValid("some-idp-name", "True", "Success", "spec.githubAPI.tls.certificateAuthorityData is valid"), + buildLogForUpdatingTLSConfigurationValid("some-idp-name", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), buildLogForUpdatingGitHubConnectionValid("some-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is reachable and TLS verification succeeds`, *validFilledOutIDP.Spec.GitHubAPI.Host), buildLogForUpdatingPhase("some-idp-name", "Ready"), }, @@ -493,7 +497,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidTrue("minimal-idp-name"), buildLogForUpdatingOrganizationPolicyValid("minimal-idp-name", "True", "Success", fmt.Sprintf(`spec.allowAuthentication.organizations.policy (\"%s\") is valid`, string(*validMinimalIDP.Spec.AllowAuthentication.Organizations.Policy))), buildLogForUpdatingHostValid("minimal-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is valid`, *validMinimalIDP.Spec.GitHubAPI.Host), - buildLogForUpdatingTLSConfigurationValid("minimal-idp-name", "True", "Success", "spec.githubAPI.tls.certificateAuthorityData is valid"), + buildLogForUpdatingTLSConfigurationValid("minimal-idp-name", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), buildLogForUpdatingGitHubConnectionValid("minimal-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is reachable and TLS verification succeeds`, *validMinimalIDP.Spec.GitHubAPI.Host), buildLogForUpdatingPhase("minimal-idp-name", "Ready"), }, @@ -568,7 +572,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidTrue("minimal-idp-name"), buildLogForUpdatingOrganizationPolicyValid("minimal-idp-name", "True", "Success", fmt.Sprintf(`spec.allowAuthentication.organizations.policy (\"%s\") is valid`, string(*validMinimalIDP.Spec.AllowAuthentication.Organizations.Policy))), buildLogForUpdatingHostValid("minimal-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is valid`, "github.com"), - buildLogForUpdatingTLSConfigurationValid("minimal-idp-name", "True", "Success", "spec.githubAPI.tls.certificateAuthorityData is valid"), + buildLogForUpdatingTLSConfigurationValid("minimal-idp-name", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), buildLogForUpdatingGitHubConnectionValid("minimal-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is reachable and TLS verification succeeds`, "github.com:443"), buildLogForUpdatingPhase("minimal-idp-name", "Ready"), }, @@ -639,7 +643,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidTrue("minimal-idp-name"), buildLogForUpdatingOrganizationPolicyValid("minimal-idp-name", "True", "Success", fmt.Sprintf(`spec.allowAuthentication.organizations.policy (\"%s\") is valid`, string(*validMinimalIDP.Spec.AllowAuthentication.Organizations.Policy))), buildLogForUpdatingHostValid("minimal-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is valid`, goodServerIPv6Domain), - buildLogForUpdatingTLSConfigurationValid("minimal-idp-name", "True", "Success", "spec.githubAPI.tls.certificateAuthorityData is valid"), + buildLogForUpdatingTLSConfigurationValid("minimal-idp-name", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), buildLogForUpdatingGitHubConnectionValid("minimal-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is reachable and TLS verification succeeds`, goodServerIPv6Domain), buildLogForUpdatingPhase("minimal-idp-name", "Ready"), }, @@ -793,7 +797,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidTrue("invalid-idp-name"), buildLogForUpdatingOrganizationPolicyValid("invalid-idp-name", "True", "Success", fmt.Sprintf(`spec.allowAuthentication.organizations.policy (\"%s\") is valid`, string(*validFilledOutIDP.Spec.AllowAuthentication.Organizations.Policy))), buildLogForUpdatingHostValid("invalid-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is valid`, *validFilledOutIDP.Spec.GitHubAPI.Host), - buildLogForUpdatingTLSConfigurationValid("invalid-idp-name", "True", "Success", "spec.githubAPI.tls.certificateAuthorityData is valid"), + buildLogForUpdatingTLSConfigurationValid("invalid-idp-name", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), buildLogForUpdatingGitHubConnectionValid("invalid-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is reachable and TLS verification succeeds`, *validFilledOutIDP.Spec.GitHubAPI.Host), buildLogForUpdatingPhase("invalid-idp-name", "Error"), @@ -801,7 +805,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidTrue("other-idp-name"), buildLogForUpdatingOrganizationPolicyValid("other-idp-name", "True", "Success", fmt.Sprintf(`spec.allowAuthentication.organizations.policy (\"%s\") is valid`, string(*validFilledOutIDP.Spec.AllowAuthentication.Organizations.Policy))), buildLogForUpdatingHostValid("other-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is valid`, *validFilledOutIDP.Spec.GitHubAPI.Host), - buildLogForUpdatingTLSConfigurationValid("other-idp-name", "True", "Success", "spec.githubAPI.tls.certificateAuthorityData is valid"), + buildLogForUpdatingTLSConfigurationValid("other-idp-name", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), buildLogForUpdatingGitHubConnectionValid("other-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is reachable and TLS verification succeeds`, *validFilledOutIDP.Spec.GitHubAPI.Host), buildLogForUpdatingPhase("other-idp-name", "Ready"), @@ -809,7 +813,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidTrue("some-idp-name"), buildLogForUpdatingOrganizationPolicyValid("some-idp-name", "True", "Success", fmt.Sprintf(`spec.allowAuthentication.organizations.policy (\"%s\") is valid`, string(*validFilledOutIDP.Spec.AllowAuthentication.Organizations.Policy))), buildLogForUpdatingHostValid("some-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is valid`, *validFilledOutIDP.Spec.GitHubAPI.Host), - buildLogForUpdatingTLSConfigurationValid("some-idp-name", "True", "Success", "spec.githubAPI.tls.certificateAuthorityData is valid"), + buildLogForUpdatingTLSConfigurationValid("some-idp-name", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), buildLogForUpdatingGitHubConnectionValid("some-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is reachable and TLS verification succeeds`, *validFilledOutIDP.Spec.GitHubAPI.Host), buildLogForUpdatingPhase("some-idp-name", "Ready"), }, @@ -850,7 +854,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidTrue("some-idp-name"), buildLogForUpdatingOrganizationPolicyValid("some-idp-name", "True", "Success", fmt.Sprintf(`spec.allowAuthentication.organizations.policy (\"%s\") is valid`, string(*validFilledOutIDP.Spec.AllowAuthentication.Organizations.Policy))), buildLogForUpdatingHostValid("some-idp-name", "False", "InvalidHost", `spec.githubAPI.host (\"%s\") is not valid: must not be empty`, ""), - buildLogForUpdatingTLSConfigurationValid("some-idp-name", "True", "Success", "spec.githubAPI.tls.certificateAuthorityData is valid"), + buildLogForUpdatingTLSConfigurationValid("some-idp-name", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), buildLogForUpdatingGitHubConnectionValidUnknown("some-idp-name"), buildLogForUpdatingPhase("some-idp-name", "Error"), }, @@ -891,7 +895,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidTrue("minimal-idp-name"), buildLogForUpdatingOrganizationPolicyValid("minimal-idp-name", "True", "Success", fmt.Sprintf(`spec.allowAuthentication.organizations.policy (\"%s\") is valid`, string(*validMinimalIDP.Spec.AllowAuthentication.Organizations.Policy))), buildLogForUpdatingHostValid("minimal-idp-name", "False", "InvalidHost", `spec.githubAPI.host (\"%s\") is not valid: invalid port \"//example.com\"`, "https://example.com"), - buildLogForUpdatingTLSConfigurationValid("minimal-idp-name", "True", "Success", "spec.githubAPI.tls.certificateAuthorityData is valid"), + buildLogForUpdatingTLSConfigurationValid("minimal-idp-name", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), buildLogForUpdatingGitHubConnectionValidUnknown("minimal-idp-name"), buildLogForUpdatingPhase("minimal-idp-name", "Error"), }, @@ -932,7 +936,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidTrue("minimal-idp-name"), buildLogForUpdatingOrganizationPolicyValid("minimal-idp-name", "True", "Success", fmt.Sprintf(`spec.allowAuthentication.organizations.policy (\"%s\") is valid`, string(*validMinimalIDP.Spec.AllowAuthentication.Organizations.Policy))), buildLogForUpdatingHostValid("minimal-idp-name", "False", "InvalidHost", `spec.githubAPI.host (\"%s\") is not valid: host \"example.com/foo\" is not a valid hostname or IP address`, "example.com/foo"), - buildLogForUpdatingTLSConfigurationValid("minimal-idp-name", "True", "Success", "spec.githubAPI.tls.certificateAuthorityData is valid"), + buildLogForUpdatingTLSConfigurationValid("minimal-idp-name", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), buildLogForUpdatingGitHubConnectionValidUnknown("minimal-idp-name"), buildLogForUpdatingPhase("minimal-idp-name", "Error"), }, @@ -973,7 +977,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidTrue("minimal-idp-name"), buildLogForUpdatingOrganizationPolicyValid("minimal-idp-name", "True", "Success", fmt.Sprintf(`spec.allowAuthentication.organizations.policy (\"%s\") is valid`, string(*validMinimalIDP.Spec.AllowAuthentication.Organizations.Policy))), buildLogForUpdatingHostValid("minimal-idp-name", "False", "InvalidHost", `spec.githubAPI.host (\"%s\") is not valid: invalid port \"p@example.com\"`, "u:p@example.com"), - buildLogForUpdatingTLSConfigurationValid("minimal-idp-name", "True", "Success", "spec.githubAPI.tls.certificateAuthorityData is valid"), + buildLogForUpdatingTLSConfigurationValid("minimal-idp-name", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), buildLogForUpdatingGitHubConnectionValidUnknown("minimal-idp-name"), buildLogForUpdatingPhase("minimal-idp-name", "Error"), }, @@ -1014,7 +1018,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidTrue("minimal-idp-name"), buildLogForUpdatingOrganizationPolicyValid("minimal-idp-name", "True", "Success", fmt.Sprintf(`spec.allowAuthentication.organizations.policy (\"%s\") is valid`, string(*validMinimalIDP.Spec.AllowAuthentication.Organizations.Policy))), buildLogForUpdatingHostValid("minimal-idp-name", "False", "InvalidHost", `spec.githubAPI.host (\"%s\") is not valid: host \"example.com?a=b\" is not a valid hostname or IP address`, "example.com?a=b"), - buildLogForUpdatingTLSConfigurationValid("minimal-idp-name", "True", "Success", "spec.githubAPI.tls.certificateAuthorityData is valid"), + buildLogForUpdatingTLSConfigurationValid("minimal-idp-name", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), buildLogForUpdatingGitHubConnectionValidUnknown("minimal-idp-name"), buildLogForUpdatingPhase("minimal-idp-name", "Error"), }, @@ -1055,7 +1059,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidTrue("minimal-idp-name"), buildLogForUpdatingOrganizationPolicyValid("minimal-idp-name", "True", "Success", fmt.Sprintf(`spec.allowAuthentication.organizations.policy (\"%s\") is valid`, string(*validMinimalIDP.Spec.AllowAuthentication.Organizations.Policy))), buildLogForUpdatingHostValid("minimal-idp-name", "False", "InvalidHost", `spec.githubAPI.host (\"%s\") is not valid: host \"example.com#a\" is not a valid hostname or IP address`, "example.com#a"), - buildLogForUpdatingTLSConfigurationValid("minimal-idp-name", "True", "Success", "spec.githubAPI.tls.certificateAuthorityData is valid"), + buildLogForUpdatingTLSConfigurationValid("minimal-idp-name", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), buildLogForUpdatingGitHubConnectionValidUnknown("minimal-idp-name"), buildLogForUpdatingPhase("minimal-idp-name", "Error"), }, @@ -1090,7 +1094,7 @@ func TestController(t *testing.T) { buildGitHubConnectionValidUnknown(t), buildHostValidTrue(t, *validFilledOutIDP.Spec.GitHubAPI.Host), buildOrganizationsPolicyValidTrue(t, *validFilledOutIDP.Spec.AllowAuthentication.Organizations.Policy), - buildTLSConfigurationValidFalse(t, "spec.githubAPI.tls.certificateAuthorityData is not valid: certificateAuthorityData is not valid PEM: data does not contain any valid RSA or ECDSA certificates"), + buildTLSConfigurationValidFalse(t, "spec.githubAPI.tls.certificateAuthorityData is invalid: no certificates found"), }, }, }, @@ -1100,7 +1104,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidTrue("some-idp-name"), buildLogForUpdatingOrganizationPolicyValid("some-idp-name", "True", "Success", fmt.Sprintf(`spec.allowAuthentication.organizations.policy (\"%s\") is valid`, string(*validFilledOutIDP.Spec.AllowAuthentication.Organizations.Policy))), buildLogForUpdatingHostValid("some-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is valid`, *validFilledOutIDP.Spec.GitHubAPI.Host), - buildLogForUpdatingTLSConfigurationValid("some-idp-name", "False", "InvalidTLSConfig", "spec.githubAPI.tls.certificateAuthorityData is not valid: certificateAuthorityData is not valid PEM: data does not contain any valid RSA or ECDSA certificates"), + buildLogForUpdatingTLSConfigurationValid("some-idp-name", "False", "InvalidTLSConfig", "spec.githubAPI.tls.certificateAuthorityData is invalid: no certificates found"), buildLogForUpdatingGitHubConnectionValidUnknown("some-idp-name"), buildLogForUpdatingPhase("some-idp-name", "Error"), }, @@ -1142,7 +1146,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidTrue("minimal-idp-name"), buildLogForUpdatingOrganizationPolicyValid("minimal-idp-name", "True", "Success", fmt.Sprintf(`spec.allowAuthentication.organizations.policy (\"%s\") is valid`, string(*validMinimalIDP.Spec.AllowAuthentication.Organizations.Policy))), buildLogForUpdatingHostValid("minimal-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is valid`, "nowhere.bad-tld"), - buildLogForUpdatingTLSConfigurationValid("minimal-idp-name", "True", "Success", "spec.githubAPI.tls.certificateAuthorityData is valid"), + buildLogForUpdatingTLSConfigurationValid("minimal-idp-name", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), buildLogForUpdatingGitHubConnectionValid("minimal-idp-name", "False", "UnableToDialServer", `cannot dial server spec.githubAPI.host (\"%s\"): dial tcp: lookup nowhere.bad-tld: no such host`, "nowhere.bad-tld:443"), buildLogForUpdatingPhase("minimal-idp-name", "Error"), }, @@ -1183,7 +1187,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidTrue("minimal-idp-name"), buildLogForUpdatingOrganizationPolicyValid("minimal-idp-name", "True", "Success", fmt.Sprintf(`spec.allowAuthentication.organizations.policy (\"%s\") is valid`, string(*validMinimalIDP.Spec.AllowAuthentication.Organizations.Policy))), buildLogForUpdatingHostValid("minimal-idp-name", "False", "InvalidHost", `spec.githubAPI.host (\"0:0:0:0:0:0:0:1:9876\") is not valid: host \"%s\" is not a valid hostname or IP address`, "0:0:0:0:0:0:0:1:9876"), - buildLogForUpdatingTLSConfigurationValid("minimal-idp-name", "True", "Success", "spec.githubAPI.tls.certificateAuthorityData is valid"), + buildLogForUpdatingTLSConfigurationValid("minimal-idp-name", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), buildLogForUpdatingGitHubConnectionValidUnknown("minimal-idp-name"), buildLogForUpdatingPhase("minimal-idp-name", "Error"), }, @@ -1215,7 +1219,7 @@ func TestController(t *testing.T) { buildGitHubConnectionValidFalse(t, fmt.Sprintf(`cannot dial server spec.githubAPI.host (%q): tls: failed to verify certificate: x509: certificate signed by unknown authority`, *validFilledOutIDP.Spec.GitHubAPI.Host)), buildHostValidTrue(t, *validFilledOutIDP.Spec.GitHubAPI.Host), buildOrganizationsPolicyValidTrue(t, *validFilledOutIDP.Spec.AllowAuthentication.Organizations.Policy), - buildTLSConfigurationValidTrue(t), + buildTLSConfigurationValidTrueWithMsg(t, "no TLS configuration provided"), }, }, }, @@ -1225,7 +1229,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidTrue("some-idp-name"), buildLogForUpdatingOrganizationPolicyValid("some-idp-name", "True", "Success", fmt.Sprintf(`spec.allowAuthentication.organizations.policy (\"%s\") is valid`, string(*validFilledOutIDP.Spec.AllowAuthentication.Organizations.Policy))), buildLogForUpdatingHostValid("some-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is valid`, *validFilledOutIDP.Spec.GitHubAPI.Host), - buildLogForUpdatingTLSConfigurationValid("some-idp-name", "True", "Success", "spec.githubAPI.tls.certificateAuthorityData is valid"), + buildLogForUpdatingTLSConfigurationValid("some-idp-name", "True", "Success", "spec.githubAPI.tls is valid: no TLS configuration provided"), buildLogForUpdatingGitHubConnectionValid("some-idp-name", "False", "UnableToDialServer", `cannot dial server spec.githubAPI.host (\"%s\"): tls: failed to verify certificate: x509: certificate signed by unknown authority`, *validFilledOutIDP.Spec.GitHubAPI.Host), buildLogForUpdatingPhase("some-idp-name", "Error"), }, @@ -1271,7 +1275,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidTrue("some-idp-name"), buildLogForUpdatingOrganizationPolicyValid("some-idp-name", "True", "Success", fmt.Sprintf(`spec.allowAuthentication.organizations.policy (\"%s\") is valid`, string(*validFilledOutIDP.Spec.AllowAuthentication.Organizations.Policy))), buildLogForUpdatingHostValid("some-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is valid`, *validFilledOutIDP.Spec.GitHubAPI.Host), - buildLogForUpdatingTLSConfigurationValid("some-idp-name", "True", "Success", "spec.githubAPI.tls.certificateAuthorityData is valid"), + buildLogForUpdatingTLSConfigurationValid("some-idp-name", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), buildLogForUpdatingGitHubConnectionValid("some-idp-name", "False", "UnableToDialServer", `cannot dial server spec.githubAPI.host (\"%s\"): tls: failed to verify certificate: x509: certificate signed by unknown authority`, *validFilledOutIDP.Spec.GitHubAPI.Host), buildLogForUpdatingPhase("some-idp-name", "Error"), }, @@ -1312,7 +1316,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidTrue("some-idp-name"), buildLogForUpdatingOrganizationPolicyValid("some-idp-name", "False", "Invalid", "spec.allowAuthentication.organizations.policy must be 'OnlyUsersFromAllowedOrganizations' when spec.allowAuthentication.organizations.allowed has organizations listed"), buildLogForUpdatingHostValid("some-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is valid`, *validFilledOutIDP.Spec.GitHubAPI.Host), - buildLogForUpdatingTLSConfigurationValid("some-idp-name", "True", "Success", "spec.githubAPI.tls.certificateAuthorityData is valid"), + buildLogForUpdatingTLSConfigurationValid("some-idp-name", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), buildLogForUpdatingGitHubConnectionValid("some-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is reachable and TLS verification succeeds`, *validFilledOutIDP.Spec.GitHubAPI.Host), buildLogForUpdatingPhase("some-idp-name", "Error"), }, @@ -1353,7 +1357,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidTrue("some-idp-name"), buildLogForUpdatingOrganizationPolicyValid("some-idp-name", "False", "Invalid", "spec.allowAuthentication.organizations.policy must be 'OnlyUsersFromAllowedOrganizations' when spec.allowAuthentication.organizations.allowed has organizations listed"), buildLogForUpdatingHostValid("some-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is valid`, *validFilledOutIDP.Spec.GitHubAPI.Host), - buildLogForUpdatingTLSConfigurationValid("some-idp-name", "True", "Success", "spec.githubAPI.tls.certificateAuthorityData is valid"), + buildLogForUpdatingTLSConfigurationValid("some-idp-name", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), buildLogForUpdatingGitHubConnectionValid("some-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is reachable and TLS verification succeeds`, *validFilledOutIDP.Spec.GitHubAPI.Host), buildLogForUpdatingPhase("some-idp-name", "Error"), }, @@ -1394,7 +1398,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidTrue("some-idp-name"), buildLogForUpdatingOrganizationPolicyValid("some-idp-name", "False", "Invalid", "spec.allowAuthentication.organizations.policy must be 'OnlyUsersFromAllowedOrganizations' when spec.allowAuthentication.organizations.allowed has organizations listed"), buildLogForUpdatingHostValid("some-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is valid`, *validFilledOutIDP.Spec.GitHubAPI.Host), - buildLogForUpdatingTLSConfigurationValid("some-idp-name", "True", "Success", "spec.githubAPI.tls.certificateAuthorityData is valid"), + buildLogForUpdatingTLSConfigurationValid("some-idp-name", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), buildLogForUpdatingGitHubConnectionValid("some-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is reachable and TLS verification succeeds`, *validFilledOutIDP.Spec.GitHubAPI.Host), buildLogForUpdatingPhase("some-idp-name", "Error"), }, @@ -1435,7 +1439,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidTrue("some-idp-name"), buildLogForUpdatingOrganizationPolicyValid("some-idp-name", "False", "Invalid", "spec.allowAuthentication.organizations.policy must be 'AllGitHubUsers' when spec.allowAuthentication.organizations.allowed is empty"), buildLogForUpdatingHostValid("some-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is valid`, *validFilledOutIDP.Spec.GitHubAPI.Host), - buildLogForUpdatingTLSConfigurationValid("some-idp-name", "True", "Success", "spec.githubAPI.tls.certificateAuthorityData is valid"), + buildLogForUpdatingTLSConfigurationValid("some-idp-name", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), buildLogForUpdatingGitHubConnectionValid("some-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is reachable and TLS verification succeeds`, *validFilledOutIDP.Spec.GitHubAPI.Host), buildLogForUpdatingPhase("some-idp-name", "Error"), }, @@ -1476,7 +1480,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidFalse("some-idp-name", "spec.claims.username is required"), buildLogForUpdatingOrganizationPolicyValid("some-idp-name", "True", "Success", fmt.Sprintf(`spec.allowAuthentication.organizations.policy (\"%s\") is valid`, string(*validFilledOutIDP.Spec.AllowAuthentication.Organizations.Policy))), buildLogForUpdatingHostValid("some-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is valid`, *validFilledOutIDP.Spec.GitHubAPI.Host), - buildLogForUpdatingTLSConfigurationValid("some-idp-name", "True", "Success", "spec.githubAPI.tls.certificateAuthorityData is valid"), + buildLogForUpdatingTLSConfigurationValid("some-idp-name", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), buildLogForUpdatingGitHubConnectionValid("some-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is reachable and TLS verification succeeds`, *validFilledOutIDP.Spec.GitHubAPI.Host), buildLogForUpdatingPhase("some-idp-name", "Error"), }, @@ -1517,7 +1521,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidFalse("some-idp-name", `spec.claims.username (\"a\") is not valid`), buildLogForUpdatingOrganizationPolicyValid("some-idp-name", "True", "Success", fmt.Sprintf(`spec.allowAuthentication.organizations.policy (\"%s\") is valid`, string(*validFilledOutIDP.Spec.AllowAuthentication.Organizations.Policy))), buildLogForUpdatingHostValid("some-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is valid`, *validFilledOutIDP.Spec.GitHubAPI.Host), - buildLogForUpdatingTLSConfigurationValid("some-idp-name", "True", "Success", "spec.githubAPI.tls.certificateAuthorityData is valid"), + buildLogForUpdatingTLSConfigurationValid("some-idp-name", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), buildLogForUpdatingGitHubConnectionValid("some-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is reachable and TLS verification succeeds`, *validFilledOutIDP.Spec.GitHubAPI.Host), buildLogForUpdatingPhase("some-idp-name", "Error"), }, @@ -1558,7 +1562,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidFalse("some-idp-name", "spec.claims.groups is required"), buildLogForUpdatingOrganizationPolicyValid("some-idp-name", "True", "Success", fmt.Sprintf(`spec.allowAuthentication.organizations.policy (\"%s\") is valid`, string(*validFilledOutIDP.Spec.AllowAuthentication.Organizations.Policy))), buildLogForUpdatingHostValid("some-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is valid`, *validFilledOutIDP.Spec.GitHubAPI.Host), - buildLogForUpdatingTLSConfigurationValid("some-idp-name", "True", "Success", "spec.githubAPI.tls.certificateAuthorityData is valid"), + buildLogForUpdatingTLSConfigurationValid("some-idp-name", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), buildLogForUpdatingGitHubConnectionValid("some-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is reachable and TLS verification succeeds`, *validFilledOutIDP.Spec.GitHubAPI.Host), buildLogForUpdatingPhase("some-idp-name", "Error"), }, @@ -1599,7 +1603,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidFalse("some-idp-name", `spec.claims.groups (\"b\") is not valid`), buildLogForUpdatingOrganizationPolicyValid("some-idp-name", "True", "Success", fmt.Sprintf(`spec.allowAuthentication.organizations.policy (\"%s\") is valid`, string(*validFilledOutIDP.Spec.AllowAuthentication.Organizations.Policy))), buildLogForUpdatingHostValid("some-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is valid`, *validFilledOutIDP.Spec.GitHubAPI.Host), - buildLogForUpdatingTLSConfigurationValid("some-idp-name", "True", "Success", "spec.githubAPI.tls.certificateAuthorityData is valid"), + buildLogForUpdatingTLSConfigurationValid("some-idp-name", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), buildLogForUpdatingGitHubConnectionValid("some-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is reachable and TLS verification succeeds`, *validFilledOutIDP.Spec.GitHubAPI.Host), buildLogForUpdatingPhase("some-idp-name", "Error"), }, @@ -1642,7 +1646,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidTrue("minimal-idp-name"), buildLogForUpdatingOrganizationPolicyValid("minimal-idp-name", "True", "Success", fmt.Sprintf(`spec.allowAuthentication.organizations.policy (\"%s\") is valid`, string(*validMinimalIDP.Spec.AllowAuthentication.Organizations.Policy))), buildLogForUpdatingHostValid("minimal-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is valid`, *validFilledOutIDP.Spec.GitHubAPI.Host), - buildLogForUpdatingTLSConfigurationValid("minimal-idp-name", "True", "Success", "spec.githubAPI.tls.certificateAuthorityData is valid"), + buildLogForUpdatingTLSConfigurationValid("minimal-idp-name", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), buildLogForUpdatingGitHubConnectionValid("minimal-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is reachable and TLS verification succeeds`, *validFilledOutIDP.Spec.GitHubAPI.Host), buildLogForUpdatingPhase("minimal-idp-name", "Error"), }, @@ -1685,7 +1689,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidTrue("minimal-idp-name"), buildLogForUpdatingOrganizationPolicyValid("minimal-idp-name", "True", "Success", fmt.Sprintf(`spec.allowAuthentication.organizations.policy (\"%s\") is valid`, string(*validMinimalIDP.Spec.AllowAuthentication.Organizations.Policy))), buildLogForUpdatingHostValid("minimal-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is valid`, *validMinimalIDP.Spec.GitHubAPI.Host), - buildLogForUpdatingTLSConfigurationValid("minimal-idp-name", "True", "Success", "spec.githubAPI.tls.certificateAuthorityData is valid"), + buildLogForUpdatingTLSConfigurationValid("minimal-idp-name", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), buildLogForUpdatingGitHubConnectionValid("minimal-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is reachable and TLS verification succeeds`, *validMinimalIDP.Spec.GitHubAPI.Host), buildLogForUpdatingPhase("minimal-idp-name", "Error"), }, @@ -1728,7 +1732,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidTrue("minimal-idp-name"), buildLogForUpdatingOrganizationPolicyValid("minimal-idp-name", "True", "Success", fmt.Sprintf(`spec.allowAuthentication.organizations.policy (\"%s\") is valid`, string(*validMinimalIDP.Spec.AllowAuthentication.Organizations.Policy))), buildLogForUpdatingHostValid("minimal-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is valid`, *validFilledOutIDP.Spec.GitHubAPI.Host), - buildLogForUpdatingTLSConfigurationValid("minimal-idp-name", "True", "Success", "spec.githubAPI.tls.certificateAuthorityData is valid"), + buildLogForUpdatingTLSConfigurationValid("minimal-idp-name", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), buildLogForUpdatingGitHubConnectionValid("minimal-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is reachable and TLS verification succeeds`, *validFilledOutIDP.Spec.GitHubAPI.Host), buildLogForUpdatingPhase("minimal-idp-name", "Error"), }, @@ -1771,7 +1775,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidTrue("minimal-idp-name"), buildLogForUpdatingOrganizationPolicyValid("minimal-idp-name", "True", "Success", fmt.Sprintf(`spec.allowAuthentication.organizations.policy (\"%s\") is valid`, string(*validMinimalIDP.Spec.AllowAuthentication.Organizations.Policy))), buildLogForUpdatingHostValid("minimal-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is valid`, *validMinimalIDP.Spec.GitHubAPI.Host), - buildLogForUpdatingTLSConfigurationValid("minimal-idp-name", "True", "Success", "spec.githubAPI.tls.certificateAuthorityData is valid"), + buildLogForUpdatingTLSConfigurationValid("minimal-idp-name", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), buildLogForUpdatingGitHubConnectionValid("minimal-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is reachable and TLS verification succeeds`, *validMinimalIDP.Spec.GitHubAPI.Host), buildLogForUpdatingPhase("minimal-idp-name", "Error"), }, @@ -1814,7 +1818,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidTrue("minimal-idp-name"), buildLogForUpdatingOrganizationPolicyValid("minimal-idp-name", "True", "Success", fmt.Sprintf(`spec.allowAuthentication.organizations.policy (\"%s\") is valid`, string(*validMinimalIDP.Spec.AllowAuthentication.Organizations.Policy))), buildLogForUpdatingHostValid("minimal-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is valid`, *validFilledOutIDP.Spec.GitHubAPI.Host), - buildLogForUpdatingTLSConfigurationValid("minimal-idp-name", "True", "Success", "spec.githubAPI.tls.certificateAuthorityData is valid"), + buildLogForUpdatingTLSConfigurationValid("minimal-idp-name", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), buildLogForUpdatingGitHubConnectionValid("minimal-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is reachable and TLS verification succeeds`, *validFilledOutIDP.Spec.GitHubAPI.Host), buildLogForUpdatingPhase("minimal-idp-name", "Error"), }, @@ -2085,7 +2089,7 @@ func TestController_OnlyWantActions(t *testing.T) { ObservedGeneration: 333, LastTransitionTime: oneHourAgo, Reason: conditionsutil.ReasonSuccess, - Message: "spec.githubAPI.tls.certificateAuthorityData is valid", + Message: "spec.githubAPI.tls is valid: loaded TLS configuration", }, }, }, @@ -2197,7 +2201,7 @@ func TestController_OnlyWantActions(t *testing.T) { ObservedGeneration: 1234, LastTransitionTime: wantLastTransitionTime, Reason: conditionsutil.ReasonSuccess, - Message: "spec.githubAPI.tls.certificateAuthorityData is valid", + Message: "spec.githubAPI.tls is valid: loaded TLS configuration", }, }, } @@ -2370,7 +2374,6 @@ func TestGitHubUpstreamWatcherControllerFilterSecret(t *testing.T) { } } -// TODO: make this test pass func TestGitHubUpstreamWatcherControllerFilterConfigMaps(t *testing.T) { namespace := "some-namespace" goodCM := &corev1.ConfigMap{ diff --git a/internal/controller/supervisorconfig/ldapupstreamwatcher/ldap_upstream_watcher.go b/internal/controller/supervisorconfig/ldapupstreamwatcher/ldap_upstream_watcher.go index 02476cbfb..a34b6992b 100644 --- a/internal/controller/supervisorconfig/ldapupstreamwatcher/ldap_upstream_watcher.go +++ b/internal/controller/supervisorconfig/ldapupstreamwatcher/ldap_upstream_watcher.go @@ -7,8 +7,8 @@ package ldapupstreamwatcher import ( "context" "fmt" - corev1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/equality" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" diff --git a/internal/controller/supervisorconfig/ldapupstreamwatcher/ldap_upstream_watcher_test.go b/internal/controller/supervisorconfig/ldapupstreamwatcher/ldap_upstream_watcher_test.go index 05dfdaf62..3dde9d235 100644 --- a/internal/controller/supervisorconfig/ldapupstreamwatcher/ldap_upstream_watcher_test.go +++ b/internal/controller/supervisorconfig/ldapupstreamwatcher/ldap_upstream_watcher_test.go @@ -335,13 +335,13 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { condPtr := func(c metav1.Condition) *metav1.Condition { return &c } - tlsConfigurationValidLoadedTrueCondition := func(gen int64) metav1.Condition { + tlsConfigurationValidLoadedTrueCondition := func(gen int64, msg string) metav1.Condition { return metav1.Condition{ Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", - Message: "loaded TLS configuration", + Message: fmt.Sprintf("spec.tls is valid: %s", msg), ObservedGeneration: gen, } } @@ -349,7 +349,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { return []metav1.Condition{ bindSecretValidTrueCondition(gen), ldapConnectionValidTrueCondition(gen, secretVersion), - tlsConfigurationValidLoadedTrueCondition(gen), + tlsConfigurationValidLoadedTrueCondition(gen, "loaded TLS configuration"), } } @@ -422,7 +422,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { Message: fmt.Sprintf(`secret "%s" not found`, testBindSecretName), ObservedGeneration: 1234, }, - tlsConfigurationValidLoadedTrueCondition(1234), + tlsConfigurationValidLoadedTrueCondition(1234, "loaded TLS configuration"), }, }, }}, @@ -450,7 +450,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { Message: fmt.Sprintf(`referenced Secret "%s" has wrong type "some-other-type" (should be "kubernetes.io/basic-auth")`, testBindSecretName), ObservedGeneration: 1234, }, - tlsConfigurationValidLoadedTrueCondition(1234), + tlsConfigurationValidLoadedTrueCondition(1234, "loaded TLS configuration"), }, }, }}, @@ -477,7 +477,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { Message: fmt.Sprintf(`referenced Secret "%s" is missing required keys ["username" "password"]`, testBindSecretName), ObservedGeneration: 1234, }, - tlsConfigurationValidLoadedTrueCondition(1234), + tlsConfigurationValidLoadedTrueCondition(1234, "loaded TLS configuration"), }, }, }}, @@ -501,7 +501,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { Status: "False", LastTransitionTime: now, Reason: "InvalidTLSConfig", - Message: "certificateAuthorityData is invalid: illegal base64 data at input byte 4", + Message: "spec.tls.certificateAuthorityData is invalid: illegal base64 data at input byte 4", ObservedGeneration: 1234, }, }, @@ -527,7 +527,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { Status: "False", LastTransitionTime: now, Reason: "InvalidTLSConfig", - Message: "certificateAuthorityData is invalid: no certificates found", + Message: "spec.tls.certificateAuthorityData is invalid: no certificates found", ObservedGeneration: 1234, }, }, @@ -580,7 +580,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { Status: "True", LastTransitionTime: now, Reason: "Success", - Message: "no TLS configuration provided", + Message: "spec.tls is valid: no TLS configuration provided", ObservedGeneration: 1234, }, }, @@ -649,7 +649,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { "ldap.example.com", testBindUsername, testBindSecretName, "4242"), ObservedGeneration: 1234, }, - tlsConfigurationValidLoadedTrueCondition(1234), + tlsConfigurationValidLoadedTrueCondition(1234, "loaded TLS configuration"), }, }, }}, @@ -722,7 +722,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { "ldap.example.com:5678", testBindUsername, "ldap.example.com:5678"), ObservedGeneration: 1234, }, - tlsConfigurationValidLoadedTrueCondition(1234), + tlsConfigurationValidLoadedTrueCondition(1234, "loaded TLS configuration"), }, }, }}, @@ -765,8 +765,12 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { wantResultingUpstreams: []idpv1alpha1.LDAPIdentityProvider{{ ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testName, Generation: 1234, UID: testResourceUID}, Status: idpv1alpha1.LDAPIdentityProviderStatus{ - Phase: "Ready", - Conditions: allConditionsTrue(1234, "4242"), + Phase: "Ready", + Conditions: []metav1.Condition{ + bindSecretValidTrueCondition(1234), + ldapConnectionValidTrueCondition(1234, "4242"), + tlsConfigurationValidLoadedTrueCondition(1234, "no TLS configuration provided"), + }, }, }}, wantValidatedSettings: map[string]upstreamwatchers.ValidatedSettings{testName: { @@ -808,7 +812,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { Message: fmt.Sprintf(`secret "%s" not found`, "non-existent-secret"), ObservedGeneration: 42, }, - tlsConfigurationValidLoadedTrueCondition(42), + tlsConfigurationValidLoadedTrueCondition(42, "loaded TLS configuration"), }, }, }, @@ -857,7 +861,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { testHost, testBindUsername, testBindUsername), ObservedGeneration: 1234, }, - tlsConfigurationValidLoadedTrueCondition(1234), + tlsConfigurationValidLoadedTrueCondition(1234, "loaded TLS configuration"), }, }, }}, @@ -1178,7 +1182,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { Status: "True", LastTransitionTime: now, Reason: "Success", - Message: "loaded TLS configuration", + Message: "spec.tls is valid: loaded TLS configuration", ObservedGeneration: 1234, }, }, diff --git a/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher.go b/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher.go index c711257b5..e49cc04f9 100644 --- a/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher.go +++ b/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher.go @@ -7,7 +7,6 @@ package oidcupstreamwatcher import ( "context" "crypto/x509" - "encoding/base64" "fmt" "net/http" "net/url" @@ -329,7 +328,7 @@ func (c *oidcWatcherController) validateIssuer(ctx context.Context, upstream *id discoveredProvider, httpClient := c.validatorCache.getProvider(&upstream.Spec) tlsCondition, _, certPool, _ := tlsconfigutil.ValidateTLSConfig( tlsconfigutil.TLSSpecForSupervisor(upstream.Spec.TLS), - "oidcIdentityProvider.spec.tls", + "spec.tls", upstream.Namespace, c.secretInformer, c.configMapInformer) @@ -466,24 +465,6 @@ func (c *oidcWatcherController) updateStatus(ctx context.Context, upstream *idpv } } -func getClient(upstream *idpv1alpha1.OIDCIdentityProvider) (*http.Client, error) { - if upstream.Spec.TLS == nil || upstream.Spec.TLS.CertificateAuthorityData == "" { - return defaultClientShortTimeout(nil), nil - } - - bundle, err := base64.StdEncoding.DecodeString(upstream.Spec.TLS.CertificateAuthorityData) - if err != nil { - return nil, fmt.Errorf("spec.certificateAuthorityData is invalid: %w", err) - } - - rootCAs := x509.NewCertPool() - if !rootCAs.AppendCertsFromPEM(bundle) { - return nil, fmt.Errorf("spec.certificateAuthorityData is invalid: %w", tlsconfigutil.ErrNoCertificates) - } - - return defaultClientShortTimeout(rootCAs), nil -} - func defaultClientShortTimeout(rootCAs *x509.CertPool) *http.Client { c := phttp.Default(rootCAs) c.Timeout = time.Minute diff --git a/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher_test.go b/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher_test.go index 376b72fea..cc5d33525 100644 --- a/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher_test.go +++ b/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher_test.go @@ -260,6 +260,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { wantLogs: []string{ `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"ClientCredentialsSecretValid","status":"False","reason":"SecretNotFound","message":"secret \"test-client-secret\" not found"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","status":"True","reason":"Success","message":"discovered issuer configuration"}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: loaded TLS configuration"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"AdditionalAuthorizeParametersValid","status":"True","reason":"Success","message":"additionalAuthorizeParameters parameter names are allowed"}`, `{"level":"error","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"oidcupstreamwatcher/oidc_upstream_watcher.go:$oidcupstreamwatcher.(*oidcWatcherController).validateUpstream","message":"found failing condition","namespace":"test-namespace","name":"test-name","type":"ClientCredentialsSecretValid","reason":"SecretNotFound","message":"secret \"test-client-secret\" not found","error":"OIDCIdentityProvider has a failing condition"}`, }, @@ -284,6 +285,13 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { Reason: "Success", Message: "discovered issuer configuration", }, + { + Type: "TLSConfigurationValid", + Status: "True", + LastTransitionTime: now, + Reason: "Success", + Message: "spec.tls is valid: loaded TLS configuration", + }, }, }, }}, @@ -307,6 +315,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { wantLogs: []string{ `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"ClientCredentialsSecretValid","status":"False","reason":"SecretWrongType","message":"referenced Secret \"test-client-secret\" has wrong type \"some-other-type\" (should be \"secrets.pinniped.dev/oidc-client\")"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","status":"True","reason":"Success","message":"discovered issuer configuration"}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: loaded TLS configuration"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"AdditionalAuthorizeParametersValid","status":"True","reason":"Success","message":"additionalAuthorizeParameters parameter names are allowed"}`, `{"level":"error","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"oidcupstreamwatcher/oidc_upstream_watcher.go:$oidcupstreamwatcher.(*oidcWatcherController).validateUpstream","message":"found failing condition","namespace":"test-namespace","name":"test-name","type":"ClientCredentialsSecretValid","reason":"SecretWrongType","message":"referenced Secret \"test-client-secret\" has wrong type \"some-other-type\" (should be \"secrets.pinniped.dev/oidc-client\")","error":"OIDCIdentityProvider has a failing condition"}`, }, @@ -331,6 +340,13 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { Reason: "Success", Message: "discovered issuer configuration", }, + { + Type: "TLSConfigurationValid", + Status: "True", + LastTransitionTime: now, + Reason: "Success", + Message: "spec.tls is valid: loaded TLS configuration", + }, }, }, }}, @@ -353,6 +369,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { wantLogs: []string{ `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"ClientCredentialsSecretValid","status":"False","reason":"SecretMissingKeys","message":"referenced Secret \"test-client-secret\" is missing required keys [\"clientID\" \"clientSecret\"]"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","status":"True","reason":"Success","message":"discovered issuer configuration"}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: loaded TLS configuration"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"AdditionalAuthorizeParametersValid","status":"True","reason":"Success","message":"additionalAuthorizeParameters parameter names are allowed"}`, `{"level":"error","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"oidcupstreamwatcher/oidc_upstream_watcher.go:$oidcupstreamwatcher.(*oidcWatcherController).validateUpstream","message":"found failing condition","namespace":"test-namespace","name":"test-name","type":"ClientCredentialsSecretValid","reason":"SecretMissingKeys","message":"referenced Secret \"test-client-secret\" is missing required keys [\"clientID\" \"clientSecret\"]","error":"OIDCIdentityProvider has a failing condition"}`, }, @@ -377,6 +394,13 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { Reason: "Success", Message: "discovered issuer configuration", }, + { + Type: "TLSConfigurationValid", + Status: "True", + LastTransitionTime: now, + Reason: "Success", + Message: "spec.tls is valid: loaded TLS configuration", + }, }, }, }}, @@ -401,9 +425,11 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { wantErr: controllerlib.ErrSyntheticRequeue.Error(), wantLogs: []string{ `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"ClientCredentialsSecretValid","status":"True","reason":"Success","message":"loaded client credentials"}`, - `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","status":"False","reason":"InvalidTLSConfig","message":"spec.certificateAuthorityData is invalid: illegal base64 data at input byte 7"}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","status":"False","reason":"InvalidTLSConfig","message":"spec.tls.certificateAuthorityData is invalid: illegal base64 data at input byte 7"}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"False","reason":"InvalidTLSConfig","message":"spec.tls.certificateAuthorityData is invalid: illegal base64 data at input byte 7"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"AdditionalAuthorizeParametersValid","status":"True","reason":"Success","message":"additionalAuthorizeParameters parameter names are allowed"}`, - `{"level":"error","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"oidcupstreamwatcher/oidc_upstream_watcher.go:$oidcupstreamwatcher.(*oidcWatcherController).validateUpstream","message":"found failing condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","reason":"InvalidTLSConfig","message":"spec.certificateAuthorityData is invalid: illegal base64 data at input byte 7","error":"OIDCIdentityProvider has a failing condition"}`, + `{"level":"error","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"oidcupstreamwatcher/oidc_upstream_watcher.go:$oidcupstreamwatcher.(*oidcWatcherController).validateUpstream","message":"found failing condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","reason":"InvalidTLSConfig","message":"spec.tls.certificateAuthorityData is invalid: illegal base64 data at input byte 7","error":"OIDCIdentityProvider has a failing condition"}`, + `{"level":"error","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"oidcupstreamwatcher/oidc_upstream_watcher.go:$oidcupstreamwatcher.(*oidcWatcherController).validateUpstream","message":"found failing condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","reason":"InvalidTLSConfig","message":"spec.tls.certificateAuthorityData is invalid: illegal base64 data at input byte 7","error":"OIDCIdentityProvider has a failing condition"}`, }, wantResultingCache: []*oidctestutil.TestUpstreamOIDCIdentityProvider{}, wantResultingUpstreams: []idpv1alpha1.OIDCIdentityProvider{{ @@ -424,7 +450,14 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { Status: "False", LastTransitionTime: now, Reason: "InvalidTLSConfig", - Message: `spec.certificateAuthorityData is invalid: illegal base64 data at input byte 7`, + Message: `spec.tls.certificateAuthorityData is invalid: illegal base64 data at input byte 7`, + }, + { + Type: "TLSConfigurationValid", + Status: "False", + LastTransitionTime: now, + Reason: "InvalidTLSConfig", + Message: "spec.tls.certificateAuthorityData is invalid: illegal base64 data at input byte 7", }, }, }, @@ -450,9 +483,11 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { wantErr: controllerlib.ErrSyntheticRequeue.Error(), wantLogs: []string{ `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"ClientCredentialsSecretValid","status":"True","reason":"Success","message":"loaded client credentials"}`, - `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","status":"False","reason":"InvalidTLSConfig","message":"spec.certificateAuthorityData is invalid: no certificates found"}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","status":"False","reason":"InvalidTLSConfig","message":"spec.tls.certificateAuthorityData is invalid: no certificates found"}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"False","reason":"InvalidTLSConfig","message":"spec.tls.certificateAuthorityData is invalid: no certificates found"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"AdditionalAuthorizeParametersValid","status":"True","reason":"Success","message":"additionalAuthorizeParameters parameter names are allowed"}`, - `{"level":"error","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"oidcupstreamwatcher/oidc_upstream_watcher.go:$oidcupstreamwatcher.(*oidcWatcherController).validateUpstream","message":"found failing condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","reason":"InvalidTLSConfig","message":"spec.certificateAuthorityData is invalid: no certificates found","error":"OIDCIdentityProvider has a failing condition"}`, + `{"level":"error","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"oidcupstreamwatcher/oidc_upstream_watcher.go:$oidcupstreamwatcher.(*oidcWatcherController).validateUpstream","message":"found failing condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","reason":"InvalidTLSConfig","message":"spec.tls.certificateAuthorityData is invalid: no certificates found","error":"OIDCIdentityProvider has a failing condition"}`, + `{"level":"error","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"oidcupstreamwatcher/oidc_upstream_watcher.go:$oidcupstreamwatcher.(*oidcWatcherController).validateUpstream","message":"found failing condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","reason":"InvalidTLSConfig","message":"spec.tls.certificateAuthorityData is invalid: no certificates found","error":"OIDCIdentityProvider has a failing condition"}`, }, wantResultingCache: []*oidctestutil.TestUpstreamOIDCIdentityProvider{}, wantResultingUpstreams: []idpv1alpha1.OIDCIdentityProvider{{ @@ -473,7 +508,14 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { Status: "False", LastTransitionTime: now, Reason: "InvalidTLSConfig", - Message: `spec.certificateAuthorityData is invalid: no certificates found`, + Message: `spec.tls.certificateAuthorityData is invalid: no certificates found`, + }, + { + Type: "TLSConfigurationValid", + Status: "False", + LastTransitionTime: now, + Reason: "InvalidTLSConfig", + Message: "spec.tls.certificateAuthorityData is invalid: no certificates found", }, }, }, @@ -497,6 +539,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { wantLogs: []string{ `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"ClientCredentialsSecretValid","status":"True","reason":"Success","message":"loaded client credentials"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","status":"False","reason":"Unreachable","message":"failed to parse issuer URL: parse \"%invalid-url-that-is-really-really-long-nanananananananannanananan-batman-nanananananananananananananana-batman-lalalalalalalalalal-batman-weeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\": invalid URL escape \"%in\""}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: no TLS configuration provided"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"AdditionalAuthorizeParametersValid","status":"True","reason":"Success","message":"additionalAuthorizeParameters parameter names are allowed"}`, `{"level":"error","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"oidcupstreamwatcher/oidc_upstream_watcher.go:$oidcupstreamwatcher.(*oidcWatcherController).validateUpstream","message":"found failing condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","reason":"Unreachable","message":"failed to parse issuer URL: parse \"%invalid-url-that-is-really-really-long-nanananananananannanananan-batman-nanananananananananananananana-batman-lalalalalalalalalal-batman-weeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\": invalid URL escape \"%in\"","error":"OIDCIdentityProvider has a failing condition"}`, }, @@ -521,6 +564,13 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { Reason: "Unreachable", Message: `failed to parse issuer URL: parse "%invalid-url-that-is-really-really-long-nanananananananannanananan-batman-nanananananananananananananana-batman-lalalalalalalalalal-batman-weeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee": invalid URL escape "%in"`, }, + { + Type: "TLSConfigurationValid", + Status: "True", + LastTransitionTime: now, + Reason: "Success", + Message: "spec.tls is valid: no TLS configuration provided", + }, }, }, }}, @@ -543,6 +593,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { wantLogs: []string{ `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"ClientCredentialsSecretValid","status":"True","reason":"Success","message":"loaded client credentials"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","status":"False","reason":"Unreachable","message":"issuer URL '` + strings.Replace(testIssuerURL, "https", "http", 1) + `' must have \"https\" scheme, not \"http\""}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: no TLS configuration provided"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"AdditionalAuthorizeParametersValid","status":"True","reason":"Success","message":"additionalAuthorizeParameters parameter names are allowed"}`, `{"level":"error","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"oidcupstreamwatcher/oidc_upstream_watcher.go:$oidcupstreamwatcher.(*oidcWatcherController).validateUpstream","message":"found failing condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","reason":"Unreachable","message":"issuer URL '` + strings.Replace(testIssuerURL, "https", "http", 1) + `' must have \"https\" scheme, not \"http\"","error":"OIDCIdentityProvider has a failing condition"}`, }, @@ -567,6 +618,13 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { Reason: "Unreachable", Message: `issuer URL '` + strings.Replace(testIssuerURL, "https", "http", 1) + `' must have "https" scheme, not "http"`, }, + { + Type: "TLSConfigurationValid", + Status: "True", + LastTransitionTime: now, + Reason: "Success", + Message: "spec.tls is valid: no TLS configuration provided", + }, }, }, }}, @@ -589,6 +647,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { wantLogs: []string{ `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"ClientCredentialsSecretValid","status":"True","reason":"Success","message":"loaded client credentials"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","status":"False","reason":"Unreachable","message":"issuer URL '` + testIssuerURL + `?sub=foo' cannot contain query or fragment component"}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: no TLS configuration provided"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"AdditionalAuthorizeParametersValid","status":"True","reason":"Success","message":"additionalAuthorizeParameters parameter names are allowed"}`, `{"level":"error","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"oidcupstreamwatcher/oidc_upstream_watcher.go:$oidcupstreamwatcher.(*oidcWatcherController).validateUpstream","message":"found failing condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","reason":"Unreachable","message":"issuer URL '` + testIssuerURL + `?sub=foo' cannot contain query or fragment component","error":"OIDCIdentityProvider has a failing condition"}`, }, @@ -613,6 +672,13 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { Reason: "Unreachable", Message: `issuer URL '` + testIssuerURL + "?sub=foo" + `' cannot contain query or fragment component`, }, + { + Type: "TLSConfigurationValid", + Status: "True", + LastTransitionTime: now, + Reason: "Success", + Message: "spec.tls is valid: no TLS configuration provided", + }, }, }, }}, @@ -635,6 +701,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { wantLogs: []string{ `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"ClientCredentialsSecretValid","status":"True","reason":"Success","message":"loaded client credentials"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","status":"False","reason":"Unreachable","message":"issuer URL '` + testIssuerURL + `#fragment' cannot contain query or fragment component"}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: no TLS configuration provided"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"AdditionalAuthorizeParametersValid","status":"True","reason":"Success","message":"additionalAuthorizeParameters parameter names are allowed"}`, `{"level":"error","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"oidcupstreamwatcher/oidc_upstream_watcher.go:$oidcupstreamwatcher.(*oidcWatcherController).validateUpstream","message":"found failing condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","reason":"Unreachable","message":"issuer URL '` + testIssuerURL + `#fragment' cannot contain query or fragment component","error":"OIDCIdentityProvider has a failing condition"}`, }, @@ -659,6 +726,13 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { Reason: "Unreachable", Message: `issuer URL '` + testIssuerURL + "#fragment" + `' cannot contain query or fragment component`, }, + { + Type: "TLSConfigurationValid", + Status: "True", + LastTransitionTime: now, + Reason: "Success", + Message: "spec.tls is valid: no TLS configuration provided", + }, }, }, }}, @@ -683,6 +757,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { `{"level":"error","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"oidcupstreamwatcher/oidc_upstream_watcher.go:$oidcupstreamwatcher.(*oidcWatcherController).validateIssuer","message":"failed to perform OIDC discovery","namespace":"test-namespace","name":"test-name","issuer":"` + testIssuerURL + `/valid-url-that-is-really-really-long-nanananananananannanananan-batman-nanananananananananananananana-batman-lalalalalalalalalal-batman-weeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee","error":"Get \"` + testIssuerURL + `/valid-url-that-is-really-really-long-nanananananananannanananan-batman-nanananananananananananananana-batman-lalalalalalalalalal-batman-weeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee/.well-known/openid-configuration\": tls: failed to verify certificate: x509: certificate signed by unknown authority"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"ClientCredentialsSecretValid","status":"True","reason":"Success","message":"loaded client credentials"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","status":"False","reason":"Unreachable","message":"failed to perform OIDC discovery against \"` + testIssuerURL + `/valid-url-that-is-really-really-long-nanananananananannanananan-batman-nanananananananananananananana-batman-lalalalalalalalalal-batman-weeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\":\nGet \"` + testIssuerURL + `/valid-url-that-is-really-really-long-nanananananananannanananan-batman-nanananananananananananananana-batman-lalalalalalalalalal-batman-weeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee/.well-known/openid-configuration\": tls: failed to verify certificate: x509: certificate signed by unknown authority"}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: loaded TLS configuration"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"AdditionalAuthorizeParametersValid","status":"True","reason":"Success","message":"additionalAuthorizeParameters parameter names are allowed"}`, `{"level":"error","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"oidcupstreamwatcher/oidc_upstream_watcher.go:$oidcupstreamwatcher.(*oidcWatcherController).validateUpstream","message":"found failing condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","reason":"Unreachable","message":"failed to perform OIDC discovery against \"` + testIssuerURL + `/valid-url-that-is-really-really-long-nanananananananannanananan-batman-nanananananananananananananana-batman-lalalalalalalalalal-batman-weeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\":\nGet \"` + testIssuerURL + `/valid-url-that-is-really-really-long-nanananananananannanananan-batman-nanananananananananananananana-batman-lalalalalalalalalal-batman-weeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee/.well-known/openid-configuration\": tls: failed to verify certificate: x509: certificate signed by unknown authority","error":"OIDCIdentityProvider has a failing condition"}`, }, @@ -708,6 +783,13 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { Message: `failed to perform OIDC discovery against "` + testIssuerURL + `/valid-url-that-is-really-really-long-nanananananananannanananan-batman-nanananananananananananananana-batman-lalalalalalalalalal-batman-weeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee": Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nanananananananannanananan-batman-nanananananananananananananana-batman-lalalalalalalalalal-batman-weeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee/.well-known/openid-configuration": tls: failed to verify certificate: x509: certificate signed by unknown authority`, }, + { + Type: "TLSConfigurationValid", + Status: "True", + LastTransitionTime: now, + Reason: "Success", + Message: "spec.tls is valid: loaded TLS configuration", + }, }, }, }}, @@ -731,6 +813,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana wantLogs: []string{ `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"ClientCredentialsSecretValid","status":"True","reason":"Success","message":"loaded client credentials"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","status":"False","reason":"InvalidResponse","message":"failed to parse authorization endpoint URL: parse \"%\": invalid URL escape \"%\""}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: loaded TLS configuration"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"AdditionalAuthorizeParametersValid","status":"True","reason":"Success","message":"additionalAuthorizeParameters parameter names are allowed"}`, `{"level":"error","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"oidcupstreamwatcher/oidc_upstream_watcher.go:$oidcupstreamwatcher.(*oidcWatcherController).validateUpstream","message":"found failing condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","reason":"InvalidResponse","message":"failed to parse authorization endpoint URL: parse \"%\": invalid URL escape \"%\"","error":"OIDCIdentityProvider has a failing condition"}`, }, @@ -755,6 +838,13 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana Reason: "InvalidResponse", Message: `failed to parse authorization endpoint URL: parse "%": invalid URL escape "%"`, }, + { + Type: "TLSConfigurationValid", + Status: "True", + LastTransitionTime: now, + Reason: "Success", + Message: "spec.tls is valid: loaded TLS configuration", + }, }, }, }}, @@ -778,6 +868,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana wantLogs: []string{ `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"ClientCredentialsSecretValid","status":"True","reason":"Success","message":"loaded client credentials"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","status":"False","reason":"InvalidResponse","message":"failed to parse revocation endpoint URL: parse \"%\": invalid URL escape \"%\""}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: loaded TLS configuration"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"AdditionalAuthorizeParametersValid","status":"True","reason":"Success","message":"additionalAuthorizeParameters parameter names are allowed"}`, `{"level":"error","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"oidcupstreamwatcher/oidc_upstream_watcher.go:$oidcupstreamwatcher.(*oidcWatcherController).validateUpstream","message":"found failing condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","reason":"InvalidResponse","message":"failed to parse revocation endpoint URL: parse \"%\": invalid URL escape \"%\"","error":"OIDCIdentityProvider has a failing condition"}`, }, @@ -802,6 +893,13 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana Reason: "InvalidResponse", Message: `failed to parse revocation endpoint URL: parse "%": invalid URL escape "%"`, }, + { + Type: "TLSConfigurationValid", + Status: "True", + LastTransitionTime: now, + Reason: "Success", + Message: "spec.tls is valid: loaded TLS configuration", + }, }, }, }}, @@ -825,6 +923,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana wantLogs: []string{ `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"ClientCredentialsSecretValid","status":"True","reason":"Success","message":"loaded client credentials"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","status":"False","reason":"InvalidResponse","message":"authorization endpoint URL 'http://example.com/authorize' must have \"https\" scheme, not \"http\""}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: loaded TLS configuration"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"AdditionalAuthorizeParametersValid","status":"True","reason":"Success","message":"additionalAuthorizeParameters parameter names are allowed"}`, `{"level":"error","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"oidcupstreamwatcher/oidc_upstream_watcher.go:$oidcupstreamwatcher.(*oidcWatcherController).validateUpstream","message":"found failing condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","reason":"InvalidResponse","message":"authorization endpoint URL 'http://example.com/authorize' must have \"https\" scheme, not \"http\"","error":"OIDCIdentityProvider has a failing condition"}`, }, @@ -849,6 +948,13 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana Reason: "InvalidResponse", Message: `authorization endpoint URL 'http://example.com/authorize' must have "https" scheme, not "http"`, }, + { + Type: "TLSConfigurationValid", + Status: "True", + LastTransitionTime: now, + Reason: "Success", + Message: "spec.tls is valid: loaded TLS configuration", + }, }, }, }}, @@ -872,6 +978,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana wantLogs: []string{ `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"ClientCredentialsSecretValid","status":"True","reason":"Success","message":"loaded client credentials"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","status":"False","reason":"InvalidResponse","message":"revocation endpoint URL 'http://example.com/revoke' must have \"https\" scheme, not \"http\""}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: loaded TLS configuration"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"AdditionalAuthorizeParametersValid","status":"True","reason":"Success","message":"additionalAuthorizeParameters parameter names are allowed"}`, `{"level":"error","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"oidcupstreamwatcher/oidc_upstream_watcher.go:$oidcupstreamwatcher.(*oidcWatcherController).validateUpstream","message":"found failing condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","reason":"InvalidResponse","message":"revocation endpoint URL 'http://example.com/revoke' must have \"https\" scheme, not \"http\"","error":"OIDCIdentityProvider has a failing condition"}`, }, @@ -896,6 +1003,13 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana Reason: "InvalidResponse", Message: `revocation endpoint URL 'http://example.com/revoke' must have "https" scheme, not "http"`, }, + { + Type: "TLSConfigurationValid", + Status: "True", + LastTransitionTime: now, + Reason: "Success", + Message: "spec.tls is valid: loaded TLS configuration", + }, }, }, }}, @@ -919,6 +1033,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana wantLogs: []string{ `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"ClientCredentialsSecretValid","status":"True","reason":"Success","message":"loaded client credentials"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","status":"False","reason":"InvalidResponse","message":"token endpoint URL 'http://example.com/token' must have \"https\" scheme, not \"http\""}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: loaded TLS configuration"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"AdditionalAuthorizeParametersValid","status":"True","reason":"Success","message":"additionalAuthorizeParameters parameter names are allowed"}`, `{"level":"error","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"oidcupstreamwatcher/oidc_upstream_watcher.go:$oidcupstreamwatcher.(*oidcWatcherController).validateUpstream","message":"found failing condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","reason":"InvalidResponse","message":"token endpoint URL 'http://example.com/token' must have \"https\" scheme, not \"http\"","error":"OIDCIdentityProvider has a failing condition"}`, }, @@ -943,6 +1058,13 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana Reason: "InvalidResponse", Message: `token endpoint URL 'http://example.com/token' must have "https" scheme, not "http"`, }, + { + Type: "TLSConfigurationValid", + Status: "True", + LastTransitionTime: now, + Reason: "Success", + Message: "spec.tls is valid: loaded TLS configuration", + }, }, }, }}, @@ -966,6 +1088,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana wantLogs: []string{ `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"ClientCredentialsSecretValid","status":"True","reason":"Success","message":"loaded client credentials"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","status":"False","reason":"InvalidResponse","message":"token endpoint URL '' must have \"https\" scheme, not \"\""}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: loaded TLS configuration"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"AdditionalAuthorizeParametersValid","status":"True","reason":"Success","message":"additionalAuthorizeParameters parameter names are allowed"}`, `{"level":"error","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"oidcupstreamwatcher/oidc_upstream_watcher.go:$oidcupstreamwatcher.(*oidcWatcherController).validateUpstream","message":"found failing condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","reason":"InvalidResponse","message":"token endpoint URL '' must have \"https\" scheme, not \"\"","error":"OIDCIdentityProvider has a failing condition"}`, }, @@ -990,6 +1113,13 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana Reason: "InvalidResponse", Message: `token endpoint URL '' must have "https" scheme, not ""`, }, + { + Type: "TLSConfigurationValid", + Status: "True", + LastTransitionTime: now, + Reason: "Success", + Message: "spec.tls is valid: loaded TLS configuration", + }, }, }, }}, @@ -1013,6 +1143,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana wantLogs: []string{ `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"ClientCredentialsSecretValid","status":"True","reason":"Success","message":"loaded client credentials"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","status":"False","reason":"InvalidResponse","message":"authorization endpoint URL '' must have \"https\" scheme, not \"\""}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: loaded TLS configuration"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"AdditionalAuthorizeParametersValid","status":"True","reason":"Success","message":"additionalAuthorizeParameters parameter names are allowed"}`, `{"level":"error","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"oidcupstreamwatcher/oidc_upstream_watcher.go:$oidcupstreamwatcher.(*oidcWatcherController).validateUpstream","message":"found failing condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","reason":"InvalidResponse","message":"authorization endpoint URL '' must have \"https\" scheme, not \"\"","error":"OIDCIdentityProvider has a failing condition"}`, }, @@ -1037,6 +1168,13 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana Reason: "InvalidResponse", Message: `authorization endpoint URL '' must have "https" scheme, not ""`, }, + { + Type: "TLSConfigurationValid", + Status: "True", + LastTransitionTime: now, + Reason: "Success", + Message: "spec.tls is valid: loaded TLS configuration", + }, }, }, }}, @@ -1071,6 +1209,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana wantLogs: []string{ `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"ClientCredentialsSecretValid","status":"True","reason":"Success","message":"loaded client credentials"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","status":"True","reason":"Success","message":"discovered issuer configuration"}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: loaded TLS configuration"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"AdditionalAuthorizeParametersValid","status":"True","reason":"Success","message":"additionalAuthorizeParameters parameter names are allowed"}`, }, wantResultingCache: []*oidctestutil.TestUpstreamOIDCIdentityProvider{ @@ -1096,6 +1235,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana happyAdditionalAuthorizeParametersValidCondition, {Type: "ClientCredentialsSecretValid", Status: "True", LastTransitionTime: now, Reason: "Success", Message: "loaded client credentials"}, {Type: "OIDCDiscoverySucceeded", Status: "True", LastTransitionTime: now, Reason: "Success", Message: "discovered issuer configuration"}, + {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", Message: "spec.tls is valid: loaded TLS configuration"}, }, }, }}, @@ -1127,6 +1267,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana wantLogs: []string{ `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"ClientCredentialsSecretValid","status":"True","reason":"Success","message":"loaded client credentials"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","status":"True","reason":"Success","message":"discovered issuer configuration"}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: loaded TLS configuration"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"AdditionalAuthorizeParametersValid","status":"True","reason":"Success","message":"additionalAuthorizeParameters parameter names are allowed"}`, }, wantResultingCache: []*oidctestutil.TestUpstreamOIDCIdentityProvider{ @@ -1152,6 +1293,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana {Type: "AdditionalAuthorizeParametersValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "additionalAuthorizeParameters parameter names are allowed", ObservedGeneration: 1234}, {Type: "ClientCredentialsSecretValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "loaded client credentials", ObservedGeneration: 1234}, {Type: "OIDCDiscoverySucceeded", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "discovered issuer configuration", ObservedGeneration: 1234}, + {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", Message: "spec.tls is valid: loaded TLS configuration", ObservedGeneration: 1234}, }, }, }}, @@ -1183,6 +1325,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana wantLogs: []string{ `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"ClientCredentialsSecretValid","status":"True","reason":"Success","message":"loaded client credentials"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","status":"True","reason":"Success","message":"discovered issuer configuration"}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: loaded TLS configuration"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"AdditionalAuthorizeParametersValid","status":"True","reason":"Success","message":"additionalAuthorizeParameters parameter names are allowed"}`, }, wantResultingCache: []*oidctestutil.TestUpstreamOIDCIdentityProvider{ @@ -1208,6 +1351,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana {Type: "AdditionalAuthorizeParametersValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "additionalAuthorizeParameters parameter names are allowed", ObservedGeneration: 1234}, {Type: "ClientCredentialsSecretValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "loaded client credentials", ObservedGeneration: 1234}, {Type: "OIDCDiscoverySucceeded", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "discovered issuer configuration", ObservedGeneration: 1234}, + {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", Message: "spec.tls is valid: loaded TLS configuration", ObservedGeneration: 1234}, }, }, }}, @@ -1242,6 +1386,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana wantLogs: []string{ `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"ClientCredentialsSecretValid","status":"True","reason":"Success","message":"loaded client credentials"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","status":"True","reason":"Success","message":"discovered issuer configuration"}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: loaded TLS configuration"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"AdditionalAuthorizeParametersValid","status":"True","reason":"Success","message":"additionalAuthorizeParameters parameter names are allowed"}`, }, wantResultingCache: []*oidctestutil.TestUpstreamOIDCIdentityProvider{ @@ -1267,6 +1412,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana {Type: "AdditionalAuthorizeParametersValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "additionalAuthorizeParameters parameter names are allowed", ObservedGeneration: 1234}, {Type: "ClientCredentialsSecretValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "loaded client credentials", ObservedGeneration: 1234}, {Type: "OIDCDiscoverySucceeded", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "discovered issuer configuration", ObservedGeneration: 1234}, + {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", Message: "spec.tls is valid: loaded TLS configuration", ObservedGeneration: 1234}, }, }, }}, @@ -1309,6 +1455,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana wantLogs: []string{ `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"ClientCredentialsSecretValid","status":"True","reason":"Success","message":"loaded client credentials"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","status":"True","reason":"Success","message":"discovered issuer configuration"}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: loaded TLS configuration"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"AdditionalAuthorizeParametersValid","status":"True","reason":"Success","message":"additionalAuthorizeParameters parameter names are allowed"}`, }, wantResultingCache: []*oidctestutil.TestUpstreamOIDCIdentityProvider{ @@ -1336,6 +1483,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana {Type: "AdditionalAuthorizeParametersValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "additionalAuthorizeParameters parameter names are allowed", ObservedGeneration: 1234}, {Type: "ClientCredentialsSecretValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "loaded client credentials", ObservedGeneration: 1234}, {Type: "OIDCDiscoverySucceeded", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "discovered issuer configuration", ObservedGeneration: 1234}, + {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", Message: "spec.tls is valid: loaded TLS configuration", ObservedGeneration: 1234}, }, }, }}, @@ -1373,6 +1521,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana wantLogs: []string{ `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"ClientCredentialsSecretValid","status":"True","reason":"Success","message":"loaded client credentials"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","status":"True","reason":"Success","message":"discovered issuer configuration"}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: loaded TLS configuration"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"AdditionalAuthorizeParametersValid","status":"False","reason":"DisallowedParameterName","message":"the following additionalAuthorizeParameters are not allowed: response_type,scope,client_id,state,nonce,code_challenge,code_challenge_method,redirect_uri,hd"}`, `{"level":"error","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"oidcupstreamwatcher/oidc_upstream_watcher.go:$oidcupstreamwatcher.(*oidcWatcherController).validateUpstream","message":"found failing condition","namespace":"test-namespace","name":"test-name","type":"AdditionalAuthorizeParametersValid","reason":"DisallowedParameterName","message":"the following additionalAuthorizeParameters are not allowed: response_type,scope,client_id,state,nonce,code_challenge,code_challenge_method,redirect_uri,hd","error":"OIDCIdentityProvider has a failing condition"}`, }, @@ -1387,6 +1536,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana "response_type,scope,client_id,state,nonce,code_challenge,code_challenge_method,redirect_uri,hd", ObservedGeneration: 1234}, {Type: "ClientCredentialsSecretValid", Status: "True", LastTransitionTime: now, Reason: "Success", Message: "loaded client credentials", ObservedGeneration: 1234}, {Type: "OIDCDiscoverySucceeded", Status: "True", LastTransitionTime: now, Reason: "Success", Message: "discovered issuer configuration", ObservedGeneration: 1234}, + {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", Message: "spec.tls is valid: loaded TLS configuration", ObservedGeneration: 1234}, }, }, }}, @@ -1411,6 +1561,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana `{"level":"error","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"oidcupstreamwatcher/oidc_upstream_watcher.go:$oidcupstreamwatcher.(*oidcWatcherController).validateIssuer","message":"failed to perform OIDC discovery","namespace":"test-namespace","name":"test-name","issuer":"` + testIssuerURL + `/ends-with-slash","error":"oidc: issuer did not match the issuer returned by provider, expected \"` + testIssuerURL + `/ends-with-slash\" got \"` + testIssuerURL + `/ends-with-slash/\""}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"ClientCredentialsSecretValid","status":"True","reason":"Success","message":"loaded client credentials"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","status":"False","reason":"Unreachable","message":"failed to perform OIDC discovery against \"` + testIssuerURL + `/ends-with-slash\":\noidc: issuer did not match the issuer returned by provider, expected \"` + testIssuerURL + `/ends-with-slash\" got \"` + testIssuerURL + `/ends-with-slash/\""}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: loaded TLS configuration"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"AdditionalAuthorizeParametersValid","status":"True","reason":"Success","message":"additionalAuthorizeParameters parameter names are allowed"}`, `{"level":"error","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"oidcupstreamwatcher/oidc_upstream_watcher.go:$oidcupstreamwatcher.(*oidcWatcherController).validateUpstream","message":"found failing condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","reason":"Unreachable","message":"failed to perform OIDC discovery against \"` + testIssuerURL + `/ends-with-slash\":\noidc: issuer did not match the issuer returned by provider, expected \"` + testIssuerURL + `/ends-with-slash\" got \"` + testIssuerURL + `/ends-with-slash/\"","error":"OIDCIdentityProvider has a failing condition"}`, }, @@ -1436,6 +1587,13 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana Message: `failed to perform OIDC discovery against "` + testIssuerURL + `/ends-with-slash": oidc: issuer did not match the issuer returned by provider, expected "` + testIssuerURL + `/ends-with-slash" got "` + testIssuerURL + `/ends-with-slash/"`, }, + { + Type: "TLSConfigurationValid", + Status: "True", + LastTransitionTime: now, + Reason: "Success", + Message: "spec.tls is valid: loaded TLS configuration", + }, }, }, }}, @@ -1460,6 +1618,7 @@ oidc: issuer did not match the issuer returned by provider, expected "` + testIs `{"level":"error","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"oidcupstreamwatcher/oidc_upstream_watcher.go:$oidcupstreamwatcher.(*oidcWatcherController).validateIssuer","message":"failed to perform OIDC discovery","namespace":"test-namespace","name":"test-name","issuer":"` + testIssuerURL + `/","error":"oidc: issuer did not match the issuer returned by provider, expected \"` + testIssuerURL + `/\" got \"` + testIssuerURL + `\""}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"ClientCredentialsSecretValid","status":"True","reason":"Success","message":"loaded client credentials"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","status":"False","reason":"Unreachable","message":"failed to perform OIDC discovery against \"` + testIssuerURL + `/\":\noidc: issuer did not match the issuer returned by provider, expected \"` + testIssuerURL + `/\" got \"` + testIssuerURL + `\""}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: loaded TLS configuration"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"AdditionalAuthorizeParametersValid","status":"True","reason":"Success","message":"additionalAuthorizeParameters parameter names are allowed"}`, `{"level":"error","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"oidcupstreamwatcher/oidc_upstream_watcher.go:$oidcupstreamwatcher.(*oidcWatcherController).validateUpstream","message":"found failing condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","reason":"Unreachable","message":"failed to perform OIDC discovery against \"` + testIssuerURL + `/\":\noidc: issuer did not match the issuer returned by provider, expected \"` + testIssuerURL + `/\" got \"` + testIssuerURL + `\"","error":"OIDCIdentityProvider has a failing condition"}`, }, @@ -1485,6 +1644,13 @@ oidc: issuer did not match the issuer returned by provider, expected "` + testIs Message: `failed to perform OIDC discovery against "` + testIssuerURL + `/": oidc: issuer did not match the issuer returned by provider, expected "` + testIssuerURL + `/" got "` + testIssuerURL + `"`, }, + { + Type: "TLSConfigurationValid", + Status: "True", + LastTransitionTime: now, + Reason: "Success", + Message: "spec.tls is valid: loaded TLS configuration", + }, }, }, }}, diff --git a/internal/controller/supervisorconfig/upstreamwatchers/upstream_watchers.go b/internal/controller/supervisorconfig/upstreamwatchers/upstream_watchers.go index 35cdc05b0..5d949e011 100644 --- a/internal/controller/supervisorconfig/upstreamwatchers/upstream_watchers.go +++ b/internal/controller/supervisorconfig/upstreamwatchers/upstream_watchers.go @@ -254,7 +254,7 @@ func ValidateGenericLDAP( conditions.Append(secretValidCondition, true) tlsSpec := tlsconfigutil.TLSSpecForSupervisor(upstream.Spec().TLSSpec()) - tlsValidCondition, caBundle, _, _ := tlsconfigutil.ValidateTLSConfig(tlsSpec, "", upstream.Namespace(), secretInformer, configMapInformer) + tlsValidCondition, caBundle, _, _ := tlsconfigutil.ValidateTLSConfig(tlsSpec, "spec.tls", upstream.Namespace(), secretInformer, configMapInformer) conditions.Append(tlsValidCondition, true) config.CABundle = caBundle diff --git a/internal/controller/tlsconfigutil/tls_config_util.go b/internal/controller/tlsconfigutil/tls_config_util.go index 2f1883757..74278a608 100644 --- a/internal/controller/tlsconfigutil/tls_config_util.go +++ b/internal/controller/tlsconfigutil/tls_config_util.go @@ -11,10 +11,10 @@ import ( "github.com/pkg/errors" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/informers/core/v1" + corev1informers "k8s.io/client-go/informers/core/v1" - conciergev1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/authentication/v1alpha1" - supervisorv1alpha1 "go.pinniped.dev/generated/latest/apis/supervisor/idp/v1alpha1" + authenticationv1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/authentication/v1alpha1" + idpv1alpha1 "go.pinniped.dev/generated/latest/apis/supervisor/idp/v1alpha1" "go.pinniped.dev/internal/constable" "go.pinniped.dev/internal/controller/conditionsutil" ) @@ -36,7 +36,7 @@ type caBundleSource struct { } // TLSSpec unifies the TLSSpec type that Supervisor and Concierge both individually define. -// unifying these two definitions to allow sharing code that will read the spec and translate it into a CA bundle +// unifying these two definitions to allow sharing code that will read the spec and translate it into a CA bundle. type TLSSpec struct { // X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. CertificateAuthorityData string @@ -44,8 +44,8 @@ type TLSSpec struct { CertificateAuthorityDataSource *caBundleSource } -// TLSSpecForSupervisor is a helper function to convert the Supervisor's TLSSpec to the unified TLSSpec -func TLSSpecForSupervisor(source *supervisorv1alpha1.TLSSpec) *TLSSpec { +// TLSSpecForSupervisor is a helper function to convert the Supervisor's TLSSpec to the unified TLSSpec. +func TLSSpecForSupervisor(source *idpv1alpha1.TLSSpec) *TLSSpec { if source == nil { return nil } @@ -64,8 +64,8 @@ func TLSSpecForSupervisor(source *supervisorv1alpha1.TLSSpec) *TLSSpec { return dest } -// TlsSpecForConcierge is a helper function to convert the Concierge's TLSSpec to the unified TLSSpec -func TlsSpecForConcierge(source *conciergev1alpha1.TLSSpec) *TLSSpec { +// TlsSpecForConcierge is a helper function to convert the Concierge's TLSSpec to the unified TLSSpec. +func TlsSpecForConcierge(source *authenticationv1alpha1.TLSSpec) *TLSSpec { if source == nil { return nil } @@ -91,8 +91,8 @@ func getCertPool( tlsSpec *TLSSpec, conditionPrefix string, namespace string, - secretInformer v1.SecretInformer, - configMapInformer v1.ConfigMapInformer, + secretInformer corev1informers.SecretInformer, + configMapInformer corev1informers.ConfigMapInformer, ) (*x509.CertPool, []byte, error) { // if tlsSpec is nil, we return a nil cert pool and cert bundle. A nil error is also returned to indicate that // a nil tlsSpec is nevertheless a valid one resulting in a valid TLS condition. @@ -159,8 +159,8 @@ func ValidateTLSConfig( tlsSpec *TLSSpec, conditionPrefix string, namespace string, - secretInformer v1.SecretInformer, - configMapInformer v1.ConfigMapInformer, + secretInformer corev1informers.SecretInformer, + configMapInformer corev1informers.ConfigMapInformer, ) (*metav1.Condition, []byte, *x509.CertPool, error) { // try to build a x509 cert pool using the ca data specified in the tlsSpec. certPool, bundle, err := getCertPool(tlsSpec, conditionPrefix, namespace, secretInformer, configMapInformer) @@ -178,7 +178,7 @@ func ValidateTLSConfig( return validTLSCondition(fmt.Sprintf("%s is valid: %s", conditionPrefix, loadedTLSConfigurationMessage)), bundle, certPool, err } -func readCABundleFromSource(source *caBundleSource, namespace string, secretInformer v1.SecretInformer, configMapInformer v1.ConfigMapInformer) (string, error) { +func readCABundleFromSource(source *caBundleSource, namespace string, secretInformer corev1informers.SecretInformer, configMapInformer corev1informers.ConfigMapInformer) (string, error) { switch source.Kind { case "Secret": return readCABundleFromK8sSecret(namespace, source.Name, source.Key, secretInformer) @@ -189,7 +189,7 @@ func readCABundleFromSource(source *caBundleSource, namespace string, secretInfo } } -func readCABundleFromK8sSecret(namespace string, name string, key string, secretInformer v1.SecretInformer) (string, error) { +func readCABundleFromK8sSecret(namespace string, name string, key string, secretInformer corev1informers.SecretInformer) (string, error) { s, err := secretInformer.Lister().Secrets(namespace).Get(name) if err != nil { return "", errors.Wrapf(err, "failed to get secret %s/%s", namespace, name) @@ -206,7 +206,7 @@ func readCABundleFromK8sSecret(namespace string, name string, key string, secret return "", fmt.Errorf("key %s not found in secret %s/%s", key, namespace, name) } -func readCABundleFromK8sConfigMap(namespace string, name string, key string, configMapInformer v1.ConfigMapInformer) (string, error) { +func readCABundleFromK8sConfigMap(namespace string, name string, key string, configMapInformer corev1informers.ConfigMapInformer) (string, error) { c, err := configMapInformer.Lister().ConfigMaps(namespace).Get(name) if err != nil { return "", errors.Wrapf(err, "failed to get configmap %s/%s", namespace, name) diff --git a/internal/controller/tlsconfigutil/tls_config_util_test.go b/internal/controller/tlsconfigutil/tls_config_util_test.go index 72f459688..96e02aa1a 100644 --- a/internal/controller/tlsconfigutil/tls_config_util_test.go +++ b/internal/controller/tlsconfigutil/tls_config_util_test.go @@ -4,6 +4,7 @@ package tlsconfigutil import ( + "context" "encoding/base64" "testing" "time" @@ -16,8 +17,8 @@ import ( corev1informers "k8s.io/client-go/informers/core/v1" "k8s.io/client-go/kubernetes/fake" - conciergev1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/authentication/v1alpha1" - supervisorv1alpha1 "go.pinniped.dev/generated/latest/apis/supervisor/idp/v1alpha1" + authenticationv1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/authentication/v1alpha1" + idpv1alpha1 "go.pinniped.dev/generated/latest/apis/supervisor/idp/v1alpha1" "go.pinniped.dev/internal/certauthority" "go.pinniped.dev/internal/controller/conditionsutil" ) @@ -317,24 +318,20 @@ func TestValidateTLSConfig(t *testing.T) { var configMapInformer corev1informers.ConfigMapInformer if len(tt.k8sObjects) > 0 { - stopSecretInformer := make(chan struct{}) - stopConfigMapInformer := make(chan struct{}) fakeClient := fake.NewSimpleClientset(tt.k8sObjects...) - sharedInformers := informers.NewSharedInformerFactory(fakeClient, time.Second) + sharedInformers := informers.NewSharedInformerFactory(fakeClient, 0) configMapInformer = sharedInformers.Core().V1().ConfigMaps() secretsInformer = sharedInformers.Core().V1().Secrets() - // Run the informer so that it can sync the objects from kubernetes into its cache. - // run as a go routine so that we can stop the informer and continue with our tests. - go secretsInformer.Informer().Run(stopSecretInformer) - // wait 1s before stopping the informer. 1s because, that's the resync duration of the informer. - time.Sleep(time.Second) - close(stopSecretInformer) - // TODO: can we avoid calling Run on both informers? - go configMapInformer.Informer().Run(stopConfigMapInformer) - time.Sleep(time.Second) - close(stopConfigMapInformer) - // now the objects from kubernetes should be sync'd into the informer cache. + // calling the .Informer function registers this informer in the sharedinformer. + // doing this will ensure that this informer will be sync'd when Start is called next. + configMapInformer.Informer() + secretsInformer.Informer() + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + sharedInformers.Start(ctx.Done()) + sharedInformers.WaitForCacheSync(ctx.Done()) } actualCondition, _, _, _ := ValidateTLSConfig(tt.tlsSpec, "tls", tt.namespace, secretsInformer, configMapInformer) require.Equal(t, tt.expectedCondition, actualCondition) @@ -391,7 +388,7 @@ func TestReadCABundleFromK8sSecret(t *testing.T) { expectError: true, }, { - name: "should return data from existing secret and existing key", + name: "should return data from existing tls secret and existing key", secretNamespace: "awesome-namespace", secretName: "awesome-secret", secretKey: "awesome", @@ -401,6 +398,27 @@ func TestReadCABundleFromK8sSecret(t *testing.T) { Name: "awesome-secret", Namespace: "awesome-namespace", }, + Type: corev1.SecretTypeTLS, + Data: map[string][]byte{ + "awesome": []byte("pinniped-is-awesome"), + }, + }, + }, + expectedData: "pinniped-is-awesome", + expectError: false, + }, + { + name: "should return data from existing opaque secret and existing key", + secretNamespace: "awesome-namespace", + secretName: "awesome-secret", + secretKey: "awesome", + k8sObjects: []runtime.Object{ + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "awesome-secret", + Namespace: "awesome-namespace", + }, + Type: corev1.SecretTypeOpaque, Data: map[string][]byte{ "awesome": []byte("pinniped-is-awesome"), }, @@ -414,15 +432,18 @@ func TestReadCABundleFromK8sSecret(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { t.Parallel() - stop := make(chan struct{}) fakeClient := fake.NewSimpleClientset(tt.k8sObjects...) - secretsInformer := informers.NewSharedInformerFactory(fakeClient, time.Second).Core().V1().Secrets() - // Run the informer so that it can sync the objects from kubernetes into its cache. - // run as a go routine so that we can stop the informer and continue with our tests. - go secretsInformer.Informer().Run(stop) - // wait 1s before stopping the informer. 1s because, that's the resync duration of the informer. - time.Sleep(time.Second) - close(stop) + sharedInformers := informers.NewSharedInformerFactory(fakeClient, 0) + secretsInformer := sharedInformers.Core().V1().Secrets() + + // calling the .Informer function registers this informer in the sharedinformer. + // doing this will ensure that this informer will be sync'd when Start is called next. + secretsInformer.Informer() + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + sharedInformers.Start(ctx.Done()) + sharedInformers.WaitForCacheSync(ctx.Done()) // now the objects from kubernetes should be sync'd into the informer cache. actualData, actualError := readCABundleFromK8sSecret(tt.secretNamespace, tt.secretName, tt.secretKey, secretsInformer) if tt.expectError { @@ -507,17 +528,19 @@ func TestReadCABundleFromK8sConfigMap(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { t.Parallel() - stop := make(chan struct{}) fakeClient := fake.NewSimpleClientset(tt.k8sObjects...) - configMapInformer := informers.NewSharedInformerFactory(fakeClient, time.Second).Core().V1().ConfigMaps() - // Run the informer so that it can sync the objects from kubernetes into its cache. - // run as a go routine so that we can stop the informer and continue with our tests. - go configMapInformer.Informer().Run(stop) - // wait 1s before stopping the informer. 1s because, that's the resync duration of the informer. - time.Sleep(time.Second) - close(stop) - // now the objects from kubernetes should be sync'd into the informer cache. + sharedInformers := informers.NewSharedInformerFactory(fakeClient, 0) + configMapInformer := sharedInformers.Core().V1().ConfigMaps() + + // calling the .Informer function registers this informer in the sharedinformer. + // doing this will ensure that this informer will be sync'd when Start is called next. + configMapInformer.Informer() + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + sharedInformers.Start(ctx.Done()) + sharedInformers.WaitForCacheSync(ctx.Done()) actualData, actualError := readCABundleFromK8sConfigMap(tt.configMapNamespace, tt.configMapName, tt.configMapKey, configMapInformer) if tt.expectError { require.Error(t, actualError) @@ -536,7 +559,7 @@ func TestNewCommonTLSSpecForSupervisor(t *testing.T) { base64EncodedBundle := base64.StdEncoding.EncodeToString(bundle) tests := []struct { name string - supervisorTLSSpec *supervisorv1alpha1.TLSSpec + supervisorTLSSpec *idpv1alpha1.TLSSpec expected *TLSSpec }{ { @@ -546,7 +569,7 @@ func TestNewCommonTLSSpecForSupervisor(t *testing.T) { }, { name: "should return tls spec with non-empty certificateAuthorityData", - supervisorTLSSpec: &supervisorv1alpha1.TLSSpec{ + supervisorTLSSpec: &idpv1alpha1.TLSSpec{ CertificateAuthorityData: base64EncodedBundle, CertificateAuthorityDataSource: nil, }, @@ -557,8 +580,8 @@ func TestNewCommonTLSSpecForSupervisor(t *testing.T) { }, { name: "should return tls spec with certificateAuthorityDataSource", - supervisorTLSSpec: &supervisorv1alpha1.TLSSpec{ - CertificateAuthorityDataSource: &supervisorv1alpha1.CABundleSource{ + supervisorTLSSpec: &idpv1alpha1.TLSSpec{ + CertificateAuthorityDataSource: &idpv1alpha1.CABundleSource{ Kind: "Secret", Name: "awesome-secret", Key: "ca-bundle", @@ -590,7 +613,7 @@ func TestNewCommonTlsSpecForConcierge(t *testing.T) { base64EncodedBundle := base64.StdEncoding.EncodeToString(bundle) tests := []struct { name string - conciergeTLSSpec *conciergev1alpha1.TLSSpec + conciergeTLSSpec *authenticationv1alpha1.TLSSpec expected *TLSSpec }{ { @@ -600,7 +623,7 @@ func TestNewCommonTlsSpecForConcierge(t *testing.T) { }, { name: "should return tls spec with non-empty certificateAuthorityData", - conciergeTLSSpec: &conciergev1alpha1.TLSSpec{ + conciergeTLSSpec: &authenticationv1alpha1.TLSSpec{ CertificateAuthorityData: base64EncodedBundle, CertificateAuthorityDataSource: nil, }, @@ -611,8 +634,8 @@ func TestNewCommonTlsSpecForConcierge(t *testing.T) { }, { name: "should return tls spec with certificateAuthorityDataSource", - conciergeTLSSpec: &conciergev1alpha1.TLSSpec{ - CertificateAuthorityDataSource: &conciergev1alpha1.CABundleSource{ + conciergeTLSSpec: &authenticationv1alpha1.TLSSpec{ + CertificateAuthorityDataSource: &authenticationv1alpha1.CABundleSource{ Kind: "Secret", Name: "awesome-secret", Key: "ca-bundle", diff --git a/internal/controller/utils.go b/internal/controller/utils.go index 2202c92b5..193448562 100644 --- a/internal/controller/utils.go +++ b/internal/controller/utils.go @@ -66,15 +66,6 @@ func MatchAnySecretOfTypeFilter(secretType corev1.SecretType, parentFunc control return SimpleFilter(isSecretOfType, parentFunc) } -func containsSecretType(filter []corev1.SecretType, secretType corev1.SecretType) bool { - for _, filter := range filter { - if filter == secretType { - return true - } - } - return false -} - func MatchAnySecretOfTypesFilter(secretTypes []corev1.SecretType, parentFunc controllerlib.ParentFunc, namespaces ...string) controllerlib.Filter { isSecretOfType := func(obj metav1.Object) bool { secret, ok := obj.(*corev1.Secret) diff --git a/test/integration/concierge_tls_spec_test.go b/test/integration/concierge_tls_spec_test.go index 3453d0c19..845f77a1f 100644 --- a/test/integration/concierge_tls_spec_test.go +++ b/test/integration/concierge_tls_spec_test.go @@ -221,6 +221,7 @@ func TestTLSSpecKubeBuilderValidationConcierge_Parallel(t *testing.T) { yamlBytes := []byte(fmt.Sprintf(tc.customResourceYaml, env.APIGroupSuffix, resourceName)) require.NoError(t, os.WriteFile(yamlFilepath, yamlBytes, 0600)) + //nolint:gosec // this is test code. cmd := exec.CommandContext(context.Background(), "kubectl", []string{"apply", "-f", yamlFilepath}...) var stdOut, stdErr bytes.Buffer cmd.Stdout = &stdOut @@ -228,6 +229,7 @@ func TestTLSSpecKubeBuilderValidationConcierge_Parallel(t *testing.T) { err := cmd.Run() t.Cleanup(func() { t.Helper() + //nolint:gosec // this is test code. require.NoError(t, exec.Command("kubectl", []string{"delete", "--ignore-not-found", "-f", yamlFilepath}...).Run()) }) if tc.expectedError == "" { diff --git a/test/integration/supervisor_tls_spec_test.go b/test/integration/supervisor_tls_spec_test.go index 7c4304860..b7d80b608 100644 --- a/test/integration/supervisor_tls_spec_test.go +++ b/test/integration/supervisor_tls_spec_test.go @@ -271,6 +271,7 @@ func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { yamlBytes := []byte(fmt.Sprintf(tc.customResourceYaml, env.APIGroupSuffix, resourceName)) require.NoError(t, os.WriteFile(yamlFilepath, yamlBytes, 0600)) + //nolint:gosec // this is test code. cmd := exec.CommandContext(context.Background(), "kubectl", []string{"apply", "-f", yamlFilepath}...) var stdOut, stdErr bytes.Buffer cmd.Stdout = &stdOut @@ -278,6 +279,7 @@ func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { err := cmd.Run() t.Cleanup(func() { t.Helper() + //nolint:gosec // this is test code. require.NoError(t, exec.Command("kubectl", []string{"delete", "--ignore-not-found", "-f", yamlFilepath}...).Run()) }) if tc.expectedError == "" { From 207bac94524ac5657abd062646effbe338db90b9 Mon Sep 17 00:00:00 2001 From: Ashish Amarnath Date: Mon, 1 Jul 2024 23:34:59 -0700 Subject: [PATCH 08/99] webhook cache filler Signed-off-by: Ashish Amarnath --- .../webhookcachefiller/webhookcachefiller.go | 63 +++++++++---------- .../webhookcachefiller_test.go | 13 ++-- .../controllermanager/prepare_controllers.go | 2 + 3 files changed, 40 insertions(+), 38 deletions(-) diff --git a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go index f9400db4b..ef13bef0d 100644 --- a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go +++ b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go @@ -21,6 +21,7 @@ import ( k8snetutil "k8s.io/apimachinery/pkg/util/net" "k8s.io/apiserver/pkg/authentication/authenticator" "k8s.io/apiserver/plugin/pkg/authenticator/token/webhook" + corev1informers "k8s.io/client-go/informers/core/v1" "k8s.io/client-go/rest" "k8s.io/klog/v2" "k8s.io/utils/clock" @@ -31,6 +32,7 @@ import ( pinnipedcontroller "go.pinniped.dev/internal/controller" "go.pinniped.dev/internal/controller/authenticator/authncache" "go.pinniped.dev/internal/controller/conditionsutil" + "go.pinniped.dev/internal/controller/tlsconfigutil" "go.pinniped.dev/internal/controllerlib" "go.pinniped.dev/internal/crypto/ptls" "go.pinniped.dev/internal/endpointaddr" @@ -67,6 +69,8 @@ func New( cache *authncache.Cache, client conciergeclientset.Interface, webhooks authinformers.WebhookAuthenticatorInformer, + secretInformer corev1informers.SecretInformer, + configMapInformer corev1informers.ConfigMapInformer, clock clock.Clock, log plog.Logger, ) controllerlib.Controller { @@ -74,11 +78,13 @@ func New( controllerlib.Config{ Name: controllerName, Syncer: &webhookCacheFillerController{ - cache: cache, - client: client, - webhooks: webhooks, - clock: clock, - log: log.WithName(controllerName), + cache: cache, + client: client, + webhooks: webhooks, + secretInformer: secretInformer, + configMapInformer: configMapInformer, + clock: clock, + log: log.WithName(controllerName), }, }, controllerlib.WithInformer( @@ -90,11 +96,13 @@ func New( } type webhookCacheFillerController struct { - cache *authncache.Cache - webhooks authinformers.WebhookAuthenticatorInformer - client conciergeclientset.Interface - clock clock.Clock - log plog.Logger + cache *authncache.Cache + webhooks authinformers.WebhookAuthenticatorInformer + secretInformer corev1informers.SecretInformer + configMapInformer corev1informers.ConfigMapInformer + client conciergeclientset.Interface + clock clock.Clock + log plog.Logger } // Sync implements controllerlib.Syncer. @@ -136,7 +144,7 @@ func (c *webhookCacheFillerController) Sync(ctx controllerlib.Context) error { conditions := make([]*metav1.Condition, 0) var errs []error - certPool, pemBytes, conditions, tlsBundleOk := c.validateTLSBundle(obj.Spec.TLS, conditions) + certPool, pemBytes, conditions, tlsBundleOk := c.validateTLSBundle(obj.Spec.TLS, obj.Namespace, conditions) endpointHostPort, conditions, endpointOk := c.validateEndpoint(obj.Spec.Endpoint, conditions) okSoFar := tlsBundleOk && endpointOk @@ -312,29 +320,16 @@ func (c *webhookCacheFillerController) validateConnection(certPool *x509.CertPoo return conditions, nil } -func (c *webhookCacheFillerController) validateTLSBundle(tlsSpec *authenticationv1alpha1.TLSSpec, conditions []*metav1.Condition) (*x509.CertPool, []byte, []*metav1.Condition, bool) { - rootCAs, pemBytes, err := pinnipedcontroller.BuildCertPoolAuth(tlsSpec) - if err != nil { - msg := fmt.Sprintf("%s: %s", "invalid TLS configuration", err.Error()) - conditions = append(conditions, &metav1.Condition{ - Type: typeTLSConfigurationValid, - Status: metav1.ConditionFalse, - Reason: reasonInvalidTLSConfiguration, - Message: msg, - }) - return rootCAs, pemBytes, conditions, false - } - msg := "successfully parsed specified CA bundle" - if rootCAs == nil { - msg = "no CA bundle specified" - } - conditions = append(conditions, &metav1.Condition{ - Type: typeTLSConfigurationValid, - Status: metav1.ConditionTrue, - Reason: reasonSuccess, - Message: msg, - }) - return rootCAs, pemBytes, conditions, true +func (c *webhookCacheFillerController) validateTLSBundle(tlsSpec *authenticationv1alpha1.TLSSpec, namespace string, conditions []*metav1.Condition) (*x509.CertPool, []byte, []*metav1.Condition, bool) { + condition, pemBytes, rootCAs, _ := tlsconfigutil.ValidateTLSConfig( + tlsconfigutil.TlsSpecForConcierge(tlsSpec), + "spec.tls", + namespace, + c.secretInformer, + c.configMapInformer) + + conditions = append(conditions, condition) + return rootCAs, pemBytes, conditions, condition.Status == metav1.ConditionTrue } func (c *webhookCacheFillerController) validateEndpoint(endpoint string, conditions []*metav1.Condition) (*endpointaddr.HostPort, []*metav1.Condition, bool) { diff --git a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go index b2c60418c..bc6157adb 100644 --- a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go +++ b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go @@ -25,6 +25,8 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apiserver/pkg/authentication/authenticator" + kubeinformers "k8s.io/client-go/informers" + kubernetesfake "k8s.io/client-go/kubernetes/fake" coretesting "k8s.io/client-go/testing" clocktesting "k8s.io/utils/clock/testing" "k8s.io/utils/ptr" @@ -221,7 +223,7 @@ func TestController(t *testing.T) { ObservedGeneration: observedGeneration, LastTransitionTime: time, Reason: "Success", - Message: "successfully parsed specified CA bundle", + Message: "spec.tls is valid: loaded TLS configuration", } } happyTLSConfigurationValidNoCA := func(time metav1.Time, observedGeneration int64) metav1.Condition { @@ -231,7 +233,7 @@ func TestController(t *testing.T) { ObservedGeneration: observedGeneration, LastTransitionTime: time, Reason: "Success", - Message: "no CA bundle specified", + Message: "spec.tls is valid: no TLS configuration provided", } } sadTLSConfigurationValid := func(time metav1.Time, observedGeneration int64) metav1.Condition { @@ -240,8 +242,8 @@ func TestController(t *testing.T) { Status: "False", ObservedGeneration: observedGeneration, LastTransitionTime: time, - Reason: "InvalidTLSConfiguration", - Message: "invalid TLS configuration: illegal base64 data at input byte 7", + Reason: "InvalidTLSConfig", + Message: "spec.tls.certificateAuthorityData is invalid: illegal base64 data at input byte 7", } } @@ -1488,6 +1490,7 @@ func TestController(t *testing.T) { tt.configClient(pinnipedAPIClient) } informers := conciergeinformers.NewSharedInformerFactory(pinnipedAPIClient, 0) + kubeInformers := kubeinformers.NewSharedInformerFactory(kubernetesfake.NewSimpleClientset(), 0) cache := authncache.New() var log bytes.Buffer @@ -1501,6 +1504,8 @@ func TestController(t *testing.T) { cache, pinnipedAPIClient, informers.Authentication().V1alpha1().WebhookAuthenticators(), + kubeInformers.Core().V1().Secrets(), + kubeInformers.Core().V1().ConfigMaps(), frozenClock, logger) diff --git a/internal/controllermanager/prepare_controllers.go b/internal/controllermanager/prepare_controllers.go index a7edc4fd4..274aebcec 100644 --- a/internal/controllermanager/prepare_controllers.go +++ b/internal/controllermanager/prepare_controllers.go @@ -238,6 +238,8 @@ func PrepareControllers(c *Config) (controllerinit.RunnerBuilder, error) { //nol c.AuthenticatorCache, client.PinnipedConcierge, informers.pinniped.Authentication().V1alpha1().WebhookAuthenticators(), + informers.installationNamespaceK8s.Core().V1().Secrets(), + informers.installationNamespaceK8s.Core().V1().ConfigMaps(), clock.RealClock{}, plog.New(), ), From 9ab7c39d56e41987a3325aafd98592c96cfa3fad Mon Sep 17 00:00:00 2001 From: Ashish Amarnath Date: Tue, 2 Jul 2024 00:11:30 -0700 Subject: [PATCH 09/99] jwt cache filler Signed-off-by: Ashish Amarnath --- .../jwtcachefiller/jwtcachefiller.go | 42 ++++++++----------- .../jwtcachefiller/jwtcachefiller_test.go | 13 ++++-- .../controllermanager/prepare_controllers.go | 2 + 3 files changed, 29 insertions(+), 28 deletions(-) diff --git a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go index ed1acf2bb..fb68efc38 100644 --- a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go +++ b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go @@ -25,6 +25,7 @@ import ( "k8s.io/apiserver/pkg/apis/apiserver" "k8s.io/apiserver/pkg/authentication/authenticator" "k8s.io/apiserver/plugin/pkg/authenticator/token/oidc" + corev1informers "k8s.io/client-go/informers/core/v1" "k8s.io/klog/v2" "k8s.io/utils/clock" "k8s.io/utils/ptr" @@ -37,6 +38,7 @@ import ( pinnipedauthenticator "go.pinniped.dev/internal/controller/authenticator" "go.pinniped.dev/internal/controller/authenticator/authncache" "go.pinniped.dev/internal/controller/conditionsutil" + "go.pinniped.dev/internal/controller/tlsconfigutil" "go.pinniped.dev/internal/controllerlib" "go.pinniped.dev/internal/net/phttp" "go.pinniped.dev/internal/plog" @@ -132,6 +134,8 @@ func New( cache *authncache.Cache, client conciergeclientset.Interface, jwtAuthenticators authinformers.JWTAuthenticatorInformer, + secretInformer corev1informers.SecretInformer, + configMapInformer corev1informers.ConfigMapInformer, clock clock.Clock, log plog.Logger, ) controllerlib.Controller { @@ -142,6 +146,8 @@ func New( cache: cache, client: client, jwtAuthenticators: jwtAuthenticators, + secretInformer: secretInformer, + configMapInformer: configMapInformer, clock: clock, log: log.WithName(controllerName), }, @@ -157,6 +163,8 @@ func New( type jwtCacheFillerController struct { cache *authncache.Cache jwtAuthenticators authinformers.JWTAuthenticatorInformer + secretInformer corev1informers.SecretInformer + configMapInformer corev1informers.ConfigMapInformer client conciergeclientset.Interface clock clock.Clock log plog.Logger @@ -202,7 +210,7 @@ func (c *jwtCacheFillerController) Sync(ctx controllerlib.Context) error { conditions := make([]*metav1.Condition, 0) var errs []error - rootCAs, conditions, tlsOk := c.validateTLSBundle(obj.Spec.TLS, conditions) + rootCAs, conditions, tlsOk := c.validateTLSBundle(obj.Spec.TLS, obj.Namespace, conditions) _, conditions, issuerOk := c.validateIssuer(obj.Spec.Issuer, conditions) okSoFar := tlsOk && issuerOk @@ -270,30 +278,16 @@ func (c *jwtCacheFillerController) cacheValueAsJWTAuthenticator(value authncache return jwtAuthenticator } -func (c *jwtCacheFillerController) validateTLSBundle(tlsSpec *authenticationv1alpha1.TLSSpec, conditions []*metav1.Condition) (*x509.CertPool, []*metav1.Condition, bool) { - rootCAs, _, err := pinnipedcontroller.BuildCertPoolAuth(tlsSpec) - if err != nil { - msg := fmt.Sprintf("%s: %s", "invalid TLS configuration", err.Error()) - conditions = append(conditions, &metav1.Condition{ - Type: typeTLSConfigurationValid, - Status: metav1.ConditionFalse, - Reason: reasonInvalidTLSConfiguration, - Message: msg, - }) - return rootCAs, conditions, false - } +func (c *jwtCacheFillerController) validateTLSBundle(tlsSpec *authenticationv1alpha1.TLSSpec, namespace string, conditions []*metav1.Condition) (*x509.CertPool, []*metav1.Condition, bool) { + condition, _, rootCAs, _ := tlsconfigutil.ValidateTLSConfig( + tlsconfigutil.TlsSpecForConcierge(tlsSpec), + "spec.tls", + namespace, + c.secretInformer, + c.configMapInformer) - msg := "successfully parsed specified CA bundle" - if rootCAs == nil { - msg = "no CA bundle specified" - } - conditions = append(conditions, &metav1.Condition{ - Type: typeTLSConfigurationValid, - Status: metav1.ConditionTrue, - Reason: reasonSuccess, - Message: msg, - }) - return rootCAs, conditions, true + conditions = append(conditions, condition) + return rootCAs, conditions, condition.Status == metav1.ConditionTrue } func (c *jwtCacheFillerController) validateIssuer(issuer string, conditions []*metav1.Condition) (*url.URL, []*metav1.Condition, bool) { diff --git a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller_test.go b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller_test.go index cdc207b1b..3e5b9729e 100644 --- a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller_test.go +++ b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller_test.go @@ -32,6 +32,8 @@ import ( "k8s.io/apimachinery/pkg/util/wait" "k8s.io/apiserver/pkg/authentication/authenticator" "k8s.io/apiserver/pkg/authentication/user" + kubeinformers "k8s.io/client-go/informers" + kubernetesfake "k8s.io/client-go/kubernetes/fake" coretesting "k8s.io/client-go/testing" clocktesting "k8s.io/utils/clock/testing" @@ -379,7 +381,7 @@ func TestController(t *testing.T) { ObservedGeneration: observedGeneration, LastTransitionTime: time, Reason: "Success", - Message: "successfully parsed specified CA bundle", + Message: "spec.tls is valid: loaded TLS configuration", } } happyTLSConfigurationValidNoCA := func(time metav1.Time, observedGeneration int64) metav1.Condition { @@ -389,7 +391,7 @@ func TestController(t *testing.T) { ObservedGeneration: observedGeneration, LastTransitionTime: time, Reason: "Success", - Message: "no CA bundle specified", + Message: "spec.tls is valid: no TLS configuration provided", } } sadTLSConfigurationValid := func(time metav1.Time, observedGeneration int64) metav1.Condition { @@ -398,8 +400,8 @@ func TestController(t *testing.T) { Status: "False", ObservedGeneration: observedGeneration, LastTransitionTime: time, - Reason: "InvalidTLSConfiguration", - Message: "invalid TLS configuration: illegal base64 data at input byte 7", + Reason: "InvalidTLSConfig", + Message: "spec.tls.certificateAuthorityData is invalid: illegal base64 data at input byte 7", } } @@ -1842,6 +1844,7 @@ func TestController(t *testing.T) { tt.configClient(pinnipedAPIClient) } pinnipedInformers := conciergeinformers.NewSharedInformerFactory(pinnipedAPIClient, 0) + kubeInformers := kubeinformers.NewSharedInformerFactory(kubernetesfake.NewSimpleClientset(), 0) cache := authncache.New() var log bytes.Buffer @@ -1855,6 +1858,8 @@ func TestController(t *testing.T) { cache, pinnipedAPIClient, pinnipedInformers.Authentication().V1alpha1().JWTAuthenticators(), + kubeInformers.Core().V1().Secrets(), + kubeInformers.Core().V1().ConfigMaps(), frozenClock, logger) diff --git a/internal/controllermanager/prepare_controllers.go b/internal/controllermanager/prepare_controllers.go index 274aebcec..31c8b81bb 100644 --- a/internal/controllermanager/prepare_controllers.go +++ b/internal/controllermanager/prepare_controllers.go @@ -250,6 +250,8 @@ func PrepareControllers(c *Config) (controllerinit.RunnerBuilder, error) { //nol c.AuthenticatorCache, client.PinnipedConcierge, informers.pinniped.Authentication().V1alpha1().JWTAuthenticators(), + informers.installationNamespaceK8s.Core().V1().Secrets(), + informers.installationNamespaceK8s.Core().V1().ConfigMaps(), clock.RealClock{}, plog.New(), ), From 90e8cc86c267bca2a243f80ca0752dcff1bb0b99 Mon Sep 17 00:00:00 2001 From: Ashish Amarnath Date: Tue, 2 Jul 2024 23:11:47 -0700 Subject: [PATCH 10/99] integration tests pass --- .../integration/concierge_jwtauthenticator_status_test.go | 8 ++++---- .../concierge_webhookauthenticator_status_test.go | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/test/integration/concierge_jwtauthenticator_status_test.go b/test/integration/concierge_jwtauthenticator_status_test.go index 8c77da8ac..94a8f907d 100644 --- a/test/integration/concierge_jwtauthenticator_status_test.go +++ b/test/integration/concierge_jwtauthenticator_status_test.go @@ -91,8 +91,8 @@ func TestConciergeJWTAuthenticatorStatus_Parallel(t *testing.T) { }, { Type: "TLSConfigurationValid", Status: "False", - Reason: "InvalidTLSConfiguration", - Message: "invalid TLS configuration: illegal base64 data at input byte 7", + Reason: "InvalidTLSConfig", + Message: "spec.tls.certificateAuthorityData is invalid: illegal base64 data at input byte 7", }, }, )) @@ -148,7 +148,7 @@ func TestConciergeJWTAuthenticatorStatus_Parallel(t *testing.T) { Type: "TLSConfigurationValid", Status: "True", Reason: "Success", - Message: "successfully parsed specified CA bundle", + Message: "spec.tls is valid: loaded TLS configuration", }, }, )) @@ -357,7 +357,7 @@ func TestConciergeJWTAuthenticatorCRDValidations_Parallel(t *testing.T) { func allSuccessfulJWTAuthenticatorConditions(caBundleExists bool) []metav1.Condition { tlsConfigValidMsg := "no CA bundle specified" if caBundleExists { - tlsConfigValidMsg = "successfully parsed specified CA bundle" + tlsConfigValidMsg = "spec.tls is valid: loaded TLS configuration" } return []metav1.Condition{{ Type: "AuthenticatorValid", diff --git a/test/integration/concierge_webhookauthenticator_status_test.go b/test/integration/concierge_webhookauthenticator_status_test.go index 99d06034a..9a2f2b0ae 100644 --- a/test/integration/concierge_webhookauthenticator_status_test.go +++ b/test/integration/concierge_webhookauthenticator_status_test.go @@ -65,8 +65,8 @@ func TestConciergeWebhookAuthenticatorStatus_Parallel(t *testing.T) { }, { Type: "TLSConfigurationValid", Status: "False", - Reason: "InvalidTLSConfiguration", - Message: "invalid TLS configuration: illegal base64 data at input byte 7", + Reason: "InvalidTLSConfig", + Message: "spec.tls.certificateAuthorityData is invalid: illegal base64 data at input byte 7", }, { Type: "WebhookConnectionValid", Status: "Unknown", From edc327ba335cf8fb6a21372987e8a68adf29312e Mon Sep 17 00:00:00 2001 From: Ashish Amarnath Date: Tue, 2 Jul 2024 23:21:29 -0700 Subject: [PATCH 11/99] update supervisor RBAC to allow get, list, and watch on configmaps Signed-off-by: Ashish Amarnath --- deploy/supervisor/rbac.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/deploy/supervisor/rbac.yaml b/deploy/supervisor/rbac.yaml index 2d51e383a..85a2e9efa 100644 --- a/deploy/supervisor/rbac.yaml +++ b/deploy/supervisor/rbac.yaml @@ -16,6 +16,9 @@ rules: - apiGroups: [""] resources: [secrets] verbs: [create, get, list, patch, update, watch, delete] + - apiGroups: [""] + resources: [configmaps] + verbs: [get, list, watch] - apiGroups: - #@ pinnipedDevAPIGroupWithPrefix("config.supervisor") resources: [federationdomains] From afcd80de37b32ae721dd8d4dafab462ff256467c Mon Sep 17 00:00:00 2001 From: Ashish Amarnath Date: Wed, 3 Jul 2024 00:58:59 -0700 Subject: [PATCH 12/99] more integration tests pass Signed-off-by: Ashish Amarnath --- ...ncierge_webhookauthenticator_status_test.go | 2 +- test/integration/kube_api_discovery_test.go | 2 +- test/integration/supervisor_github_idp_test.go | 6 +++--- test/integration/supervisor_login_test.go | 8 ++++---- test/integration/supervisor_upstream_test.go | 18 ++++++++++++++++++ 5 files changed, 27 insertions(+), 9 deletions(-) diff --git a/test/integration/concierge_webhookauthenticator_status_test.go b/test/integration/concierge_webhookauthenticator_status_test.go index 9a2f2b0ae..88106e984 100644 --- a/test/integration/concierge_webhookauthenticator_status_test.go +++ b/test/integration/concierge_webhookauthenticator_status_test.go @@ -289,7 +289,7 @@ func allSuccessfulWebhookAuthenticatorConditions() []metav1.Condition { Type: "TLSConfigurationValid", Status: "True", Reason: "Success", - Message: "successfully parsed specified CA bundle", + Message: "spec.tls is valid: loaded TLS configuration", }, { Type: "WebhookConnectionValid", diff --git a/test/integration/kube_api_discovery_test.go b/test/integration/kube_api_discovery_test.go index 571adbdfe..e01d0a63d 100644 --- a/test/integration/kube_api_discovery_test.go +++ b/test/integration/kube_api_discovery_test.go @@ -452,7 +452,7 @@ func TestGetAPIResourceList(t *testing.T) { //nolint:gocyclo // each t.Run is pr } // manually update this value whenever you add additional fields to an API resource and then run the generator - totalExpectedAPIFields := 289 + totalExpectedAPIFields := 313 // Because we are parsing text from `kubectl explain` and because the format of that text can change // over time, make a rudimentary assertion that this test exercised the whole tree of all fields of all diff --git a/test/integration/supervisor_github_idp_test.go b/test/integration/supervisor_github_idp_test.go index b250cdf3f..47c237334 100644 --- a/test/integration/supervisor_github_idp_test.go +++ b/test/integration/supervisor_github_idp_test.go @@ -410,7 +410,7 @@ func TestGitHubIDPPhaseAndConditions_Parallel(t *testing.T) { Type: "TLSConfigurationValid", Status: metav1.ConditionTrue, Reason: "Success", - Message: "spec.githubAPI.tls.certificateAuthorityData is valid", + Message: "spec.githubAPI.tls is valid: no TLS configuration provided", }, }, }, @@ -479,7 +479,7 @@ func TestGitHubIDPPhaseAndConditions_Parallel(t *testing.T) { Type: "TLSConfigurationValid", Status: metav1.ConditionTrue, Reason: "Success", - Message: `spec.githubAPI.tls.certificateAuthorityData is valid`, + Message: `spec.githubAPI.tls is valid: no TLS configuration provided`, }, }, }, @@ -686,7 +686,7 @@ func TestGitHubIDPSecretInOtherNamespace_Parallel(t *testing.T) { Type: "TLSConfigurationValid", Status: metav1.ConditionTrue, Reason: "Success", - Message: "spec.githubAPI.tls.certificateAuthorityData is valid", + Message: "spec.githubAPI.tls is valid: no TLS configuration provided", }, }) } diff --git a/test/integration/supervisor_login_test.go b/test/integration/supervisor_login_test.go index 442985c8a..1b360322f 100644 --- a/test/integration/supervisor_login_test.go +++ b/test/integration/supervisor_login_test.go @@ -2415,7 +2415,7 @@ func requireSuccessfulLDAPIdentityProviderConditions(t *testing.T, ldapIDP *idpv case "BindSecretValid": require.Equal(t, "loaded bind secret", condition.Message) case "TLSConfigurationValid": - require.Equal(t, "loaded TLS configuration", condition.Message) + require.Equal(t, "spec.tls is valid: loaded TLS configuration", condition.Message) case "LDAPConnectionValid": require.Equal(t, expectedLDAPConnectionValidMessage, condition.Message) } @@ -2440,7 +2440,7 @@ func requireSuccessfulActiveDirectoryIdentityProviderConditions(t *testing.T, ad case "BindSecretValid": require.Equal(t, "loaded bind secret", condition.Message) case "TLSConfigurationValid": - require.Equal(t, "loaded TLS configuration", condition.Message) + require.Equal(t, "spec.tls is valid: loaded TLS configuration", condition.Message) case "LDAPConnectionValid": require.Equal(t, expectedActiveDirectoryConnectionValidMessage, condition.Message) } @@ -2474,7 +2474,7 @@ func requireEventuallySuccessfulLDAPIdentityProviderConditions(t *testing.T, req case "BindSecretValid": requireEventually.Equal("loaded bind secret", condition.Message) case "TLSConfigurationValid": - requireEventually.Equal("loaded TLS configuration", condition.Message) + requireEventually.Equal("spec.tls is valid: loaded TLS configuration", condition.Message) case "LDAPConnectionValid": requireEventually.Equal(expectedLDAPConnectionValidMessage, condition.Message) } @@ -2500,7 +2500,7 @@ func requireEventuallySuccessfulActiveDirectoryIdentityProviderConditions(t *tes case "BindSecretValid": requireEventually.Equal("loaded bind secret", condition.Message) case "TLSConfigurationValid": - requireEventually.Equal("loaded TLS configuration", condition.Message) + requireEventually.Equal("spec.tls is valid: loaded TLS configuration", condition.Message) case "LDAPConnectionValid": requireEventually.Equal(expectedActiveDirectoryConnectionValidMessage, condition.Message) } diff --git a/test/integration/supervisor_upstream_test.go b/test/integration/supervisor_upstream_test.go index 0c7e463d7..ab5ffcfd5 100644 --- a/test/integration/supervisor_upstream_test.go +++ b/test/integration/supervisor_upstream_test.go @@ -46,6 +46,12 @@ Get "https://127.0.0.1:444444/invalid-url-that-is-really-really-long-nananananan Reason: "Success", Message: "additionalAuthorizeParameters parameter names are allowed", }, + { + Type: "TLSConfigurationValid", + Status: "True", + Reason: "Success", + Message: `spec.tls is valid: no TLS configuration provided`, + }, }) }) @@ -84,6 +90,12 @@ oidc: issuer did not match the issuer returned by provider, expected "` + env.Su Reason: "Success", Message: "additionalAuthorizeParameters parameter names are allowed", }, + { + Type: "TLSConfigurationValid", + Status: "True", + Reason: "Success", + Message: `spec.tls is valid: loaded TLS configuration`, + }, }) }) @@ -121,6 +133,12 @@ oidc: issuer did not match the issuer returned by provider, expected "` + env.Su Reason: "Success", Message: "additionalAuthorizeParameters parameter names are allowed", }, + { + Type: "TLSConfigurationValid", + Status: "True", + Reason: "Success", + Message: `spec.tls is valid: loaded TLS configuration`, + }, }) }) } From 821a893f700dbaec6df6b30f3608deacfda66567 Mon Sep 17 00:00:00 2001 From: Ashish Amarnath Date: Mon, 8 Jul 2024 23:54:06 -0700 Subject: [PATCH 13/99] integration tests for supervisor oidc, ldap, activedirectory IDP Signed-off-by: Ashish Amarnath --- test/integration/supervisor_login_test.go | 402 +++++++++++++++++++++- test/testlib/client.go | 21 ++ 2 files changed, 422 insertions(+), 1 deletion(-) diff --git a/test/integration/supervisor_login_test.go b/test/integration/supervisor_login_test.go index 1b360322f..07aa179fd 100644 --- a/test/integration/supervisor_login_test.go +++ b/test/integration/supervisor_login_test.go @@ -301,7 +301,7 @@ func TestSupervisorLogin_Browser(t *testing.T) { regexp.QuoteMeta("&sub="+base64.RawURLEncoding.EncodeToString([]byte(env.SupervisorUpstreamLDAP.TestUserUniqueIDAttributeValue))) + "$" - // The downstream ID token Subject should be in the the same format as LDAP above, but with AD-specific values. + // The downstream ID token Subject should be in the same format as LDAP above, but with AD-specific values. expectedIDTokenSubjectRegexForUpstreamAD := "^" + regexp.QuoteMeta("ldaps://"+env.SupervisorUpstreamActiveDirectory.Host+"?base="+url.QueryEscape(env.SupervisorUpstreamActiveDirectory.DefaultNamingContextSearchBase)) + regexp.QuoteMeta("&idpName=test-upstream-ad-idp-") + `[\w]+` + @@ -322,6 +322,7 @@ func TestSupervisorLogin_Browser(t *testing.T) { regexp.QuoteMeta("&sub=") + ".+" + "$" + // TODO: update this test table to add 2 tests per IDP each to source ca bundle from secret and cm tests := []*supervisorLoginTestcase{ { name: "oidc with default username and groups claim settings", @@ -338,6 +339,121 @@ func TestSupervisorLogin_Browser(t *testing.T) { // the ID token Username should include the upstream user ID after the upstream issuer name wantDownstreamIDTokenUsernameToMatch: func(_ string) string { return "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?sub=") + ".+" }, }, + { + name: "oidc IDP using secrets of type opaque to source ca bundle with default username and groups claim settings", + maybeSkip: skipNever, + createIDP: func(t *testing.T) string { + idpSpec := basicOIDCIdentityProviderSpec() + caData, _ := base64.StdEncoding.DecodeString(idpSpec.TLS.CertificateAuthorityData) + caSecret := testlib.CreateTestSecret(t, env.SupervisorNamespace, "ca-cert", corev1.SecretTypeOpaque, + map[string]string{ + "ca.crt": string(caData), + }) + idpSpec.TLS.CertificateAuthorityData = "" + idpSpec.TLS.CertificateAuthorityDataSource = &idpv1alpha1.CABundleSource{ + Kind: "Secret", + Name: caSecret.Name, + Key: "ca.crt", + } + + return testlib.CreateTestOIDCIdentityProvider(t, idpSpec, idpv1alpha1.PhaseReady).Name + }, + requestAuthorization: requestAuthorizationUsingBrowserAuthcodeFlowOIDC, + breakRefreshSessionData: func(t *testing.T, pinnipedSession *psession.PinnipedSession, _, _ string) { + pinnipedSessionData := pinnipedSession.Custom + pinnipedSessionData.OIDC.UpstreamIssuer = "wrong-issuer" + }, + wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamOIDC, + // the ID token Username should include the upstream user ID after the upstream issuer name + wantDownstreamIDTokenUsernameToMatch: func(_ string) string { return "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?sub=") + ".+" }, + }, + { + name: "oidc IDP using secrets of type TLS to source ca bundle with default username and groups claim settings", + maybeSkip: skipNever, + createIDP: func(t *testing.T) string { + idpSpec := basicOIDCIdentityProviderSpec() + caData, _ := base64.StdEncoding.DecodeString(idpSpec.TLS.CertificateAuthorityData) + caSecret := testlib.CreateTestSecret(t, env.SupervisorNamespace, "ca-cert", corev1.SecretTypeTLS, + map[string]string{ + "ca.crt": string(caData), + "tls.crt": "", + "tls.key": "", + }) + idpSpec.TLS.CertificateAuthorityData = "" + idpSpec.TLS.CertificateAuthorityDataSource = &idpv1alpha1.CABundleSource{ + Kind: "Secret", + Name: caSecret.Name, + Key: "ca.crt", + } + + return testlib.CreateTestOIDCIdentityProvider(t, idpSpec, idpv1alpha1.PhaseReady).Name + }, + requestAuthorization: requestAuthorizationUsingBrowserAuthcodeFlowOIDC, + breakRefreshSessionData: func(t *testing.T, pinnipedSession *psession.PinnipedSession, _, _ string) { + pinnipedSessionData := pinnipedSession.Custom + pinnipedSessionData.OIDC.UpstreamIssuer = "wrong-issuer" + }, + wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamOIDC, + // the ID token Username should include the upstream user ID after the upstream issuer name + wantDownstreamIDTokenUsernameToMatch: func(_ string) string { return "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?sub=") + ".+" }, + }, + { + name: "oidc IDP using configmaps to source ca bundle with default username and groups claim settings", + maybeSkip: skipNever, + createIDP: func(t *testing.T) string { + idpSpec := basicOIDCIdentityProviderSpec() + caData, _ := base64.StdEncoding.DecodeString(idpSpec.TLS.CertificateAuthorityData) + caConfigMap := testlib.CreateTestConfigMap(t, env.SupervisorNamespace, "ca-cert", map[string]string{ + "ca.crt": string(caData), + }) + idpSpec.TLS.CertificateAuthorityData = "" + idpSpec.TLS.CertificateAuthorityDataSource = &idpv1alpha1.CABundleSource{ + Kind: "ConfigMap", + Name: caConfigMap.Name, + Key: "ca.crt", + } + + return testlib.CreateTestOIDCIdentityProvider(t, idpSpec, idpv1alpha1.PhaseReady).Name + }, + requestAuthorization: requestAuthorizationUsingBrowserAuthcodeFlowOIDC, + breakRefreshSessionData: func(t *testing.T, pinnipedSession *psession.PinnipedSession, _, _ string) { + pinnipedSessionData := pinnipedSession.Custom + pinnipedSessionData.OIDC.UpstreamIssuer = "wrong-issuer" + }, + wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamOIDC, + // the ID token Username should include the upstream user ID after the upstream issuer name + wantDownstreamIDTokenUsernameToMatch: func(_ string) string { return "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?sub=") + ".+" }, + }, + + { + name: "oidc IDP using secrets of type opaque to source ca bundle with default username and groups claim settings", + maybeSkip: skipNever, + createIDP: func(t *testing.T) string { + idpSpec := basicOIDCIdentityProviderSpec() + caData, _ := base64.StdEncoding.DecodeString(idpSpec.TLS.CertificateAuthorityData) + caSecret := testlib.CreateTestSecret(t, env.SupervisorNamespace, "ca-cert", corev1.SecretTypeOpaque, + map[string]string{ + "ca.crt": string(caData), + }) + idpSpec.TLS.CertificateAuthorityData = "" + idpSpec.TLS.CertificateAuthorityDataSource = &idpv1alpha1.CABundleSource{ + Kind: "Secret", + Name: caSecret.Name, + Key: "ca.crt", + } + + return testlib.CreateTestOIDCIdentityProvider(t, idpSpec, idpv1alpha1.PhaseReady).Name + }, + requestAuthorization: requestAuthorizationUsingBrowserAuthcodeFlowOIDC, + breakRefreshSessionData: func(t *testing.T, pinnipedSession *psession.PinnipedSession, _, _ string) { + pinnipedSessionData := pinnipedSession.Custom + pinnipedSessionData.OIDC.UpstreamIssuer = "wrong-issuer" + }, + wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamOIDC, + // the ID token Username should include the upstream user ID after the upstream issuer name + wantDownstreamIDTokenUsernameToMatch: func(_ string) string { return "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?sub=") + ".+" }, + }, + { name: "oidc with custom username and groups claim settings", maybeSkip: skipNever, @@ -534,6 +650,162 @@ func TestSupervisorLogin_Browser(t *testing.T) { }, wantDownstreamIDTokenGroups: env.SupervisorUpstreamLDAP.TestUserDirectGroupsDNs, }, + { + name: "ldap IDP using secrets of type opaque to source ca bundle and with email as username and groups names as DNs and using an LDAP provider which supports TLS", + maybeSkip: skipLDAPTests, + createIDP: func(t *testing.T) string { + + idp, _ := createLDAPIdentityProvider(t, func(spec *idpv1alpha1.LDAPIdentityProviderSpec) { + caSecret := testlib.CreateTestSecret(t, env.SupervisorNamespace, "ca-cert", corev1.SecretTypeOpaque, + map[string]string{ + "ca.crt": env.SupervisorUpstreamLDAP.CABundle, + }) + + spec.TLS.CertificateAuthorityData = "" + spec.TLS.CertificateAuthorityDataSource = &idpv1alpha1.CABundleSource{ + Kind: "Secret", + Name: caSecret.Name, + Key: "ca.crt", + } + }) + return idp.Name + }, + requestAuthorization: func(t *testing.T, _, downstreamAuthorizeURL, _, _, _ string, httpClient *http.Client) { + requestAuthorizationUsingCLIPasswordFlow(t, + downstreamAuthorizeURL, + env.SupervisorUpstreamLDAP.TestUserMailAttributeValue, // username to present to server during login + env.SupervisorUpstreamLDAP.TestUserPassword, // password to present to server during login + httpClient, + false, + ) + }, + editRefreshSessionDataWithoutBreaking: func(t *testing.T, sessionData *psession.PinnipedSession, _, _ string) []string { + // Even if we update this group to the some names that did not come from the LDAP server, + // we expect that it will return to the real groups from the LDAP server after we refresh. + initialGroupMembership := []string{"some-wrong-group", "some-other-group"} + sessionData.Custom.UpstreamGroups = initialGroupMembership // upstream group names in session + sessionData.Fosite.Claims.Extra["groups"] = initialGroupMembership // downstream group names in session + return env.SupervisorUpstreamLDAP.TestUserDirectGroupsDNs + }, + breakRefreshSessionData: func(t *testing.T, pinnipedSession *psession.PinnipedSession, _, _ string) { + customSessionData := pinnipedSession.Custom + require.Equal(t, psession.ProviderTypeLDAP, customSessionData.ProviderType) + require.NotEmpty(t, customSessionData.LDAP.UserDN) + fositeSessionData := pinnipedSession.Fosite + fositeSessionData.Claims.Subject = "not-right" + }, + wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamLDAP, + // the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute + wantDownstreamIDTokenUsernameToMatch: func(_ string) string { + return "^" + regexp.QuoteMeta(env.SupervisorUpstreamLDAP.TestUserMailAttributeValue) + "$" + }, + wantDownstreamIDTokenGroups: env.SupervisorUpstreamLDAP.TestUserDirectGroupsDNs, + }, + { + name: "ldap IDP using secrets of type TLS to source ca bundle and with email as username and groups names as DNs and using an LDAP provider which supports TLS", + maybeSkip: skipLDAPTests, + createIDP: func(t *testing.T) string { + + idp, _ := createLDAPIdentityProvider(t, func(spec *idpv1alpha1.LDAPIdentityProviderSpec) { + caSecret := testlib.CreateTestSecret(t, env.SupervisorNamespace, "ca-cert", corev1.SecretTypeTLS, + map[string]string{ + "ca.crt": env.SupervisorUpstreamLDAP.CABundle, + "tls.crt": "", + "tls.key": "", + }) + + spec.TLS.CertificateAuthorityData = "" + spec.TLS.CertificateAuthorityDataSource = &idpv1alpha1.CABundleSource{ + Kind: "Secret", + Name: caSecret.Name, + Key: "ca.crt", + } + }) + return idp.Name + }, + requestAuthorization: func(t *testing.T, _, downstreamAuthorizeURL, _, _, _ string, httpClient *http.Client) { + requestAuthorizationUsingCLIPasswordFlow(t, + downstreamAuthorizeURL, + env.SupervisorUpstreamLDAP.TestUserMailAttributeValue, // username to present to server during login + env.SupervisorUpstreamLDAP.TestUserPassword, // password to present to server during login + httpClient, + false, + ) + }, + editRefreshSessionDataWithoutBreaking: func(t *testing.T, sessionData *psession.PinnipedSession, _, _ string) []string { + // Even if we update this group to the some names that did not come from the LDAP server, + // we expect that it will return to the real groups from the LDAP server after we refresh. + initialGroupMembership := []string{"some-wrong-group", "some-other-group"} + sessionData.Custom.UpstreamGroups = initialGroupMembership // upstream group names in session + sessionData.Fosite.Claims.Extra["groups"] = initialGroupMembership // downstream group names in session + return env.SupervisorUpstreamLDAP.TestUserDirectGroupsDNs + }, + breakRefreshSessionData: func(t *testing.T, pinnipedSession *psession.PinnipedSession, _, _ string) { + customSessionData := pinnipedSession.Custom + require.Equal(t, psession.ProviderTypeLDAP, customSessionData.ProviderType) + require.NotEmpty(t, customSessionData.LDAP.UserDN) + fositeSessionData := pinnipedSession.Fosite + fositeSessionData.Claims.Subject = "not-right" + }, + wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamLDAP, + // the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute + wantDownstreamIDTokenUsernameToMatch: func(_ string) string { + return "^" + regexp.QuoteMeta(env.SupervisorUpstreamLDAP.TestUserMailAttributeValue) + "$" + }, + wantDownstreamIDTokenGroups: env.SupervisorUpstreamLDAP.TestUserDirectGroupsDNs, + }, + { + name: "ldap IDP using configmaps to source ca bundle and with email as username and groups names as DNs and using an LDAP provider which supports TLS", + maybeSkip: skipLDAPTests, + createIDP: func(t *testing.T) string { + + idp, _ := createLDAPIdentityProvider(t, func(spec *idpv1alpha1.LDAPIdentityProviderSpec) { + + caConfigMap := testlib.CreateTestConfigMap(t, env.SupervisorNamespace, "ca-cert", + map[string]string{ + "ca.crt": env.SupervisorUpstreamLDAP.CABundle, + }) + + spec.TLS.CertificateAuthorityData = "" + spec.TLS.CertificateAuthorityDataSource = &idpv1alpha1.CABundleSource{ + Kind: "ConfigMap", + Name: caConfigMap.Name, + Key: "ca.crt", + } + }) + return idp.Name + }, + requestAuthorization: func(t *testing.T, _, downstreamAuthorizeURL, _, _, _ string, httpClient *http.Client) { + requestAuthorizationUsingCLIPasswordFlow(t, + downstreamAuthorizeURL, + env.SupervisorUpstreamLDAP.TestUserMailAttributeValue, // username to present to server during login + env.SupervisorUpstreamLDAP.TestUserPassword, // password to present to server during login + httpClient, + false, + ) + }, + editRefreshSessionDataWithoutBreaking: func(t *testing.T, sessionData *psession.PinnipedSession, _, _ string) []string { + // Even if we update this group to the some names that did not come from the LDAP server, + // we expect that it will return to the real groups from the LDAP server after we refresh. + initialGroupMembership := []string{"some-wrong-group", "some-other-group"} + sessionData.Custom.UpstreamGroups = initialGroupMembership // upstream group names in session + sessionData.Fosite.Claims.Extra["groups"] = initialGroupMembership // downstream group names in session + return env.SupervisorUpstreamLDAP.TestUserDirectGroupsDNs + }, + breakRefreshSessionData: func(t *testing.T, pinnipedSession *psession.PinnipedSession, _, _ string) { + customSessionData := pinnipedSession.Custom + require.Equal(t, psession.ProviderTypeLDAP, customSessionData.ProviderType) + require.NotEmpty(t, customSessionData.LDAP.UserDN) + fositeSessionData := pinnipedSession.Fosite + fositeSessionData.Claims.Subject = "not-right" + }, + wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamLDAP, + // the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute + wantDownstreamIDTokenUsernameToMatch: func(_ string) string { + return "^" + regexp.QuoteMeta(env.SupervisorUpstreamLDAP.TestUserMailAttributeValue) + "$" + }, + wantDownstreamIDTokenGroups: env.SupervisorUpstreamLDAP.TestUserDirectGroupsDNs, + }, { name: "ldap using posix groups by using the UserAttributeForFilter option to adjust the group search filter behavior", maybeSkip: skipLDAPTests, @@ -969,6 +1241,134 @@ func TestSupervisorLogin_Browser(t *testing.T) { }, wantDownstreamIDTokenGroups: env.SupervisorUpstreamActiveDirectory.TestUserIndirectGroupsSAMAccountPlusDomainNames, }, + // TODO: this test is disabled- where can this be run? + { + name: "active directory IDP using secret of type opaque to source ca bundle with all default options", + maybeSkip: skipActiveDirectoryTests, + createIDP: func(t *testing.T) string { + idp, _ := createActiveDirectoryIdentityProvider(t, func(spec *idpv1alpha1.ActiveDirectoryIdentityProviderSpec) { + caSecret := testlib.CreateTestSecret(t, env.SupervisorNamespace, "ca-cert", corev1.SecretTypeOpaque, + map[string]string{ + "ca.crt": env.SupervisorUpstreamActiveDirectory.CABundle, + }) + + spec.TLS.CertificateAuthorityData = "" + spec.TLS.CertificateAuthorityDataSource = &idpv1alpha1.CABundleSource{ + Kind: "Secret", + Name: caSecret.Name, + Key: "ca.crt", + } + }) + return idp.Name + }, + requestAuthorization: func(t *testing.T, _, downstreamAuthorizeURL, _, _, _ string, httpClient *http.Client) { + requestAuthorizationUsingCLIPasswordFlow(t, + downstreamAuthorizeURL, + env.SupervisorUpstreamActiveDirectory.TestUserPrincipalNameValue, // username to present to server during login + env.SupervisorUpstreamActiveDirectory.TestUserPassword, // password to present to server during login + httpClient, + false, + ) + }, + breakRefreshSessionData: func(t *testing.T, pinnipedSession *psession.PinnipedSession, _, _ string) { + customSessionData := pinnipedSession.Custom + require.Equal(t, psession.ProviderTypeActiveDirectory, customSessionData.ProviderType) + require.NotEmpty(t, customSessionData.ActiveDirectory.UserDN) + customSessionData.Username = "not-the-same" + }, + wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamAD, + // the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute + wantDownstreamIDTokenUsernameToMatch: func(_ string) string { + return "^" + regexp.QuoteMeta(env.SupervisorUpstreamActiveDirectory.TestUserPrincipalNameValue) + "$" + }, + wantDownstreamIDTokenGroups: env.SupervisorUpstreamActiveDirectory.TestUserIndirectGroupsSAMAccountPlusDomainNames, + }, + // TODO: this test is disabled- where can this be run? + { + name: "active directory IDP using secret of type TLS to source ca bundle with all default options", + maybeSkip: skipActiveDirectoryTests, + createIDP: func(t *testing.T) string { + idp, _ := createActiveDirectoryIdentityProvider(t, func(spec *idpv1alpha1.ActiveDirectoryIdentityProviderSpec) { + caSecret := testlib.CreateTestSecret(t, env.SupervisorNamespace, "ca-cert", corev1.SecretTypeTLS, + map[string]string{ + "ca.crt": env.SupervisorUpstreamActiveDirectory.CABundle, + "tls.crt": "", + "tls.key": "", + }) + + spec.TLS.CertificateAuthorityData = "" + spec.TLS.CertificateAuthorityDataSource = &idpv1alpha1.CABundleSource{ + Kind: "Secret", + Name: caSecret.Name, + Key: "ca.crt", + } + }) + return idp.Name + }, + requestAuthorization: func(t *testing.T, _, downstreamAuthorizeURL, _, _, _ string, httpClient *http.Client) { + requestAuthorizationUsingCLIPasswordFlow(t, + downstreamAuthorizeURL, + env.SupervisorUpstreamActiveDirectory.TestUserPrincipalNameValue, // username to present to server during login + env.SupervisorUpstreamActiveDirectory.TestUserPassword, // password to present to server during login + httpClient, + false, + ) + }, + breakRefreshSessionData: func(t *testing.T, pinnipedSession *psession.PinnipedSession, _, _ string) { + customSessionData := pinnipedSession.Custom + require.Equal(t, psession.ProviderTypeActiveDirectory, customSessionData.ProviderType) + require.NotEmpty(t, customSessionData.ActiveDirectory.UserDN) + customSessionData.Username = "not-the-same" + }, + wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamAD, + // the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute + wantDownstreamIDTokenUsernameToMatch: func(_ string) string { + return "^" + regexp.QuoteMeta(env.SupervisorUpstreamActiveDirectory.TestUserPrincipalNameValue) + "$" + }, + wantDownstreamIDTokenGroups: env.SupervisorUpstreamActiveDirectory.TestUserIndirectGroupsSAMAccountPlusDomainNames, + }, + // TODO: this test is disabled- where can this be run? + { + name: "active directory IDP using configmaps to source ca bundle with all default options", + maybeSkip: skipActiveDirectoryTests, + createIDP: func(t *testing.T) string { + idp, _ := createActiveDirectoryIdentityProvider(t, func(spec *idpv1alpha1.ActiveDirectoryIdentityProviderSpec) { + caConfigMap := testlib.CreateTestConfigMap(t, env.SupervisorNamespace, "ca-cert", + map[string]string{ + "ca.crt": env.SupervisorUpstreamActiveDirectory.CABundle, + }) + + spec.TLS.CertificateAuthorityData = "" + spec.TLS.CertificateAuthorityDataSource = &idpv1alpha1.CABundleSource{ + Kind: "Secret", + Name: caConfigMap.Name, + Key: "ca.crt", + } + }) + return idp.Name + }, + requestAuthorization: func(t *testing.T, _, downstreamAuthorizeURL, _, _, _ string, httpClient *http.Client) { + requestAuthorizationUsingCLIPasswordFlow(t, + downstreamAuthorizeURL, + env.SupervisorUpstreamActiveDirectory.TestUserPrincipalNameValue, // username to present to server during login + env.SupervisorUpstreamActiveDirectory.TestUserPassword, // password to present to server during login + httpClient, + false, + ) + }, + breakRefreshSessionData: func(t *testing.T, pinnipedSession *psession.PinnipedSession, _, _ string) { + customSessionData := pinnipedSession.Custom + require.Equal(t, psession.ProviderTypeActiveDirectory, customSessionData.ProviderType) + require.NotEmpty(t, customSessionData.ActiveDirectory.UserDN) + customSessionData.Username = "not-the-same" + }, + wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamAD, + // the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute + wantDownstreamIDTokenUsernameToMatch: func(_ string) string { + return "^" + regexp.QuoteMeta(env.SupervisorUpstreamActiveDirectory.TestUserPrincipalNameValue) + "$" + }, + wantDownstreamIDTokenGroups: env.SupervisorUpstreamActiveDirectory.TestUserIndirectGroupsSAMAccountPlusDomainNames, + }, { name: "active directory with custom options", maybeSkip: skipActiveDirectoryTests, diff --git a/test/testlib/client.go b/test/testlib/client.go index 504ef0bdf..499a2127f 100644 --- a/test/testlib/client.go +++ b/test/testlib/client.go @@ -453,6 +453,27 @@ func RandHex(t *testing.T, numBytes int) string { return hex.EncodeToString(RandBytes(t, numBytes)) } +func CreateTestConfigMap(t *testing.T, namespace string, baseName string, stringData map[string]string) *corev1.ConfigMap { + t.Helper() + client := NewKubernetesClientset(t) + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + created, err := client.CoreV1().ConfigMaps(namespace).Create(ctx, &corev1.ConfigMap{ + ObjectMeta: TestObjectMeta(t, baseName), + Data: stringData, + }, metav1.CreateOptions{}) + require.NoError(t, err) + + t.Cleanup(func() { + t.Logf("cleaning up test Configmap %s/%s", created.Namespace, created.Name) + err := client.CoreV1().ConfigMaps(namespace).Delete(context.Background(), created.Name, metav1.DeleteOptions{}) + require.NoError(t, err) + }) + t.Logf("created test ConfigMap %s", created.Name) + return created +} + func CreateTestSecret(t *testing.T, namespace string, baseName string, secretType corev1.SecretType, stringData map[string]string) *corev1.Secret { t.Helper() client := NewKubernetesClientset(t) From 6a610a9d5197b7502e5aa7f7cf1d0493ade1b7ad Mon Sep 17 00:00:00 2001 From: Ashish Amarnath Date: Tue, 9 Jul 2024 14:20:52 -0700 Subject: [PATCH 14/99] add namespace to jwt authenticator controller Signed-off-by: Ashish Amarnath --- .../authenticator/jwtcachefiller/jwtcachefiller.go | 5 ++++- .../authenticator/jwtcachefiller/jwtcachefiller_test.go | 1 + internal/controllermanager/prepare_controllers.go | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go index fb68efc38..952d539bd 100644 --- a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go +++ b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go @@ -131,6 +131,7 @@ var _ tokenAuthenticatorCloser = (*cachedJWTAuthenticator)(nil) // New instantiates a new controllerlib.Controller which will populate the provided authncache.Cache. func New( + namespace string, cache *authncache.Cache, client conciergeclientset.Interface, jwtAuthenticators authinformers.JWTAuthenticatorInformer, @@ -148,6 +149,7 @@ func New( jwtAuthenticators: jwtAuthenticators, secretInformer: secretInformer, configMapInformer: configMapInformer, + namespace: namespace, clock: clock, log: log.WithName(controllerName), }, @@ -166,6 +168,7 @@ type jwtCacheFillerController struct { secretInformer corev1informers.SecretInformer configMapInformer corev1informers.ConfigMapInformer client conciergeclientset.Interface + namespace string clock clock.Clock log plog.Logger } @@ -210,7 +213,7 @@ func (c *jwtCacheFillerController) Sync(ctx controllerlib.Context) error { conditions := make([]*metav1.Condition, 0) var errs []error - rootCAs, conditions, tlsOk := c.validateTLSBundle(obj.Spec.TLS, obj.Namespace, conditions) + rootCAs, conditions, tlsOk := c.validateTLSBundle(obj.Spec.TLS, c.namespace, conditions) _, conditions, issuerOk := c.validateIssuer(obj.Spec.Issuer, conditions) okSoFar := tlsOk && issuerOk diff --git a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller_test.go b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller_test.go index 3e5b9729e..0d1a3872a 100644 --- a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller_test.go +++ b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller_test.go @@ -1855,6 +1855,7 @@ func TestController(t *testing.T) { } controller := New( + "concierge", // namespace for the controller cache, pinnipedAPIClient, pinnipedInformers.Authentication().V1alpha1().JWTAuthenticators(), diff --git a/internal/controllermanager/prepare_controllers.go b/internal/controllermanager/prepare_controllers.go index 31c8b81bb..7bb059edc 100644 --- a/internal/controllermanager/prepare_controllers.go +++ b/internal/controllermanager/prepare_controllers.go @@ -247,6 +247,7 @@ func PrepareControllers(c *Config) (controllerinit.RunnerBuilder, error) { //nol ). WithController( jwtcachefiller.New( + c.ServerInstallationInfo.Namespace, c.AuthenticatorCache, client.PinnipedConcierge, informers.pinniped.Authentication().V1alpha1().JWTAuthenticators(), From 8eb15a924fc5b39eab9a3b9154ea51109b64f5ab Mon Sep 17 00:00:00 2001 From: Ashish Amarnath Date: Tue, 9 Jul 2024 14:24:39 -0700 Subject: [PATCH 15/99] integration tests for supervisor oidc, ldap, activedirectory IDP Signed-off-by: Ashish Amarnath --- .../jwtcachefiller/jwtcachefiller.go | 4 +- test/integration/e2e_test.go | 63 ++++++++++++++++++- test/integration/supervisor_login_test.go | 16 ++--- test/testlib/client.go | 4 +- 4 files changed, 73 insertions(+), 14 deletions(-) diff --git a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go index 952d539bd..870feb0e9 100644 --- a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go +++ b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go @@ -144,12 +144,12 @@ func New( controllerlib.Config{ Name: controllerName, Syncer: &jwtCacheFillerController{ + namespace: namespace, cache: cache, client: client, jwtAuthenticators: jwtAuthenticators, secretInformer: secretInformer, configMapInformer: configMapInformer, - namespace: namespace, clock: clock, log: log.WithName(controllerName), }, @@ -163,12 +163,12 @@ func New( } type jwtCacheFillerController struct { + namespace string cache *authncache.Cache jwtAuthenticators authinformers.JWTAuthenticatorInformer secretInformer corev1informers.SecretInformer configMapInformer corev1informers.ConfigMapInformer client conciergeclientset.Interface - namespace string clock clock.Clock log plog.Logger } diff --git a/test/integration/e2e_test.go b/test/integration/e2e_test.go index 66aa863a2..1a51c3a3e 100644 --- a/test/integration/e2e_test.go +++ b/test/integration/e2e_test.go @@ -115,11 +115,11 @@ func TestE2EFullIntegration_Browser(t *testing.T) { // Create a JWTAuthenticator that will validate the tokens from the downstream issuer. // If the FederationDomain is not Ready, the JWTAuthenticator cannot be ready, either. clusterAudience := "test-cluster-" + testlib.RandHex(t, 8) - authenticator := testlib.CreateTestJWTAuthenticator(topSetupCtx, t, authenticationv1alpha1.JWTAuthenticatorSpec{ + defaultJWTAuthenticatorSpec := authenticationv1alpha1.JWTAuthenticatorSpec{ Issuer: federationDomain.Spec.Issuer, Audience: clusterAudience, TLS: &authenticationv1alpha1.TLSSpec{CertificateAuthorityData: testCABundleBase64}, - }, authenticationv1alpha1.JWTAuthenticatorPhaseError) + } // Add an OIDC upstream IDP and try using it to authenticate during kubectl commands. t.Run("with Supervisor OIDC upstream IDP and browser flow with with form_post automatic authcode delivery to CLI", func(t *testing.T) { @@ -146,6 +146,7 @@ func TestE2EFullIntegration_Browser(t *testing.T) { Resource: "namespaces", }) + authenticator := testlib.CreateTestJWTAuthenticator(topSetupCtx, t, defaultJWTAuthenticatorSpec, authenticationv1alpha1.JWTAuthenticatorPhaseError) // Create upstream OIDC provider and wait for it to become ready. createdProvider := testlib.CreateTestOIDCIdentityProvider(t, idpv1alpha1.OIDCIdentityProviderSpec{ Issuer: env.SupervisorUpstreamOIDC.Issuer, @@ -232,6 +233,21 @@ func TestE2EFullIntegration_Browser(t *testing.T) { Resource: "namespaces", }) + // in this test, use a secret of type TLS to source ca bundle for the JWT authenticator + caSecret := testlib.CreateTestSecret(t, env.ConciergeNamespace, "ca-cert", corev1.SecretTypeTLS, + map[string]string{ + "ca.crt": string(testCABundlePEM), + "tls.crt": "", + "tls.key": "", + }) + jwtAuthnSpec := defaultJWTAuthenticatorSpec.DeepCopy() + jwtAuthnSpec.TLS.CertificateAuthorityData = "" + jwtAuthnSpec.TLS.CertificateAuthorityDataSource = &authenticationv1alpha1.CABundleSource{ + Kind: "Secret", + Name: caSecret.Name, + Key: "ca.crt", + } + authenticator := testlib.CreateTestJWTAuthenticator(topSetupCtx, t, *jwtAuthnSpec, authenticationv1alpha1.JWTAuthenticatorPhaseError) // Create upstream OIDC provider and wait for it to become ready. createdProvider := testlib.CreateTestOIDCIdentityProvider(t, idpv1alpha1.OIDCIdentityProviderSpec{ Issuer: env.SupervisorUpstreamOIDC.Issuer, @@ -320,6 +336,22 @@ func TestE2EFullIntegration_Browser(t *testing.T) { Resource: "namespaces", }) + // in this test, use a secret of type opaque to source ca bundle for the JWT authenticator + caSecret := testlib.CreateTestSecret(t, env.ConciergeNamespace, "ca-cert", corev1.SecretTypeOpaque, + map[string]string{ + "ca.crt": string(testCABundlePEM), + }) + t.Logf("created secret %s/%s", caSecret.Namespace, caSecret.Name) + jwtAuthnSpec := defaultJWTAuthenticatorSpec.DeepCopy() + jwtAuthnSpec.TLS.CertificateAuthorityData = "" + jwtAuthnSpec.TLS.CertificateAuthorityDataSource = &authenticationv1alpha1.CABundleSource{ + Kind: "Secret", + Name: caSecret.Name, + Key: "ca.crt", + } + + authenticator := testlib.CreateTestJWTAuthenticator(topSetupCtx, t, *jwtAuthnSpec, authenticationv1alpha1.JWTAuthenticatorPhaseError) + t.Logf("authenticator: %s/%s; concierge ns: %s", authenticator.Namespace, authenticator.Name, env.ConciergeNamespace) // Create upstream OIDC provider and wait for it to become ready. createdProvider := testlib.CreateTestOIDCIdentityProvider(t, idpv1alpha1.OIDCIdentityProviderSpec{ Issuer: env.SupervisorUpstreamOIDC.Issuer, @@ -444,6 +476,20 @@ func TestE2EFullIntegration_Browser(t *testing.T) { } } + // in this test, use a configmap to source ca bundle for the JWT authenticator + caConfigMap := testlib.CreateTestConfigMap(t, env.ConciergeNamespace, "ca-cert", + map[string]string{ + "ca.crt": string(testCABundlePEM), + }) + jwtAuthnSpec := defaultJWTAuthenticatorSpec.DeepCopy() + jwtAuthnSpec.TLS.CertificateAuthorityData = "" + jwtAuthnSpec.TLS.CertificateAuthorityDataSource = &authenticationv1alpha1.CABundleSource{ + Kind: "ConfigMap", + Name: caConfigMap.Name, + Key: "ca.crt", + } + + authenticator := testlib.CreateTestJWTAuthenticator(topSetupCtx, t, defaultJWTAuthenticatorSpec, authenticationv1alpha1.JWTAuthenticatorPhaseError) // Create upstream OIDC provider and wait for it to become ready. createdProvider := testlib.CreateTestOIDCIdentityProvider(t, idpv1alpha1.OIDCIdentityProviderSpec{ Issuer: env.SupervisorUpstreamOIDC.Issuer, @@ -574,6 +620,7 @@ func TestE2EFullIntegration_Browser(t *testing.T) { Resource: "namespaces", }) + authenticator := testlib.CreateTestJWTAuthenticator(topSetupCtx, t, defaultJWTAuthenticatorSpec, authenticationv1alpha1.JWTAuthenticatorPhaseError) // Create upstream OIDC provider and wait for it to become ready. createdProvider := testlib.CreateTestOIDCIdentityProvider(t, idpv1alpha1.OIDCIdentityProviderSpec{ Issuer: env.SupervisorUpstreamOIDC.Issuer, @@ -647,6 +694,7 @@ func TestE2EFullIntegration_Browser(t *testing.T) { tempDir := t.TempDir() // per-test tmp dir to avoid sharing files between tests + authenticator := testlib.CreateTestJWTAuthenticator(topSetupCtx, t, defaultJWTAuthenticatorSpec, authenticationv1alpha1.JWTAuthenticatorPhaseError) // Create upstream OIDC provider and wait for it to become ready. oidcIdentityProvider := testlib.CreateTestOIDCIdentityProvider(t, idpv1alpha1.OIDCIdentityProviderSpec{ Issuer: env.SupervisorUpstreamOIDC.Issuer, @@ -728,6 +776,7 @@ func TestE2EFullIntegration_Browser(t *testing.T) { expectedUsername := env.SupervisorUpstreamLDAP.TestUserMailAttributeValue expectedGroups := env.SupervisorUpstreamLDAP.TestUserDirectGroupsDNs + authenticator := testlib.CreateTestJWTAuthenticator(topSetupCtx, t, defaultJWTAuthenticatorSpec, authenticationv1alpha1.JWTAuthenticatorPhaseError) createdProvider := setupClusterForEndToEndLDAPTest(t, expectedUsername, env) testlib.WaitForFederationDomainStatusPhase(testCtx, t, federationDomain.Name, supervisorconfigv1alpha1.FederationDomainPhaseReady) testlib.WaitForJWTAuthenticatorStatusPhase(testCtx, t, authenticator.Name, authenticationv1alpha1.JWTAuthenticatorPhaseReady) @@ -787,6 +836,7 @@ func TestE2EFullIntegration_Browser(t *testing.T) { expectedUsername := env.SupervisorUpstreamLDAP.TestUserMailAttributeValue expectedGroups := env.SupervisorUpstreamLDAP.TestUserDirectGroupsDNs + authenticator := testlib.CreateTestJWTAuthenticator(topSetupCtx, t, defaultJWTAuthenticatorSpec, authenticationv1alpha1.JWTAuthenticatorPhaseError) createdProvider := setupClusterForEndToEndLDAPTest(t, expectedUsername, env) testlib.WaitForFederationDomainStatusPhase(testCtx, t, federationDomain.Name, supervisorconfigv1alpha1.FederationDomainPhaseReady) testlib.WaitForJWTAuthenticatorStatusPhase(testCtx, t, authenticator.Name, authenticationv1alpha1.JWTAuthenticatorPhaseReady) @@ -850,6 +900,7 @@ func TestE2EFullIntegration_Browser(t *testing.T) { expectedUsername := env.SupervisorUpstreamLDAP.TestUserMailAttributeValue expectedGroups := env.SupervisorUpstreamLDAP.TestUserDirectGroupsDNs + authenticator := testlib.CreateTestJWTAuthenticator(topSetupCtx, t, defaultJWTAuthenticatorSpec, authenticationv1alpha1.JWTAuthenticatorPhaseError) createdProvider := setupClusterForEndToEndLDAPTest(t, expectedUsername, env) testlib.WaitForFederationDomainStatusPhase(testCtx, t, federationDomain.Name, supervisorconfigv1alpha1.FederationDomainPhaseReady) testlib.WaitForJWTAuthenticatorStatusPhase(testCtx, t, authenticator.Name, authenticationv1alpha1.JWTAuthenticatorPhaseReady) @@ -921,6 +972,7 @@ func TestE2EFullIntegration_Browser(t *testing.T) { expectedUsername := env.SupervisorUpstreamActiveDirectory.TestUserPrincipalNameValue expectedGroups := env.SupervisorUpstreamActiveDirectory.TestUserIndirectGroupsSAMAccountPlusDomainNames + authenticator := testlib.CreateTestJWTAuthenticator(topSetupCtx, t, defaultJWTAuthenticatorSpec, authenticationv1alpha1.JWTAuthenticatorPhaseError) createdProvider := setupClusterForEndToEndActiveDirectoryTest(t, expectedUsername, env) testlib.WaitForFederationDomainStatusPhase(testCtx, t, federationDomain.Name, supervisorconfigv1alpha1.FederationDomainPhaseReady) testlib.WaitForJWTAuthenticatorStatusPhase(testCtx, t, authenticator.Name, authenticationv1alpha1.JWTAuthenticatorPhaseReady) @@ -980,6 +1032,7 @@ func TestE2EFullIntegration_Browser(t *testing.T) { expectedUsername := env.SupervisorUpstreamActiveDirectory.TestUserPrincipalNameValue expectedGroups := env.SupervisorUpstreamActiveDirectory.TestUserIndirectGroupsSAMAccountPlusDomainNames + authenticator := testlib.CreateTestJWTAuthenticator(topSetupCtx, t, defaultJWTAuthenticatorSpec, authenticationv1alpha1.JWTAuthenticatorPhaseError) createdProvider := setupClusterForEndToEndActiveDirectoryTest(t, expectedUsername, env) testlib.WaitForFederationDomainStatusPhase(testCtx, t, federationDomain.Name, supervisorconfigv1alpha1.FederationDomainPhaseReady) testlib.WaitForJWTAuthenticatorStatusPhase(testCtx, t, authenticator.Name, authenticationv1alpha1.JWTAuthenticatorPhaseReady) @@ -1053,6 +1106,7 @@ func TestE2EFullIntegration_Browser(t *testing.T) { expectedUsername := env.SupervisorUpstreamLDAP.TestUserMailAttributeValue expectedGroups := env.SupervisorUpstreamLDAP.TestUserDirectGroupsDNs + authenticator := testlib.CreateTestJWTAuthenticator(topSetupCtx, t, defaultJWTAuthenticatorSpec, authenticationv1alpha1.JWTAuthenticatorPhaseError) createdProvider := setupClusterForEndToEndLDAPTest(t, expectedUsername, env) testlib.WaitForFederationDomainStatusPhase(testCtx, t, federationDomain.Name, supervisorconfigv1alpha1.FederationDomainPhaseReady) testlib.WaitForJWTAuthenticatorStatusPhase(testCtx, t, authenticator.Name, authenticationv1alpha1.JWTAuthenticatorPhaseReady) @@ -1108,6 +1162,7 @@ func TestE2EFullIntegration_Browser(t *testing.T) { expectedUsername := env.SupervisorUpstreamActiveDirectory.TestUserPrincipalNameValue expectedGroups := env.SupervisorUpstreamActiveDirectory.TestUserIndirectGroupsSAMAccountPlusDomainNames + authenticator := testlib.CreateTestJWTAuthenticator(topSetupCtx, t, defaultJWTAuthenticatorSpec, authenticationv1alpha1.JWTAuthenticatorPhaseError) createdProvider := setupClusterForEndToEndActiveDirectoryTest(t, expectedUsername, env) testlib.WaitForFederationDomainStatusPhase(testCtx, t, federationDomain.Name, supervisorconfigv1alpha1.FederationDomainPhaseReady) testlib.WaitForJWTAuthenticatorStatusPhase(testCtx, t, authenticator.Name, authenticationv1alpha1.JWTAuthenticatorPhaseReady) @@ -1163,6 +1218,7 @@ func TestE2EFullIntegration_Browser(t *testing.T) { expectedUsername := env.SupervisorUpstreamLDAP.TestUserMailAttributeValue expectedGroups := env.SupervisorUpstreamLDAP.TestUserDirectGroupsDNs + authenticator := testlib.CreateTestJWTAuthenticator(topSetupCtx, t, defaultJWTAuthenticatorSpec, authenticationv1alpha1.JWTAuthenticatorPhaseError) createdProvider := setupClusterForEndToEndLDAPTest(t, expectedUsername, env) testlib.WaitForFederationDomainStatusPhase(testCtx, t, federationDomain.Name, supervisorconfigv1alpha1.FederationDomainPhaseReady) testlib.WaitForJWTAuthenticatorStatusPhase(testCtx, t, authenticator.Name, authenticationv1alpha1.JWTAuthenticatorPhaseReady) @@ -1253,6 +1309,7 @@ func TestE2EFullIntegration_Browser(t *testing.T) { ).Name, }, }, idpv1alpha1.GitHubPhaseReady) + authenticator := testlib.CreateTestJWTAuthenticator(topSetupCtx, t, defaultJWTAuthenticatorSpec, authenticationv1alpha1.JWTAuthenticatorPhaseError) testlib.WaitForFederationDomainStatusPhase(testCtx, t, federationDomain.Name, supervisorconfigv1alpha1.FederationDomainPhaseReady) testlib.WaitForJWTAuthenticatorStatusPhase(testCtx, t, authenticator.Name, authenticationv1alpha1.JWTAuthenticatorPhaseReady) @@ -1327,6 +1384,7 @@ func TestE2EFullIntegration_Browser(t *testing.T) { createdLDAPProvider := setupClusterForEndToEndLDAPTest(t, expectedDownstreamLDAPUsername, env) + authenticator := testlib.CreateTestJWTAuthenticator(topSetupCtx, t, defaultJWTAuthenticatorSpec, authenticationv1alpha1.JWTAuthenticatorPhaseError) // Having one IDP should put the FederationDomain into a ready state. testlib.WaitForFederationDomainStatusPhase(testCtx, t, federationDomain.Name, supervisorconfigv1alpha1.FederationDomainPhaseReady) testlib.WaitForJWTAuthenticatorStatusPhase(testCtx, t, authenticator.Name, authenticationv1alpha1.JWTAuthenticatorPhaseReady) @@ -1647,6 +1705,7 @@ func TestE2EFullIntegration_Browser(t *testing.T) { createdLDAPProvider := setupClusterForEndToEndLDAPTest(t, expectedDownstreamLDAPUsername, env) + authenticator := testlib.CreateTestJWTAuthenticator(topSetupCtx, t, defaultJWTAuthenticatorSpec, authenticationv1alpha1.JWTAuthenticatorPhaseError) // Having one IDP should put the FederationDomain into a ready state. testlib.WaitForFederationDomainStatusPhase(testCtx, t, federationDomain.Name, supervisorconfigv1alpha1.FederationDomainPhaseReady) testlib.WaitForJWTAuthenticatorStatusPhase(testCtx, t, authenticator.Name, authenticationv1alpha1.JWTAuthenticatorPhaseReady) diff --git a/test/integration/supervisor_login_test.go b/test/integration/supervisor_login_test.go index 07aa179fd..726d31813 100644 --- a/test/integration/supervisor_login_test.go +++ b/test/integration/supervisor_login_test.go @@ -322,7 +322,6 @@ func TestSupervisorLogin_Browser(t *testing.T) { regexp.QuoteMeta("&sub=") + ".+" + "$" - // TODO: update this test table to add 2 tests per IDP each to source ca bundle from secret and cm tests := []*supervisorLoginTestcase{ { name: "oidc with default username and groups claim settings", @@ -344,7 +343,8 @@ func TestSupervisorLogin_Browser(t *testing.T) { maybeSkip: skipNever, createIDP: func(t *testing.T) string { idpSpec := basicOIDCIdentityProviderSpec() - caData, _ := base64.StdEncoding.DecodeString(idpSpec.TLS.CertificateAuthorityData) + caData, err := base64.StdEncoding.DecodeString(idpSpec.TLS.CertificateAuthorityData) + require.NoError(t, err) caSecret := testlib.CreateTestSecret(t, env.SupervisorNamespace, "ca-cert", corev1.SecretTypeOpaque, map[string]string{ "ca.crt": string(caData), @@ -372,7 +372,8 @@ func TestSupervisorLogin_Browser(t *testing.T) { maybeSkip: skipNever, createIDP: func(t *testing.T) string { idpSpec := basicOIDCIdentityProviderSpec() - caData, _ := base64.StdEncoding.DecodeString(idpSpec.TLS.CertificateAuthorityData) + caData, err := base64.StdEncoding.DecodeString(idpSpec.TLS.CertificateAuthorityData) + require.NoError(t, err) caSecret := testlib.CreateTestSecret(t, env.SupervisorNamespace, "ca-cert", corev1.SecretTypeTLS, map[string]string{ "ca.crt": string(caData), @@ -402,7 +403,8 @@ func TestSupervisorLogin_Browser(t *testing.T) { maybeSkip: skipNever, createIDP: func(t *testing.T) string { idpSpec := basicOIDCIdentityProviderSpec() - caData, _ := base64.StdEncoding.DecodeString(idpSpec.TLS.CertificateAuthorityData) + caData, err := base64.StdEncoding.DecodeString(idpSpec.TLS.CertificateAuthorityData) + require.NoError(t, err) caConfigMap := testlib.CreateTestConfigMap(t, env.SupervisorNamespace, "ca-cert", map[string]string{ "ca.crt": string(caData), }) @@ -430,7 +432,8 @@ func TestSupervisorLogin_Browser(t *testing.T) { maybeSkip: skipNever, createIDP: func(t *testing.T) string { idpSpec := basicOIDCIdentityProviderSpec() - caData, _ := base64.StdEncoding.DecodeString(idpSpec.TLS.CertificateAuthorityData) + caData, err := base64.StdEncoding.DecodeString(idpSpec.TLS.CertificateAuthorityData) + require.NoError(t, err) caSecret := testlib.CreateTestSecret(t, env.SupervisorNamespace, "ca-cert", corev1.SecretTypeOpaque, map[string]string{ "ca.crt": string(caData), @@ -654,7 +657,6 @@ func TestSupervisorLogin_Browser(t *testing.T) { name: "ldap IDP using secrets of type opaque to source ca bundle and with email as username and groups names as DNs and using an LDAP provider which supports TLS", maybeSkip: skipLDAPTests, createIDP: func(t *testing.T) string { - idp, _ := createLDAPIdentityProvider(t, func(spec *idpv1alpha1.LDAPIdentityProviderSpec) { caSecret := testlib.CreateTestSecret(t, env.SupervisorNamespace, "ca-cert", corev1.SecretTypeOpaque, map[string]string{ @@ -705,7 +707,6 @@ func TestSupervisorLogin_Browser(t *testing.T) { name: "ldap IDP using secrets of type TLS to source ca bundle and with email as username and groups names as DNs and using an LDAP provider which supports TLS", maybeSkip: skipLDAPTests, createIDP: func(t *testing.T) string { - idp, _ := createLDAPIdentityProvider(t, func(spec *idpv1alpha1.LDAPIdentityProviderSpec) { caSecret := testlib.CreateTestSecret(t, env.SupervisorNamespace, "ca-cert", corev1.SecretTypeTLS, map[string]string{ @@ -758,7 +759,6 @@ func TestSupervisorLogin_Browser(t *testing.T) { name: "ldap IDP using configmaps to source ca bundle and with email as username and groups names as DNs and using an LDAP provider which supports TLS", maybeSkip: skipLDAPTests, createIDP: func(t *testing.T) string { - idp, _ := createLDAPIdentityProvider(t, func(spec *idpv1alpha1.LDAPIdentityProviderSpec) { caConfigMap := testlib.CreateTestConfigMap(t, env.SupervisorNamespace, "ca-cert", diff --git a/test/testlib/client.go b/test/testlib/client.go index 499a2127f..36f455e82 100644 --- a/test/testlib/client.go +++ b/test/testlib/client.go @@ -470,7 +470,7 @@ func CreateTestConfigMap(t *testing.T, namespace string, baseName string, string err := client.CoreV1().ConfigMaps(namespace).Delete(context.Background(), created.Name, metav1.DeleteOptions{}) require.NoError(t, err) }) - t.Logf("created test ConfigMap %s", created.Name) + t.Logf("created test ConfigMap %s/%s", created.Namespace, created.Name) return created } @@ -492,7 +492,7 @@ func CreateTestSecret(t *testing.T, namespace string, baseName string, secretTyp err := client.CoreV1().Secrets(namespace).Delete(context.Background(), created.Name, metav1.DeleteOptions{}) require.NoError(t, err) }) - t.Logf("created test Secret %s", created.Name) + t.Logf("created test Secret %s/%s", created.Namespace, created.Name) return created } From cb4b63f8b356442d255897b98d906ae50df2894d Mon Sep 17 00:00:00 2001 From: Ashish Amarnath Date: Wed, 10 Jul 2024 00:15:16 -0700 Subject: [PATCH 16/99] integration tests for concierge authenticators Signed-off-by: Ashish Amarnath --- .../webhookcachefiller/webhookcachefiller.go | 26 +++- .../webhookcachefiller_test.go | 1 + .../controllermanager/prepare_controllers.go | 1 + test/integration/concierge_client_test.go | 123 ++++++++++++++---- 4 files changed, 122 insertions(+), 29 deletions(-) diff --git a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go index ef13bef0d..f3615eae3 100644 --- a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go +++ b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go @@ -14,6 +14,7 @@ import ( "time" k8sauthv1beta1 "k8s.io/api/authentication/v1beta1" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/equality" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -66,6 +67,7 @@ type cachedWebhookAuthenticator struct { // New instantiates a new controllerlib.Controller which will populate the provided authncache.Cache. func New( + namespace string, cache *authncache.Cache, client conciergeclientset.Interface, webhooks authinformers.WebhookAuthenticatorInformer, @@ -78,6 +80,7 @@ func New( controllerlib.Config{ Name: controllerName, Syncer: &webhookCacheFillerController{ + namespace: namespace, cache: cache, client: client, webhooks: webhooks, @@ -92,11 +95,28 @@ func New( pinnipedcontroller.MatchAnythingFilter(nil), // nil parent func is fine because each event is distinct controllerlib.InformerOption{}, ), + controllerlib.WithInformer( + secretInformer, + pinnipedcontroller.MatchAnySecretOfTypesFilter( + []corev1.SecretType{ + corev1.SecretTypeOpaque, + corev1.SecretTypeTLS, + }, + pinnipedcontroller.SingletonQueue(), + ), // nil parent func is fine because each event is distinct + controllerlib.InformerOption{}, + ), + controllerlib.WithInformer( + configMapInformer, + pinnipedcontroller.MatchAnythingFilter(pinnipedcontroller.SingletonQueue()), + controllerlib.InformerOption{}, + ), ) } type webhookCacheFillerController struct { cache *authncache.Cache + namespace string webhooks authinformers.WebhookAuthenticatorInformer secretInformer corev1informers.SecretInformer configMapInformer corev1informers.ConfigMapInformer @@ -144,7 +164,7 @@ func (c *webhookCacheFillerController) Sync(ctx controllerlib.Context) error { conditions := make([]*metav1.Condition, 0) var errs []error - certPool, pemBytes, conditions, tlsBundleOk := c.validateTLSBundle(obj.Spec.TLS, obj.Namespace, conditions) + certPool, pemBytes, conditions, tlsBundleOk := c.validateTLSBundle(obj.Spec.TLS, conditions) endpointHostPort, conditions, endpointOk := c.validateEndpoint(obj.Spec.Endpoint, conditions) okSoFar := tlsBundleOk && endpointOk @@ -320,11 +340,11 @@ func (c *webhookCacheFillerController) validateConnection(certPool *x509.CertPoo return conditions, nil } -func (c *webhookCacheFillerController) validateTLSBundle(tlsSpec *authenticationv1alpha1.TLSSpec, namespace string, conditions []*metav1.Condition) (*x509.CertPool, []byte, []*metav1.Condition, bool) { +func (c *webhookCacheFillerController) validateTLSBundle(tlsSpec *authenticationv1alpha1.TLSSpec, conditions []*metav1.Condition) (*x509.CertPool, []byte, []*metav1.Condition, bool) { condition, pemBytes, rootCAs, _ := tlsconfigutil.ValidateTLSConfig( tlsconfigutil.TlsSpecForConcierge(tlsSpec), "spec.tls", - namespace, + c.namespace, c.secretInformer, c.configMapInformer) diff --git a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go index bc6157adb..49ab602e3 100644 --- a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go +++ b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go @@ -1501,6 +1501,7 @@ func TestController(t *testing.T) { } controller := New( + "concierge", // namespace for controller cache, pinnipedAPIClient, informers.Authentication().V1alpha1().WebhookAuthenticators(), diff --git a/internal/controllermanager/prepare_controllers.go b/internal/controllermanager/prepare_controllers.go index 7bb059edc..7dc14c629 100644 --- a/internal/controllermanager/prepare_controllers.go +++ b/internal/controllermanager/prepare_controllers.go @@ -235,6 +235,7 @@ func PrepareControllers(c *Config) (controllerinit.RunnerBuilder, error) { //nol // authenticators up to date. WithController( webhookcachefiller.New( + c.ServerInstallationInfo.Namespace, c.AuthenticatorCache, client.PinnipedConcierge, informers.pinniped.Authentication().V1alpha1().WebhookAuthenticators(), diff --git a/test/integration/concierge_client_test.go b/test/integration/concierge_client_test.go index 7ea58ee97..2d6eb428a 100644 --- a/test/integration/concierge_client_test.go +++ b/test/integration/concierge_client_test.go @@ -5,11 +5,13 @@ package integration import ( "context" + "encoding/base64" "strings" "testing" "time" "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" authenticationv1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/authentication/v1alpha1" "go.pinniped.dev/internal/here" @@ -59,34 +61,103 @@ func TestClient(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Minute) defer cancel() - webhook := testlib.CreateTestWebhookAuthenticator(ctx, t, &testlib.IntegrationEnv(t).TestWebhook, authenticationv1alpha1.WebhookAuthenticatorPhaseReady) - - // Use an invalid certificate/key to validate that the ServerVersion API fails like we assume. - invalidClient := testlib.NewClientsetWithCertAndKey(t, testCert, testKey) - _, err := invalidClient.Discovery().ServerVersion() - require.EqualError(t, err, "the server has asked for the client to provide credentials") - - // Using the CA bundle and host from the current (admin) kubeconfig, do the token exchange. - clientConfig := testlib.NewClientConfig(t) - client, err := conciergeclient.New( - conciergeclient.WithCABundle(string(clientConfig.CAData)), - conciergeclient.WithEndpoint(clientConfig.Host), - conciergeclient.WithAuthenticator("webhook", webhook.Name), - conciergeclient.WithAPIGroupSuffix(env.APIGroupSuffix), - ) + defaultWebhook := &testlib.IntegrationEnv(t).TestWebhook + TLSCABundle, err := base64.StdEncoding.DecodeString(env.TestWebhook.TLS.CertificateAuthorityData) require.NoError(t, err) - testlib.RequireEventually(t, func(requireEventually *require.Assertions) { - resp, err := client.ExchangeToken(ctx, env.TestUser.Token) - requireEventually.NoError(err) - requireEventually.NotNil(resp.Status.ExpirationTimestamp) - requireEventually.InDelta(5*time.Minute, time.Until(resp.Status.ExpirationTimestamp.Time), float64(time.Minute)) + tests := []struct { + name string + edit func(t *testing.T, spec *authenticationv1alpha1.WebhookAuthenticatorSpec) + }{ + { + name: "default webhook authenticator", + edit: nil, + }, + { + name: "webhook authenticator with secret of type TLS to source ca bundle", + edit: func(t *testing.T, spec *authenticationv1alpha1.WebhookAuthenticatorSpec) { + caSecret := testlib.CreateTestSecret(t, env.ConciergeNamespace, "ca-cert", corev1.SecretTypeTLS, + map[string]string{ + "ca.crt": string(TLSCABundle), + "tls.crt": "", + "tls.key": "", + }) + spec.TLS.CertificateAuthorityData = "" + spec.TLS.CertificateAuthorityDataSource = &authenticationv1alpha1.CABundleSource{ + Kind: "Secret", + Name: caSecret.Name, + Key: "ca.crt", + } + }, + }, + { + name: "webhook authenticator with secret of type opaque to source ca bundle", + edit: func(t *testing.T, spec *authenticationv1alpha1.WebhookAuthenticatorSpec) { + caSecret := testlib.CreateTestSecret(t, env.ConciergeNamespace, "ca-cert", corev1.SecretTypeOpaque, + map[string]string{ + "ca.crt": string(TLSCABundle), + }) + spec.TLS.CertificateAuthorityData = "" + spec.TLS.CertificateAuthorityDataSource = &authenticationv1alpha1.CABundleSource{ + Kind: "Secret", + Name: caSecret.Name, + Key: "ca.crt", + } + }, + }, + { + name: "webhook authenticator with configmap to source ca bundle", + edit: func(t *testing.T, spec *authenticationv1alpha1.WebhookAuthenticatorSpec) { + caConfigmap := testlib.CreateTestConfigMap(t, env.ConciergeNamespace, "ca-cert", + map[string]string{ + "ca.crt": string(TLSCABundle), + }) + spec.TLS.CertificateAuthorityData = "" + spec.TLS.CertificateAuthorityDataSource = &authenticationv1alpha1.CABundleSource{ + Kind: "ConfigMap", + Name: caConfigmap.Name, + Key: "ca.crt", + } + }, + }, + } - // Create a client using the certificate and key returned by the token exchange. - validClient := testlib.NewClientsetWithCertAndKey(t, resp.Status.ClientCertificateData, resp.Status.ClientKeyData) + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + webhookSpec := defaultWebhook.DeepCopy() + if test.edit != nil { + test.edit(t, webhookSpec) + } + webhook := testlib.CreateTestWebhookAuthenticator(ctx, t, webhookSpec, authenticationv1alpha1.WebhookAuthenticatorPhaseReady) - // Make a version request, which should succeed even without any authorization. - _, err = validClient.Discovery().ServerVersion() - requireEventually.NoError(err) - }, 10*time.Second, 500*time.Millisecond) + // Use an invalid certificate/key to validate that the ServerVersion API fails like we assume. + invalidClient := testlib.NewClientsetWithCertAndKey(t, testCert, testKey) + _, err := invalidClient.Discovery().ServerVersion() + require.EqualError(t, err, "the server has asked for the client to provide credentials") + + // Using the CA bundle and host from the current (admin) kubeconfig, do the token exchange. + clientConfig := testlib.NewClientConfig(t) + client, err := conciergeclient.New( + conciergeclient.WithCABundle(string(clientConfig.CAData)), + conciergeclient.WithEndpoint(clientConfig.Host), + conciergeclient.WithAuthenticator("webhook", webhook.Name), + conciergeclient.WithAPIGroupSuffix(env.APIGroupSuffix), + ) + require.NoError(t, err) + + testlib.RequireEventually(t, func(requireEventually *require.Assertions) { + resp, err := client.ExchangeToken(ctx, env.TestUser.Token) + requireEventually.NoError(err) + requireEventually.NotNil(resp.Status.ExpirationTimestamp) + requireEventually.InDelta(5*time.Minute, time.Until(resp.Status.ExpirationTimestamp.Time), float64(time.Minute)) + + // Create a client using the certificate and key returned by the token exchange. + validClient := testlib.NewClientsetWithCertAndKey(t, resp.Status.ClientCertificateData, resp.Status.ClientKeyData) + + // Make a version request, which should succeed even without any authorization. + _, err = validClient.Discovery().ServerVersion() + requireEventually.NoError(err) + }, 10*time.Second, 500*time.Millisecond) + }) + } } From 1b7a26d932e98e1163c9ee0402ff9dc80b5e5eec Mon Sep 17 00:00:00 2001 From: Ashish Amarnath Date: Wed, 10 Jul 2024 00:53:06 -0700 Subject: [PATCH 17/99] test secret and configmap filtering in concierge authenticator controllers Signed-off-by: Ashish Amarnath --- .../jwtcachefiller/jwtcachefiller.go | 20 ++- .../jwtcachefiller/jwtcachefiller_test.go | 152 ++++++++++++++++++ .../webhookcachefiller/webhookcachefiller.go | 7 +- .../webhookcachefiller_test.go | 151 +++++++++++++++++ .../github_upstream_watcher_test.go | 4 +- .../controllermanager/prepare_controllers.go | 2 + test/integration/supervisor_login_test.go | 1 - 7 files changed, 330 insertions(+), 7 deletions(-) diff --git a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go index 870feb0e9..f2e1127db 100644 --- a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go +++ b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go @@ -18,6 +18,7 @@ import ( coreosoidc "github.com/coreos/go-oidc/v3/oidc" "github.com/go-jose/go-jose/v4" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/equality" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -137,6 +138,7 @@ func New( jwtAuthenticators authinformers.JWTAuthenticatorInformer, secretInformer corev1informers.SecretInformer, configMapInformer corev1informers.ConfigMapInformer, + withInformer pinnipedcontroller.WithInformerOptionFunc, clock clock.Clock, log plog.Logger, ) controllerlib.Controller { @@ -154,11 +156,27 @@ func New( log: log.WithName(controllerName), }, }, - controllerlib.WithInformer( + withInformer( jwtAuthenticators, pinnipedcontroller.MatchAnythingFilter(nil), // nil parent func is fine because each event is distinct controllerlib.InformerOption{}, ), + withInformer( + secretInformer, + pinnipedcontroller.MatchAnySecretOfTypesFilter( + []corev1.SecretType{ + corev1.SecretTypeOpaque, + corev1.SecretTypeTLS, + }, + pinnipedcontroller.SingletonQueue(), + ), // nil parent func is fine because each event is distinct + controllerlib.InformerOption{}, + ), + withInformer( + configMapInformer, + pinnipedcontroller.MatchAnythingFilter(pinnipedcontroller.SingletonQueue()), + controllerlib.InformerOption{}, + ), ) } diff --git a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller_test.go b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller_test.go index 0d1a3872a..048270c46 100644 --- a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller_test.go +++ b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller_test.go @@ -26,12 +26,14 @@ import ( "github.com/google/go-cmp/cmp" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/apiserver/pkg/authentication/authenticator" "k8s.io/apiserver/pkg/authentication/user" + k8sinformers "k8s.io/client-go/informers" kubeinformers "k8s.io/client-go/informers" kubernetesfake "k8s.io/client-go/kubernetes/fake" coretesting "k8s.io/client-go/testing" @@ -1845,6 +1847,7 @@ func TestController(t *testing.T) { } pinnipedInformers := conciergeinformers.NewSharedInformerFactory(pinnipedAPIClient, 0) kubeInformers := kubeinformers.NewSharedInformerFactory(kubernetesfake.NewSimpleClientset(), 0) + observableInformers := testutil.NewObservableWithInformerOption() cache := authncache.New() var log bytes.Buffer @@ -1861,6 +1864,7 @@ func TestController(t *testing.T) { pinnipedInformers.Authentication().V1alpha1().JWTAuthenticators(), kubeInformers.Core().V1().Secrets(), kubeInformers.Core().V1().ConfigMaps(), + observableInformers.WithInformer, frozenClock, logger) @@ -1868,6 +1872,8 @@ func TestController(t *testing.T) { defer cancel() pinnipedInformers.Start(ctx.Done()) + kubeInformers.Start(ctx.Done()) + controllerlib.TestRunSynchronously(t, controller) syncCtx := controllerlib.Context{Context: ctx, Key: tt.syncKey} @@ -2275,3 +2281,149 @@ func newCacheValue(t *testing.T, spec authenticationv1alpha1.JWTAuthenticatorSpe }, } } + +func TestControllerFilterSecret(t *testing.T) { + tests := []struct { + name string + secret metav1.Object + wantAdd bool + wantUpdate bool + wantDelete bool + }{ + { + name: "should return true for a secret of the type Opaque", + secret: &corev1.Secret{ + Type: corev1.SecretTypeOpaque, + ObjectMeta: metav1.ObjectMeta{ + Name: "some-name", + }, + }, + wantAdd: true, + wantUpdate: true, + wantDelete: true, + }, + { + name: "should return true for a secret of the type TLS", + secret: &corev1.Secret{ + Type: corev1.SecretTypeTLS, + ObjectMeta: metav1.ObjectMeta{ + Name: "some-name", + }, + }, + wantAdd: true, + wantUpdate: true, + wantDelete: true, + }, + { + name: "should return false for a secret of the wrong type", + secret: &corev1.Secret{ + Type: "other-type", + ObjectMeta: metav1.ObjectMeta{ + Name: "some-name", + }, + }, + }, + { + name: "should return false for a resource of wrong data type", + secret: &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{Name: "some-name", Namespace: "some-namespace"}, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + var log bytes.Buffer + logger := plog.TestLogger(t, &log) + + nowDoesntMatter := time.Date(1122, time.September, 33, 4, 55, 56, 778899, time.Local) + frozenClock := clocktesting.NewFakeClock(nowDoesntMatter) + + kubeInformers := k8sinformers.NewSharedInformerFactoryWithOptions(kubernetesfake.NewSimpleClientset(), 0) + secretInformer := kubeInformers.Core().V1().Secrets() + configMapInformer := kubeInformers.Core().V1().ConfigMaps() + pinnipedAPIClient := conciergefake.NewSimpleClientset() + pinnipedInformers := conciergeinformers.NewSharedInformerFactory(pinnipedAPIClient, 0) + observableInformers := testutil.NewObservableWithInformerOption() + + _ = New( + "concierge", // namespace for the controller + authncache.New(), + pinnipedAPIClient, + pinnipedInformers.Authentication().V1alpha1().JWTAuthenticators(), + secretInformer, + configMapInformer, + observableInformers.WithInformer, + frozenClock, + logger) + + unrelated := &corev1.Secret{} + filter := observableInformers.GetFilterForInformer(secretInformer) + require.Equal(t, tt.wantAdd, filter.Add(tt.secret)) + require.Equal(t, tt.wantUpdate, filter.Update(unrelated, tt.secret)) + require.Equal(t, tt.wantUpdate, filter.Update(tt.secret, unrelated)) + require.Equal(t, tt.wantDelete, filter.Delete(tt.secret)) + }) + } +} + +func TestControllerFilterConfigMap(t *testing.T) { + namespace := "some-namespace" + goodCM := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + }, + } + + tests := []struct { + name string + cm metav1.Object + wantAdd bool + wantUpdate bool + wantDelete bool + }{ + { + name: "a configMap in the right namespace", + cm: goodCM, + wantAdd: true, + wantUpdate: true, + wantDelete: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + var log bytes.Buffer + logger := plog.TestLogger(t, &log) + + nowDoesntMatter := time.Date(1122, time.September, 33, 4, 55, 56, 778899, time.Local) + frozenClock := clocktesting.NewFakeClock(nowDoesntMatter) + + kubeInformers := k8sinformers.NewSharedInformerFactoryWithOptions(kubernetesfake.NewSimpleClientset(), 0) + secretInformer := kubeInformers.Core().V1().Secrets() + configMapInformer := kubeInformers.Core().V1().ConfigMaps() + pinnipedAPIClient := conciergefake.NewSimpleClientset() + pinnipedInformers := conciergeinformers.NewSharedInformerFactory(pinnipedAPIClient, 0) + observableInformers := testutil.NewObservableWithInformerOption() + + _ = New( + "concierge", // namespace for the controller + authncache.New(), + pinnipedAPIClient, + pinnipedInformers.Authentication().V1alpha1().JWTAuthenticators(), + secretInformer, + configMapInformer, + observableInformers.WithInformer, + frozenClock, + logger) + + unrelated := &corev1.ConfigMap{} + filter := observableInformers.GetFilterForInformer(configMapInformer) + require.Equal(t, tt.wantAdd, filter.Add(tt.cm)) + require.Equal(t, tt.wantUpdate, filter.Update(unrelated, tt.cm)) + require.Equal(t, tt.wantUpdate, filter.Update(tt.cm, unrelated)) + require.Equal(t, tt.wantDelete, filter.Delete(tt.cm)) + }) + } +} diff --git a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go index f3615eae3..5601b8b85 100644 --- a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go +++ b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go @@ -73,6 +73,7 @@ func New( webhooks authinformers.WebhookAuthenticatorInformer, secretInformer corev1informers.SecretInformer, configMapInformer corev1informers.ConfigMapInformer, + withInformer pinnipedcontroller.WithInformerOptionFunc, clock clock.Clock, log plog.Logger, ) controllerlib.Controller { @@ -90,12 +91,12 @@ func New( log: log.WithName(controllerName), }, }, - controllerlib.WithInformer( + withInformer( webhooks, pinnipedcontroller.MatchAnythingFilter(nil), // nil parent func is fine because each event is distinct controllerlib.InformerOption{}, ), - controllerlib.WithInformer( + withInformer( secretInformer, pinnipedcontroller.MatchAnySecretOfTypesFilter( []corev1.SecretType{ @@ -106,7 +107,7 @@ func New( ), // nil parent func is fine because each event is distinct controllerlib.InformerOption{}, ), - controllerlib.WithInformer( + withInformer( configMapInformer, pinnipedcontroller.MatchAnythingFilter(pinnipedcontroller.SingletonQueue()), controllerlib.InformerOption{}, diff --git a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go index 49ab602e3..2c1353861 100644 --- a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go +++ b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go @@ -21,10 +21,12 @@ import ( "github.com/stretchr/testify/require" authenticationv1beta1 "k8s.io/api/authentication/v1beta1" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apiserver/pkg/authentication/authenticator" + k8sinformers "k8s.io/client-go/informers" kubeinformers "k8s.io/client-go/informers" kubernetesfake "k8s.io/client-go/kubernetes/fake" coretesting "k8s.io/client-go/testing" @@ -1491,6 +1493,7 @@ func TestController(t *testing.T) { } informers := conciergeinformers.NewSharedInformerFactory(pinnipedAPIClient, 0) kubeInformers := kubeinformers.NewSharedInformerFactory(kubernetesfake.NewSimpleClientset(), 0) + observableInformers := testutil.NewObservableWithInformerOption() cache := authncache.New() var log bytes.Buffer @@ -1507,6 +1510,7 @@ func TestController(t *testing.T) { informers.Authentication().V1alpha1().WebhookAuthenticators(), kubeInformers.Core().V1().Secrets(), kubeInformers.Core().V1().ConfigMaps(), + observableInformers.WithInformer, frozenClock, logger) @@ -1514,6 +1518,7 @@ func TestController(t *testing.T) { defer cancel() informers.Start(ctx.Done()) + kubeInformers.Start(ctx.Done()) controllerlib.TestRunSynchronously(t, controller) syncCtx := controllerlib.Context{Context: ctx, Key: tt.syncKey} @@ -1677,3 +1682,149 @@ func TestNewWebhookAuthenticator(t *testing.T) { }) } } + +func TestControllerFilterSecret(t *testing.T) { + tests := []struct { + name string + secret metav1.Object + wantAdd bool + wantUpdate bool + wantDelete bool + }{ + { + name: "should return true for a secret of the type Opaque", + secret: &corev1.Secret{ + Type: corev1.SecretTypeOpaque, + ObjectMeta: metav1.ObjectMeta{ + Name: "some-name", + }, + }, + wantAdd: true, + wantUpdate: true, + wantDelete: true, + }, + { + name: "should return true for a secret of the type TLS", + secret: &corev1.Secret{ + Type: corev1.SecretTypeTLS, + ObjectMeta: metav1.ObjectMeta{ + Name: "some-name", + }, + }, + wantAdd: true, + wantUpdate: true, + wantDelete: true, + }, + { + name: "should return false for a secret of the wrong type", + secret: &corev1.Secret{ + Type: "other-type", + ObjectMeta: metav1.ObjectMeta{ + Name: "some-name", + }, + }, + }, + { + name: "should return false for a resource of wrong data type", + secret: &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{Name: "some-name", Namespace: "some-namespace"}, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + var log bytes.Buffer + logger := plog.TestLogger(t, &log) + + nowDoesntMatter := time.Date(1122, time.September, 33, 4, 55, 56, 778899, time.Local) + frozenClock := clocktesting.NewFakeClock(nowDoesntMatter) + + kubeInformers := k8sinformers.NewSharedInformerFactoryWithOptions(kubernetesfake.NewSimpleClientset(), 0) + secretInformer := kubeInformers.Core().V1().Secrets() + configMapInformer := kubeInformers.Core().V1().ConfigMaps() + pinnipedAPIClient := conciergefake.NewSimpleClientset() + pinnipedInformers := conciergeinformers.NewSharedInformerFactory(pinnipedAPIClient, 0) + observableInformers := testutil.NewObservableWithInformerOption() + + _ = New( + "concierge", // namespace for controller + authncache.New(), + pinnipedAPIClient, + pinnipedInformers.Authentication().V1alpha1().WebhookAuthenticators(), + secretInformer, + configMapInformer, + observableInformers.WithInformer, + frozenClock, + logger) + + unrelated := &corev1.Secret{} + filter := observableInformers.GetFilterForInformer(secretInformer) + require.Equal(t, tt.wantAdd, filter.Add(tt.secret)) + require.Equal(t, tt.wantUpdate, filter.Update(unrelated, tt.secret)) + require.Equal(t, tt.wantUpdate, filter.Update(tt.secret, unrelated)) + require.Equal(t, tt.wantDelete, filter.Delete(tt.secret)) + }) + } +} + +func TestControllerFilterConfigMap(t *testing.T) { + namespace := "some-namespace" + goodCM := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + }, + } + + tests := []struct { + name string + cm metav1.Object + wantAdd bool + wantUpdate bool + wantDelete bool + }{ + { + name: "a configMap in the right namespace", + cm: goodCM, + wantAdd: true, + wantUpdate: true, + wantDelete: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + var log bytes.Buffer + logger := plog.TestLogger(t, &log) + + nowDoesntMatter := time.Date(1122, time.September, 33, 4, 55, 56, 778899, time.Local) + frozenClock := clocktesting.NewFakeClock(nowDoesntMatter) + + kubeInformers := k8sinformers.NewSharedInformerFactoryWithOptions(kubernetesfake.NewSimpleClientset(), 0) + secretInformer := kubeInformers.Core().V1().Secrets() + configMapInformer := kubeInformers.Core().V1().ConfigMaps() + pinnipedAPIClient := conciergefake.NewSimpleClientset() + pinnipedInformers := conciergeinformers.NewSharedInformerFactory(pinnipedAPIClient, 0) + observableInformers := testutil.NewObservableWithInformerOption() + + _ = New( + "concierge", // namespace for the controller + authncache.New(), + pinnipedAPIClient, + pinnipedInformers.Authentication().V1alpha1().WebhookAuthenticators(), + secretInformer, + configMapInformer, + observableInformers.WithInformer, + frozenClock, + logger) + + unrelated := &corev1.ConfigMap{} + filter := observableInformers.GetFilterForInformer(configMapInformer) + require.Equal(t, tt.wantAdd, filter.Add(tt.cm)) + require.Equal(t, tt.wantUpdate, filter.Update(unrelated, tt.cm)) + require.Equal(t, tt.wantUpdate, filter.Update(tt.cm, unrelated)) + require.Equal(t, tt.wantDelete, filter.Delete(tt.cm)) + }) + } +} diff --git a/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher_test.go b/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher_test.go index 250124a2e..0248fa3b0 100644 --- a/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher_test.go +++ b/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher_test.go @@ -2302,7 +2302,7 @@ func TestGitHubUpstreamWatcherControllerFilterSecret(t *testing.T) { wantDelete: true, }, { - name: "should return false for a secret of the type Opaque", + name: "should return true for a secret of the type Opaque", secret: func() *corev1.Secret { otherSecret := goodSecret.DeepCopy() otherSecret.Type = corev1.SecretTypeOpaque @@ -2313,7 +2313,7 @@ func TestGitHubUpstreamWatcherControllerFilterSecret(t *testing.T) { wantDelete: true, }, { - name: "should return false for a secret of the type TLS", + name: "should return true for a secret of the type TLS", secret: func() *corev1.Secret { otherSecret := goodSecret.DeepCopy() otherSecret.Type = corev1.SecretTypeTLS diff --git a/internal/controllermanager/prepare_controllers.go b/internal/controllermanager/prepare_controllers.go index 7dc14c629..6d5144216 100644 --- a/internal/controllermanager/prepare_controllers.go +++ b/internal/controllermanager/prepare_controllers.go @@ -241,6 +241,7 @@ func PrepareControllers(c *Config) (controllerinit.RunnerBuilder, error) { //nol informers.pinniped.Authentication().V1alpha1().WebhookAuthenticators(), informers.installationNamespaceK8s.Core().V1().Secrets(), informers.installationNamespaceK8s.Core().V1().ConfigMaps(), + controllerlib.WithInformer, clock.RealClock{}, plog.New(), ), @@ -254,6 +255,7 @@ func PrepareControllers(c *Config) (controllerinit.RunnerBuilder, error) { //nol informers.pinniped.Authentication().V1alpha1().JWTAuthenticators(), informers.installationNamespaceK8s.Core().V1().Secrets(), informers.installationNamespaceK8s.Core().V1().ConfigMaps(), + controllerlib.WithInformer, clock.RealClock{}, plog.New(), ), diff --git a/test/integration/supervisor_login_test.go b/test/integration/supervisor_login_test.go index 726d31813..089a3bbe3 100644 --- a/test/integration/supervisor_login_test.go +++ b/test/integration/supervisor_login_test.go @@ -760,7 +760,6 @@ func TestSupervisorLogin_Browser(t *testing.T) { maybeSkip: skipLDAPTests, createIDP: func(t *testing.T) string { idp, _ := createLDAPIdentityProvider(t, func(spec *idpv1alpha1.LDAPIdentityProviderSpec) { - caConfigMap := testlib.CreateTestConfigMap(t, env.SupervisorNamespace, "ca-cert", map[string]string{ "ca.crt": env.SupervisorUpstreamLDAP.CABundle, From 6e9023e090a215f3d296357eaa03101ebb387d55 Mon Sep 17 00:00:00 2001 From: Joshua Casey Date: Thu, 18 Jul 2024 09:32:48 -0500 Subject: [PATCH 18/99] add code review todos and light refactoring Co-authored-by: Ryan Richard --- .../jwtcachefiller/jwtcachefiller.go | 26 +-- .../webhookcachefiller/webhookcachefiller.go | 4 +- .../conditionsutil/conditions_util.go | 1 + .../github_upstream_watcher.go | 3 +- .../github_upstream_watcher_test.go | 15 +- .../oidc_upstream_watcher.go | 8 +- .../upstreamwatchers/upstream_watchers.go | 3 +- .../tlsconfigutil/tls_config_util.go | 32 ++-- .../tlsconfigutil/tls_config_util_test.go | 162 ++++++++++-------- test/integration/concierge_tls_spec_test.go | 2 + test/integration/supervisor_tls_spec_test.go | 2 + 11 files changed, 142 insertions(+), 116 deletions(-) diff --git a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go index f2e1127db..aa3ace88f 100644 --- a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go +++ b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go @@ -48,13 +48,12 @@ import ( const ( controllerName = "jwtcachefiller-controller" - typeReady = "Ready" - typeTLSConfigurationValid = "TLSConfigurationValid" - typeIssuerURLValid = "IssuerURLValid" - typeDiscoveryValid = "DiscoveryURLValid" - typeJWKSURLValid = "JWKSURLValid" - typeJWKSFetchValid = "JWKSFetchValid" - typeAuthenticatorValid = "AuthenticatorValid" + typeReady = "Ready" + typeIssuerURLValid = "IssuerURLValid" + typeDiscoveryValid = "DiscoveryURLValid" + typeJWKSURLValid = "JWKSURLValid" + typeJWKSFetchValid = "JWKSFetchValid" + typeAuthenticatorValid = "AuthenticatorValid" reasonSuccess = "Success" reasonNotReady = "NotReady" @@ -66,7 +65,6 @@ const ( reasonInvalidIssuerURLContainsWellKnownEndpoint = "InvalidIssuerURLContainsWellKnownEndpoint" reasonInvalidProviderJWKSURL = "InvalidProviderJWKSURL" reasonInvalidProviderJWKSURLScheme = "InvalidProviderJWKSURLScheme" - reasonInvalidTLSConfiguration = "InvalidTLSConfiguration" reasonInvalidDiscoveryProbe = "InvalidDiscoveryProbe" reasonInvalidAuthenticator = "InvalidAuthenticator" reasonInvalidCouldNotFetchJWKS = "InvalidCouldNotFetchJWKS" @@ -117,7 +115,8 @@ type tokenAuthenticatorCloser interface { type cachedJWTAuthenticator struct { authenticator.Token - spec *authenticationv1alpha1.JWTAuthenticatorSpec + spec *authenticationv1alpha1.JWTAuthenticatorSpec + // TODO: maybe also keep track of the bytes of the CA bundle itself (or a hash of those bytes) that were used to validate previously?? cancel context.CancelFunc } @@ -220,6 +219,7 @@ func (c *jwtCacheFillerController) Sync(ctx controllerlib.Context) error { var jwtAuthenticatorFromCache *cachedJWTAuthenticator if valueFromCache := c.cache.Get(cacheKey); valueFromCache != nil { jwtAuthenticatorFromCache = c.cacheValueAsJWTAuthenticator(valueFromCache) + // TODO: this is only comparing the specs of the old/new JWTAuthenticator.... BUT the CA bundle from the ConfigMap or Secret could have also changed, so check that somehow if jwtAuthenticatorFromCache != nil && reflect.DeepEqual(jwtAuthenticatorFromCache.spec, &obj.Spec) { c.log.WithValues("jwtAuthenticator", klog.KObj(obj), "issuer", obj.Spec.Issuer). Info("actual jwt authenticator and desired jwt authenticator are the same") @@ -231,7 +231,7 @@ func (c *jwtCacheFillerController) Sync(ctx controllerlib.Context) error { conditions := make([]*metav1.Condition, 0) var errs []error - rootCAs, conditions, tlsOk := c.validateTLSBundle(obj.Spec.TLS, c.namespace, conditions) + rootCAs, conditions, tlsOk := c.validateTLSBundle(obj.Spec.TLS, conditions) _, conditions, issuerOk := c.validateIssuer(obj.Spec.Issuer, conditions) okSoFar := tlsOk && issuerOk @@ -299,11 +299,11 @@ func (c *jwtCacheFillerController) cacheValueAsJWTAuthenticator(value authncache return jwtAuthenticator } -func (c *jwtCacheFillerController) validateTLSBundle(tlsSpec *authenticationv1alpha1.TLSSpec, namespace string, conditions []*metav1.Condition) (*x509.CertPool, []*metav1.Condition, bool) { - condition, _, rootCAs, _ := tlsconfigutil.ValidateTLSConfig( +func (c *jwtCacheFillerController) validateTLSBundle(tlsSpec *authenticationv1alpha1.TLSSpec, conditions []*metav1.Condition) (*x509.CertPool, []*metav1.Condition, bool) { + condition, _, rootCAs := tlsconfigutil.ValidateTLSConfig( tlsconfigutil.TlsSpecForConcierge(tlsSpec), "spec.tls", - namespace, + c.namespace, c.secretInformer, c.configMapInformer) diff --git a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go index 5601b8b85..40d7c66bc 100644 --- a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go +++ b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go @@ -44,7 +44,6 @@ import ( const ( controllerName = "webhookcachefiller-controller" typeReady = "Ready" - typeTLSConfigurationValid = "TLSConfigurationValid" typeWebhookConnectionValid = "WebhookConnectionValid" typeEndpointURLValid = "EndpointURLValid" typeAuthenticatorValid = "AuthenticatorValid" @@ -53,7 +52,6 @@ const ( reasonUnableToValidate = "UnableToValidate" reasonUnableToCreateClient = "UnableToCreateClient" reasonUnableToInstantiateWebhook = "UnableToInstantiateWebhook" - reasonInvalidTLSConfiguration = "InvalidTLSConfiguration" reasonInvalidEndpointURL = "InvalidEndpointURL" reasonInvalidEndpointURLScheme = "InvalidEndpointURLScheme" reasonUnableToDialServer = "UnableToDialServer" @@ -342,7 +340,7 @@ func (c *webhookCacheFillerController) validateConnection(certPool *x509.CertPoo } func (c *webhookCacheFillerController) validateTLSBundle(tlsSpec *authenticationv1alpha1.TLSSpec, conditions []*metav1.Condition) (*x509.CertPool, []byte, []*metav1.Condition, bool) { - condition, pemBytes, rootCAs, _ := tlsconfigutil.ValidateTLSConfig( + condition, pemBytes, rootCAs := tlsconfigutil.ValidateTLSConfig( tlsconfigutil.TlsSpecForConcierge(tlsSpec), "spec.tls", c.namespace, diff --git a/internal/controller/conditionsutil/conditions_util.go b/internal/controller/conditionsutil/conditions_util.go index 383ba20dd..fd667ca10 100644 --- a/internal/controller/conditionsutil/conditions_util.go +++ b/internal/controller/conditionsutil/conditions_util.go @@ -13,6 +13,7 @@ import ( ) const ( + // TODO: why only move one here, why not more? ReasonSuccess = "Success" ) diff --git a/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher.go b/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher.go index 48e812d99..9a58f7e80 100644 --- a/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher.go +++ b/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher.go @@ -282,6 +282,7 @@ func (c *gitHubWatcherController) validateUpstreamAndUpdateConditions(ctx contro tlsConfigCondition, certPool := c.validateTLSConfiguration(upstream.Spec.GitHubAPI.TLS) conditions = append(conditions, tlsConfigCondition) + // TODO: skip this if it is already validated for this same spec and CA bundle (or perhaps hash of CA bundle) githubConnectionCondition, hostURL, httpClient, githubConnectionErr := c.validateGitHubConnection( hostPort, certPool, @@ -373,7 +374,7 @@ func validateHost(gitHubAPIConfig idpv1alpha1.GitHubAPIConfig) (*metav1.Conditio } func (c *gitHubWatcherController) validateTLSConfiguration(tlsSpec *idpv1alpha1.TLSSpec) (*metav1.Condition, *x509.CertPool) { - tlsCondition, _, certPool, _ := tlsconfigutil.ValidateTLSConfig( + tlsCondition, _, certPool := tlsconfigutil.ValidateTLSConfig( tlsconfigutil.TLSSpecForSupervisor(tlsSpec), "spec.githubAPI.tls", c.namespace, diff --git a/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher_test.go b/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher_test.go index 0248fa3b0..d076246d7 100644 --- a/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher_test.go +++ b/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher_test.go @@ -2347,9 +2347,8 @@ func TestGitHubUpstreamWatcherControllerFilterSecret(t *testing.T) { var log bytes.Buffer logger := plog.TestLogger(t, &log) - secretInformer := kubeInformers.Core().V1().Secrets() - configMapInformer := kubeInformers.Core().V1().ConfigMaps() observableInformers := testutil.NewObservableWithInformerOption() + secretInformer := kubeInformers.Core().V1().Secrets() _ = New( "some-namespace", @@ -2357,7 +2356,7 @@ func TestGitHubUpstreamWatcherControllerFilterSecret(t *testing.T) { supervisorfake.NewSimpleClientset(), supervisorinformers.NewSharedInformerFactory(supervisorfake.NewSimpleClientset(), 0).IDP().V1alpha1().GitHubIdentityProviders(), secretInformer, - configMapInformer, + kubeInformers.Core().V1().ConfigMaps(), logger, observableInformers.WithInformer, clock.RealClock{}, @@ -2390,7 +2389,7 @@ func TestGitHubUpstreamWatcherControllerFilterConfigMaps(t *testing.T) { wantDelete bool }{ { - name: "a configMap in the right namespace", + name: "any ConfigMap", cm: goodCM, wantAdd: true, wantUpdate: true, @@ -2404,16 +2403,14 @@ func TestGitHubUpstreamWatcherControllerFilterConfigMaps(t *testing.T) { var log bytes.Buffer logger := plog.TestLogger(t, &log) - gitHubIdentityProviderInformer := supervisorinformers.NewSharedInformerFactory(supervisorfake.NewSimpleClientset(), 0).IDP().V1alpha1().GitHubIdentityProviders() observableInformers := testutil.NewObservableWithInformerOption() - configMapInformer := k8sinformers.NewSharedInformerFactoryWithOptions(kubernetesfake.NewSimpleClientset(), 0).Core().V1().ConfigMaps() _ = New( namespace, dynamicupstreamprovider.NewDynamicUpstreamIDPProvider(), supervisorfake.NewSimpleClientset(), - gitHubIdentityProviderInformer, + supervisorinformers.NewSharedInformerFactory(supervisorfake.NewSimpleClientset(), 0).IDP().V1alpha1().GitHubIdentityProviders(), k8sinformers.NewSharedInformerFactoryWithOptions(kubernetesfake.NewSimpleClientset(), 0).Core().V1().Secrets(), configMapInformer, logger, @@ -2448,7 +2445,7 @@ func TestGitHubUpstreamWatcherControllerFilterGitHubIDP(t *testing.T) { wantDelete bool }{ { - name: "an IDP in the right namespace", + name: "any GitHubIdentityProvider", idp: goodIDP, wantAdd: true, wantUpdate: true, @@ -2462,8 +2459,8 @@ func TestGitHubUpstreamWatcherControllerFilterGitHubIDP(t *testing.T) { var log bytes.Buffer logger := plog.TestLogger(t, &log) - gitHubIdentityProviderInformer := supervisorinformers.NewSharedInformerFactory(supervisorfake.NewSimpleClientset(), 0).IDP().V1alpha1().GitHubIdentityProviders() observableInformers := testutil.NewObservableWithInformerOption() + gitHubIdentityProviderInformer := supervisorinformers.NewSharedInformerFactory(supervisorfake.NewSimpleClientset(), 0).IDP().V1alpha1().GitHubIdentityProviders() _ = New( namespace, diff --git a/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher.go b/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher.go index e49cc04f9..ea7503253 100644 --- a/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher.go +++ b/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher.go @@ -324,15 +324,17 @@ func (c *oidcWatcherController) validateSecret(upstream *idpv1alpha1.OIDCIdentit // validateIssuer validates the .spec.issuer field, performs OIDC discovery, and returns the appropriate OIDCDiscoverySucceeded condition. func (c *oidcWatcherController) validateIssuer(ctx context.Context, upstream *idpv1alpha1.OIDCIdentityProvider, result *upstreamoidc.ProviderConfig) []*metav1.Condition { - // Get the provider and HTTP Client from cache if possible. - discoveredProvider, httpClient := c.validatorCache.getProvider(&upstream.Spec) - tlsCondition, _, certPool, _ := tlsconfigutil.ValidateTLSConfig( + tlsCondition, _, certPool := tlsconfigutil.ValidateTLSConfig( tlsconfigutil.TLSSpecForSupervisor(upstream.Spec.TLS), "spec.tls", upstream.Namespace, c.secretInformer, c.configMapInformer) + // TODO: If either the spec or the CA bundle has changed, then we need to redo the validations below. So maybe the cache key should be the combination of spec and bundle (or hash of bundle)? + // Get the provider and HTTP Client from cache if possible. + discoveredProvider, httpClient := c.validatorCache.getProvider(&upstream.Spec) + // If the provider does not exist in the cache, do a fresh discovery lookup and save to the cache. if discoveredProvider == nil { var err error diff --git a/internal/controller/supervisorconfig/upstreamwatchers/upstream_watchers.go b/internal/controller/supervisorconfig/upstreamwatchers/upstream_watchers.go index 5d949e011..81d791d80 100644 --- a/internal/controller/supervisorconfig/upstreamwatchers/upstream_watchers.go +++ b/internal/controller/supervisorconfig/upstreamwatchers/upstream_watchers.go @@ -254,7 +254,7 @@ func ValidateGenericLDAP( conditions.Append(secretValidCondition, true) tlsSpec := tlsconfigutil.TLSSpecForSupervisor(upstream.Spec().TLSSpec()) - tlsValidCondition, caBundle, _, _ := tlsconfigutil.ValidateTLSConfig(tlsSpec, "spec.tls", upstream.Namespace(), secretInformer, configMapInformer) + tlsValidCondition, caBundle, _ := tlsconfigutil.ValidateTLSConfig(tlsSpec, "spec.tls", upstream.Namespace(), secretInformer, configMapInformer) conditions.Append(tlsValidCondition, true) config.CABundle = caBundle @@ -277,6 +277,7 @@ func validateAndSetLDAPServerConnectivityAndSearchBase( config *upstreamldap.ProviderConfig, currentSecretVersion string, ) (*metav1.Condition, *metav1.Condition) { + // TODO: if the CA bundle has changed, then we should redo the below connection probes. So maybe this cache should also include the CA bundle (or the hash of the bundle) as part of the lookup? validatedSettings, hasPreviousValidatedSettings := validatedSettingsCache.Get(upstream.Name(), currentSecretVersion, upstream.Generation()) var ldapConnectionValidCondition, searchBaseFoundCondition *metav1.Condition diff --git a/internal/controller/tlsconfigutil/tls_config_util.go b/internal/controller/tlsconfigutil/tls_config_util.go index 74278a608..cf7261fac 100644 --- a/internal/controller/tlsconfigutil/tls_config_util.go +++ b/internal/controller/tlsconfigutil/tls_config_util.go @@ -149,33 +149,31 @@ func getCertPool( // ValidateTLSConfig reads ca bundle in the tlsSpec, supplied either inline using the CertificateAuthorityDate // or as a reference to a kubernetes secret or configmap using the CertificateAuthorityDataSource, and returns -// a condition of type TLSConfigurationValid based on the validity of the ca bundle, -// a pem encoded ca bundle -// a X509 cert pool with the ca bundle -// any error encountered. -// TODO: it should suffice that this function returns a TLSConfigurationValid condition, and perhaps we could skip -// returning the error. This can be done once all controllers are able to use this function. +// - a condition of type TLSConfigurationValid based on the validity of the ca bundle, +// - a pem encoded ca bundle +// - a X509 cert pool with the ca bundle +// TODO: should this show the resource version of the Secret/ConfigMap to the user on all conditions? func ValidateTLSConfig( tlsSpec *TLSSpec, conditionPrefix string, namespace string, secretInformer corev1informers.SecretInformer, configMapInformer corev1informers.ConfigMapInformer, -) (*metav1.Condition, []byte, *x509.CertPool, error) { +) (*metav1.Condition, []byte, *x509.CertPool) { // try to build a x509 cert pool using the ca data specified in the tlsSpec. certPool, bundle, err := getCertPool(tlsSpec, conditionPrefix, namespace, secretInformer, configMapInformer) if err != nil { // an error encountered during building a certpool using the ca data from the tlsSpec results in an invalid // TLS condition. - return invalidTLSCondition(err.Error()), nil, nil, err + return invalidTLSCondition(err.Error()), nil, nil } // for us, an empty or nil ca bundle read is results in a valid TLS condition, but we do want to convey that // no ca data was supplied. if bundle == nil { - return validTLSCondition(fmt.Sprintf("%s is valid: %s", conditionPrefix, noTLSConfigurationMessage)), bundle, certPool, err + return validTLSCondition(fmt.Sprintf("%s is valid: %s", conditionPrefix, noTLSConfigurationMessage)), bundle, certPool } - return validTLSCondition(fmt.Sprintf("%s is valid: %s", conditionPrefix, loadedTLSConfigurationMessage)), bundle, certPool, err + return validTLSCondition(fmt.Sprintf("%s is valid: %s", conditionPrefix, loadedTLSConfigurationMessage)), bundle, certPool } func readCABundleFromSource(source *caBundleSource, namespace string, secretInformer corev1informers.SecretInformer, configMapInformer corev1informers.ConfigMapInformer) (string, error) { @@ -190,33 +188,37 @@ func readCABundleFromSource(source *caBundleSource, namespace string, secretInfo } func readCABundleFromK8sSecret(namespace string, name string, key string, secretInformer corev1informers.SecretInformer) (string, error) { + namespacedName := fmt.Sprintf("%s/%s", namespace, name) + s, err := secretInformer.Lister().Secrets(namespace).Get(name) if err != nil { - return "", errors.Wrapf(err, "failed to get secret %s/%s", namespace, name) + return "", errors.Wrapf(err, "failed to get secret %q", namespacedName) } // for kubernetes secrets to be used as a certificate authority data source, the secret should be of type // kubernetes.io/tls or Opaque. It is an error to use a secret that is of any other type. if s.Type != corev1.SecretTypeTLS && s.Type != corev1.SecretTypeOpaque { - return "", fmt.Errorf("secret %s/%s of type %s cannot be used as a certificate authority data source", namespace, name, s.Type) + return "", fmt.Errorf("secret %q of type %q cannot be used as a certificate authority data source", namespacedName, s.Type) } // ca bundle in the secret is expected to exist in a specific key, if that key does not exist, then it is an error if val, exists := s.Data[key]; exists { return string(val), nil } - return "", fmt.Errorf("key %s not found in secret %s/%s", key, namespace, name) + return "", fmt.Errorf("key %q not found in secret %q", key, namespacedName) } func readCABundleFromK8sConfigMap(namespace string, name string, key string, configMapInformer corev1informers.ConfigMapInformer) (string, error) { + namespacedName := fmt.Sprintf("%s/%s", namespace, name) + c, err := configMapInformer.Lister().ConfigMaps(namespace).Get(name) if err != nil { - return "", errors.Wrapf(err, "failed to get configmap %s/%s", namespace, name) + return "", errors.Wrapf(err, "failed to get configmap %q", namespacedName) } // ca bundle in the secret is expected to exist in a specific key, if that key does not exist, then it is an error if val, exists := c.Data[key]; exists { return val, nil } - return "", fmt.Errorf("key %s not found in configmap %s/%s", key, namespace, name) + return "", fmt.Errorf("key %q not found in configmap %q", key, namespacedName) } func validTLSCondition(message string) *metav1.Condition { diff --git a/internal/controller/tlsconfigutil/tls_config_util_test.go b/internal/controller/tlsconfigutil/tls_config_util_test.go index 96e02aa1a..89d7490f7 100644 --- a/internal/controller/tlsconfigutil/tls_config_util_test.go +++ b/internal/controller/tlsconfigutil/tls_config_util_test.go @@ -193,7 +193,7 @@ func TestValidateTLSConfig(t *testing.T) { Type: typeTLSConfigurationValid, Status: metav1.ConditionFalse, Reason: ReasonInvalidTLSConfig, - Message: "tls.certificateAuthorityDataSource is invalid: secret awesome-namespace/awesome-secret-ba of type kubernetes.io/basic-auth cannot be used as a certificate authority data source", + Message: `tls.certificateAuthorityDataSource is invalid: secret "awesome-namespace/awesome-secret-ba" of type "kubernetes.io/basic-auth" cannot be used as a certificate authority data source`, }, }, @@ -250,7 +250,7 @@ func TestValidateTLSConfig(t *testing.T) { Type: typeTLSConfigurationValid, Status: metav1.ConditionFalse, Reason: ReasonInvalidTLSConfig, - Message: "tls.certificateAuthorityDataSource is invalid: failed to get secret awesome-namespace/does-not-exist: secret \"does-not-exist\" not found", + Message: `tls.certificateAuthorityDataSource is invalid: failed to get secret "awesome-namespace/does-not-exist": secret "does-not-exist" not found`, }, }, { @@ -278,7 +278,7 @@ func TestValidateTLSConfig(t *testing.T) { Type: typeTLSConfigurationValid, Status: metav1.ConditionFalse, Reason: ReasonInvalidTLSConfig, - Message: "tls.certificateAuthorityDataSource is invalid: failed to get configmap awesome-namespace/does-not-exist: configmap \"does-not-exist\" not found", + Message: `tls.certificateAuthorityDataSource is invalid: failed to get configmap "awesome-namespace/does-not-exist": configmap "does-not-exist" not found`, }, }, { @@ -333,7 +333,7 @@ func TestValidateTLSConfig(t *testing.T) { sharedInformers.Start(ctx.Done()) sharedInformers.WaitForCacheSync(ctx.Done()) } - actualCondition, _, _, _ := ValidateTLSConfig(tt.tlsSpec, "tls", tt.namespace, secretsInformer, configMapInformer) + actualCondition, _, _ := ValidateTLSConfig(tt.tlsSpec, "tls", tt.namespace, secretsInformer, configMapInformer) require.Equal(t, tt.expectedCondition, actualCondition) }) } @@ -347,46 +347,8 @@ func TestReadCABundleFromK8sSecret(t *testing.T) { secretKey string k8sObjects []runtime.Object expectedData string - expectError bool + expectError string }{ - { - name: "should return error reading a non-existent secret", - secretNamespace: "awesome-namespace", - secretName: "does-not-exist", - secretKey: "does-not-matter", - k8sObjects: []runtime.Object{ - &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "awesome-secret", - Namespace: "awesome-namespace", - }, - Data: map[string][]byte{ - "awesome": []byte("pinniped-is-awesome"), - }, - }, - }, - expectedData: "", - expectError: true, - }, - { - name: "should return error reading a non-existing key in an existing secret", - secretNamespace: "awesome-namespace", - secretName: "awesome-secret", - secretKey: "something-else", - k8sObjects: []runtime.Object{ - &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "awesome-secret", - Namespace: "awesome-namespace", - }, - Data: map[string][]byte{ - "awesome": []byte("pinniped-is-awesome"), - }, - }, - }, - expectedData: "", - expectError: true, - }, { name: "should return data from existing tls secret and existing key", secretNamespace: "awesome-namespace", @@ -405,7 +367,7 @@ func TestReadCABundleFromK8sSecret(t *testing.T) { }, }, expectedData: "pinniped-is-awesome", - expectError: false, + expectError: "", }, { name: "should return data from existing opaque secret and existing key", @@ -425,7 +387,66 @@ func TestReadCABundleFromK8sSecret(t *testing.T) { }, }, expectedData: "pinniped-is-awesome", - expectError: false, + expectError: "", + }, + { + name: "should return error reading a non-existent secret", + secretNamespace: "awesome-namespace", + secretName: "does-not-exist", + secretKey: "does-not-matter", + k8sObjects: []runtime.Object{ + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "awesome-secret", + Namespace: "awesome-namespace", + }, + Data: map[string][]byte{ + "awesome": []byte("pinniped-is-awesome"), + }, + }, + }, + expectedData: "", + expectError: `failed to get secret "awesome-namespace/does-not-exist": secret "does-not-exist" not found`, + }, + { + name: "should return error when secret has wrong type", + secretNamespace: "awesome-namespace", + secretName: "awesome-secret", + secretKey: "awesome", + k8sObjects: []runtime.Object{ + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "awesome-secret", + Namespace: "awesome-namespace", + }, + Type: "other-type", + Data: map[string][]byte{ + "awesome": []byte("pinniped-is-awesome"), + }, + }, + }, + + expectError: `secret "awesome-namespace/awesome-secret" of type "other-type" cannot be used as a certificate authority data source`, + }, + { + name: "should return error reading a non-existing key in an existing secret", + secretNamespace: "awesome-namespace", + secretName: "awesome-secret", + secretKey: "something-else", + k8sObjects: []runtime.Object{ + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "awesome-secret", + Namespace: "awesome-namespace", + }, + Type: corev1.SecretTypeOpaque, + Data: map[string][]byte{ + "awesome": []byte("pinniped-is-awesome"), + }, + }, + }, + expectedData: "", + expectError: `key "something-else" not found in secret "awesome-namespace/awesome-secret"`, }, } @@ -446,8 +467,8 @@ func TestReadCABundleFromK8sSecret(t *testing.T) { sharedInformers.WaitForCacheSync(ctx.Done()) // now the objects from kubernetes should be sync'd into the informer cache. actualData, actualError := readCABundleFromK8sSecret(tt.secretNamespace, tt.secretName, tt.secretKey, secretsInformer) - if tt.expectError { - require.Error(t, actualError) + if tt.expectError != "" { + require.ErrorContains(t, actualError, tt.expectError) } else { require.NoError(t, actualError) } @@ -464,8 +485,26 @@ func TestReadCABundleFromK8sConfigMap(t *testing.T) { configMapKey string k8sObjects []runtime.Object expectedData string - expectError bool + expectError string }{ + { + name: "should return expected data from an existing key in an existing configMap", + configMapNamespace: "awesome-namespace", + configMapName: "awesome-configmap", + configMapKey: "awesome", + k8sObjects: []runtime.Object{ + &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "awesome-configmap", + Namespace: "awesome-namespace", + }, + Data: map[string]string{ + "awesome": "pinniped-is-awesome", + }, + }, + }, + expectedData: "pinniped-is-awesome", + }, { name: "should return error reading a non-existent configMap", configMapNamespace: "awesome-namespace", @@ -483,7 +522,7 @@ func TestReadCABundleFromK8sConfigMap(t *testing.T) { }, }, expectedData: "", - expectError: true, + expectError: `failed to get configmap "awesome-namespace/does-not-exist": configmap "does-not-exist" not found`, }, { name: "should return error reading a non-existing key in an existing configMap", @@ -502,26 +541,7 @@ func TestReadCABundleFromK8sConfigMap(t *testing.T) { }, }, expectedData: "", - expectError: true, - }, - { - name: "should return expected data from an existing key in an existing configMap", - configMapNamespace: "awesome-namespace", - configMapName: "awesome-configmap", - configMapKey: "awesome", - k8sObjects: []runtime.Object{ - &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: "awesome-configmap", - Namespace: "awesome-namespace", - }, - Data: map[string]string{ - "awesome": "pinniped-is-awesome", - }, - }, - }, - expectedData: "pinniped-is-awesome", - expectError: false, + expectError: `key "does-not-exist" not found in configmap "awesome-namespace/awesome-configmap"`, }, } @@ -542,8 +562,8 @@ func TestReadCABundleFromK8sConfigMap(t *testing.T) { sharedInformers.Start(ctx.Done()) sharedInformers.WaitForCacheSync(ctx.Done()) actualData, actualError := readCABundleFromK8sConfigMap(tt.configMapNamespace, tt.configMapName, tt.configMapKey, configMapInformer) - if tt.expectError { - require.Error(t, actualError) + if tt.expectError != "" { + require.ErrorContains(t, actualError, tt.expectError) } else { require.NoError(t, actualError) } diff --git a/test/integration/concierge_tls_spec_test.go b/test/integration/concierge_tls_spec_test.go index 845f77a1f..5cdfcb4af 100644 --- a/test/integration/concierge_tls_spec_test.go +++ b/test/integration/concierge_tls_spec_test.go @@ -28,6 +28,8 @@ func TestTLSSpecKubeBuilderValidationConcierge_Parallel(t *testing.T) { customResourceName string expectedError string }{ + // TODO: these "spec.endpoint" could use the real URL of the local-user-authenticator + // TODO: should we repeat these tests using the JWTAuthenticator too? { name: "should disallow certificate authority data source with missing name", customResourceYaml: here.Doc(` diff --git a/test/integration/supervisor_tls_spec_test.go b/test/integration/supervisor_tls_spec_test.go index b7d80b608..d3724fbdd 100644 --- a/test/integration/supervisor_tls_spec_test.go +++ b/test/integration/supervisor_tls_spec_test.go @@ -28,6 +28,8 @@ func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { customResourceName string expectedError string }{ + // TODO: use the OIDC provider from env instead of bar.com + // TODO: make ths a loop to also run the same tests on LDAP, AD, GitHub?? { name: "should disallow certificate authority data source with missing name", customResourceYaml: here.Doc(` From bf1c02d3281b73342dede142440ec2e4e4f2815b Mon Sep 17 00:00:00 2001 From: Joshua Casey Date: Thu, 18 Jul 2024 11:19:32 -0500 Subject: [PATCH 19/99] jwtauthenticator controller redoes validations when external CA bundle changes Co-authored-by: Ryan Richard --- .../jwtcachefiller/jwtcachefiller.go | 43 +- .../jwtcachefiller/jwtcachefiller_test.go | 373 +++++++++++++++--- .../webhookcachefiller_test.go | 16 +- .../tlsconfigutil/tls_config_util.go | 11 +- .../tlsconfigutil/tls_config_util_test.go | 3 +- .../testutil/conciergetestutil/tlstestutil.go | 28 -- ...cierge_webhookauthenticator_status_test.go | 2 +- 7 files changed, 361 insertions(+), 115 deletions(-) delete mode 100644 internal/testutil/conciergetestutil/tlstestutil.go diff --git a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go index aa3ace88f..906e6a894 100644 --- a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go +++ b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go @@ -7,6 +7,7 @@ package jwtcachefiller import ( "context" + "crypto/sha256" "crypto/x509" "errors" "fmt" @@ -115,9 +116,9 @@ type tokenAuthenticatorCloser interface { type cachedJWTAuthenticator struct { authenticator.Token - spec *authenticationv1alpha1.JWTAuthenticatorSpec - // TODO: maybe also keep track of the bytes of the CA bundle itself (or a hash of those bytes) that were used to validate previously?? - cancel context.CancelFunc + spec *authenticationv1alpha1.JWTAuthenticatorSpec + caBundlePEMSHA256 [32]byte + cancel context.CancelFunc } func (c *cachedJWTAuthenticator) Close() { @@ -208,6 +209,10 @@ func (c *jwtCacheFillerController) Sync(ctx controllerlib.Context) error { Name: ctx.Key.Name, } + conditions := make([]*metav1.Condition, 0) + rootCAs, conditions, caBundlePEM, tlsOk := c.validateTLSBundle(obj.Spec.TLS, conditions) + caBundlePEMSHA256 := sha256.Sum256(caBundlePEM) // note that this will always return the same hash for nil input + // Only revalidate and update the cache if the cached authenticator is different from the desired authenticator. // There is no need to repeat validations for a spec that was already successfully validated. We are making a // design decision to avoid repeating the validation which dials the server, even though the server's TLS @@ -219,8 +224,10 @@ func (c *jwtCacheFillerController) Sync(ctx controllerlib.Context) error { var jwtAuthenticatorFromCache *cachedJWTAuthenticator if valueFromCache := c.cache.Get(cacheKey); valueFromCache != nil { jwtAuthenticatorFromCache = c.cacheValueAsJWTAuthenticator(valueFromCache) - // TODO: this is only comparing the specs of the old/new JWTAuthenticator.... BUT the CA bundle from the ConfigMap or Secret could have also changed, so check that somehow - if jwtAuthenticatorFromCache != nil && reflect.DeepEqual(jwtAuthenticatorFromCache.spec, &obj.Spec) { + if jwtAuthenticatorFromCache != nil && + reflect.DeepEqual(jwtAuthenticatorFromCache.spec, &obj.Spec) && + tlsOk && // if there was any error while validating the CA bundle, then run remaining validations and update status + jwtAuthenticatorFromCache.caBundlePEMSHA256 == caBundlePEMSHA256 { c.log.WithValues("jwtAuthenticator", klog.KObj(obj), "issuer", obj.Spec.Issuer). Info("actual jwt authenticator and desired jwt authenticator are the same") // Stop, no more work to be done. This authenticator is already validated and cached. @@ -228,10 +235,7 @@ func (c *jwtCacheFillerController) Sync(ctx controllerlib.Context) error { } } - conditions := make([]*metav1.Condition, 0) var errs []error - - rootCAs, conditions, tlsOk := c.validateTLSBundle(obj.Spec.TLS, conditions) _, conditions, issuerOk := c.validateIssuer(obj.Spec.Issuer, conditions) okSoFar := tlsOk && issuerOk @@ -255,6 +259,7 @@ func (c *jwtCacheFillerController) Sync(ctx controllerlib.Context) error { client, obj.Spec.DeepCopy(), // deep copy to avoid caching original object keySet, + caBundlePEMSHA256, conditions, okSoFar) errs = append(errs, err) @@ -299,8 +304,8 @@ func (c *jwtCacheFillerController) cacheValueAsJWTAuthenticator(value authncache return jwtAuthenticator } -func (c *jwtCacheFillerController) validateTLSBundle(tlsSpec *authenticationv1alpha1.TLSSpec, conditions []*metav1.Condition) (*x509.CertPool, []*metav1.Condition, bool) { - condition, _, rootCAs := tlsconfigutil.ValidateTLSConfig( +func (c *jwtCacheFillerController) validateTLSBundle(tlsSpec *authenticationv1alpha1.TLSSpec, conditions []*metav1.Condition) (*x509.CertPool, []*metav1.Condition, []byte, bool) { + condition, pemBundle, rootCAs := tlsconfigutil.ValidateTLSConfig( tlsconfigutil.TlsSpecForConcierge(tlsSpec), "spec.tls", c.namespace, @@ -308,7 +313,7 @@ func (c *jwtCacheFillerController) validateTLSBundle(tlsSpec *authenticationv1al c.configMapInformer) conditions = append(conditions, condition) - return rootCAs, conditions, condition.Status == metav1.ConditionTrue + return rootCAs, conditions, pemBundle, condition.Status == metav1.ConditionTrue } func (c *jwtCacheFillerController) validateIssuer(issuer string, conditions []*metav1.Condition) (*url.URL, []*metav1.Condition, bool) { @@ -543,7 +548,14 @@ func (c *jwtCacheFillerController) validateJWKSFetch(ctx context.Context, jwksUR } // newCachedJWTAuthenticator creates a jwt authenticator from the provided spec. -func (c *jwtCacheFillerController) newCachedJWTAuthenticator(client *http.Client, spec *authenticationv1alpha1.JWTAuthenticatorSpec, keySet *coreosoidc.RemoteKeySet, conditions []*metav1.Condition, prereqOk bool) (*cachedJWTAuthenticator, []*metav1.Condition, error) { +func (c *jwtCacheFillerController) newCachedJWTAuthenticator( + client *http.Client, + spec *authenticationv1alpha1.JWTAuthenticatorSpec, + keySet *coreosoidc.RemoteKeySet, + caBundlePEMSHA256 [32]byte, + conditions []*metav1.Condition, + prereqOk bool, +) (*cachedJWTAuthenticator, []*metav1.Condition, error) { if !prereqOk { conditions = append(conditions, &metav1.Condition{ Type: typeAuthenticatorValid, @@ -611,9 +623,10 @@ func (c *jwtCacheFillerController) newCachedJWTAuthenticator(client *http.Client Message: msg, }) return &cachedJWTAuthenticator{ - Token: oidcAuthenticator, - spec: spec, - cancel: cancel, + Token: oidcAuthenticator, + spec: spec, + caBundlePEMSHA256: caBundlePEMSHA256, + cancel: cancel, }, conditions, nil } diff --git a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller_test.go b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller_test.go index 048270c46..5e2cbb1c2 100644 --- a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller_test.go +++ b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller_test.go @@ -10,8 +10,10 @@ import ( "crypto/elliptic" "crypto/rand" "crypto/rsa" + "crypto/sha256" "crypto/x509" _ "embed" + "encoding/base64" "encoding/json" "encoding/pem" "errors" @@ -47,7 +49,6 @@ import ( "go.pinniped.dev/internal/crypto/ptls" "go.pinniped.dev/internal/plog" "go.pinniped.dev/internal/testutil" - "go.pinniped.dev/internal/testutil/conciergetestutil" "go.pinniped.dev/internal/testutil/conditionstestutil" "go.pinniped.dev/internal/testutil/tlsserver" ) @@ -116,10 +117,13 @@ func TestController(t *testing.T) { distributedGroups := []string{"some-distributed-group-1", "some-distributed-group-2"} goodMux := http.NewServeMux() - goodOIDCIssuerServer, _ := tlsserver.TestServerIPv4(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + goodOIDCIssuerServer, goodOIDCIssuerServerCAPEM := tlsserver.TestServerIPv4(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { tlsserver.AssertTLS(t, r, ptls.Default) goodMux.ServeHTTP(w, r) }), tlsserver.RecordTLSHello) + goodOIDCIssuerServerTLSSpec := &authenticationv1alpha1.TLSSpec{ + CertificateAuthorityData: base64.StdEncoding.EncodeToString(goodOIDCIssuerServerCAPEM), + } goodMux.Handle("/.well-known/openid-configuration", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") @@ -214,10 +218,13 @@ func TestController(t *testing.T) { })) badMuxInvalidJWKSURI := http.NewServeMux() - badOIDCIssuerServerInvalidJWKSURIServer, _ := tlsserver.TestServerIPv4(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + badOIDCIssuerServerInvalidJWKSURIServer, badOIDCIssuerServerInvalidJWKSURIServerCAPEM := tlsserver.TestServerIPv4(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { tlsserver.AssertTLS(t, r, ptls.Default) badMuxInvalidJWKSURI.ServeHTTP(w, r) }), tlsserver.RecordTLSHello) + badOIDCIssuerServerInvalidJWKSURIServerTLSSpec := &authenticationv1alpha1.TLSSpec{ + CertificateAuthorityData: base64.StdEncoding.EncodeToString(badOIDCIssuerServerInvalidJWKSURIServerCAPEM), + } badMuxInvalidJWKSURI.Handle("/.well-known/openid-configuration", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") _, err := fmt.Fprintf(w, `{"issuer": "%s", "jwks_uri": "%s"}`, badOIDCIssuerServerInvalidJWKSURIServer.URL, "https://.café .com/café/café/café/coffee/jwks.json") @@ -225,10 +232,13 @@ func TestController(t *testing.T) { })) badMuxInvalidJWKSURIScheme := http.NewServeMux() - badOIDCIssuerServerInvalidJWKSURISchemeServer, _ := tlsserver.TestServerIPv4(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + badOIDCIssuerServerInvalidJWKSURISchemeServer, badOIDCIssuerServerInvalidJWKSURISchemeServerCAPEM := tlsserver.TestServerIPv4(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { tlsserver.AssertTLS(t, r, ptls.Default) badMuxInvalidJWKSURIScheme.ServeHTTP(w, r) }), tlsserver.RecordTLSHello) + badOIDCIssuerServerInvalidJWKSURISchemeServerTLSSpec := &authenticationv1alpha1.TLSSpec{ + CertificateAuthorityData: base64.StdEncoding.EncodeToString(badOIDCIssuerServerInvalidJWKSURISchemeServerCAPEM), + } badMuxInvalidJWKSURIScheme.Handle("/.well-known/openid-configuration", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") _, err := fmt.Fprintf(w, `{"issuer": "%s", "jwks_uri": "%s"}`, badOIDCIssuerServerInvalidJWKSURISchemeServer.URL, "http://.café.com/café/café/café/coffee/jwks.json") @@ -236,10 +246,13 @@ func TestController(t *testing.T) { })) jwksFetchShouldFailMux := http.NewServeMux() - jwksFetchShouldFailServer, _ := tlsserver.TestServerIPv4(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + jwksFetchShouldFailServer, jwksFetchShouldFailServerCAPEM := tlsserver.TestServerIPv4(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { tlsserver.AssertTLS(t, r, ptls.Default) jwksFetchShouldFailMux.ServeHTTP(w, r) }), tlsserver.RecordTLSHello) + jwksFetchShouldFailServerTLSSpec := &authenticationv1alpha1.TLSSpec{ + CertificateAuthorityData: base64.StdEncoding.EncodeToString(jwksFetchShouldFailServerCAPEM), + } jwksFetchShouldFailMux.Handle("/.well-known/openid-configuration", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") _, err := fmt.Fprintf(w, `{"issuer": "%s", "jwks_uri": "%s"}`, jwksFetchShouldFailServer.URL, jwksFetchShouldFailServer.URL+"/fetch/will/fail/jwks.json") @@ -250,10 +263,13 @@ func TestController(t *testing.T) { // in real life. Simulating this here just so we can have test coverage for the expected error that the production // code should return in this case. badMuxUsesOurHardcodedPrivateKey := http.NewServeMux() - badOIDCIssuerUsesOurHardcodedPrivateKeyServer, _ := tlsserver.TestServerIPv4(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + badOIDCIssuerUsesOurHardcodedPrivateKeyServer, badOIDCIssuerUsesOurHardcodedPrivateKeyServerCAPEM := tlsserver.TestServerIPv4(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { tlsserver.AssertTLS(t, r, ptls.Default) badMuxUsesOurHardcodedPrivateKey.ServeHTTP(w, r) }), tlsserver.RecordTLSHello) + badOIDCIssuerUsesOurHardcodedPrivateKeyServerTLSSpec := &authenticationv1alpha1.TLSSpec{ + CertificateAuthorityData: base64.StdEncoding.EncodeToString(badOIDCIssuerUsesOurHardcodedPrivateKeyServerCAPEM), + } badMuxUsesOurHardcodedPrivateKey.Handle("/.well-known/openid-configuration", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") _, err := fmt.Fprintf(w, `{"issuer": "%s", "jwks_uri": "%s"}`, badOIDCIssuerUsesOurHardcodedPrivateKeyServer.URL, badOIDCIssuerUsesOurHardcodedPrivateKeyServer.URL+"/jwks.json") @@ -285,12 +301,53 @@ func TestController(t *testing.T) { someJWTAuthenticatorSpec := &authenticationv1alpha1.JWTAuthenticatorSpec{ Issuer: goodIssuer, Audience: goodAudience, - TLS: conciergetestutil.TLSSpecFromTLSConfig(goodOIDCIssuerServer.TLS), + TLS: goodOIDCIssuerServerTLSSpec, + } + someJWTAuthenticatorSpecWithCAInSecret := &authenticationv1alpha1.JWTAuthenticatorSpec{ + Issuer: goodIssuer, + Audience: goodAudience, + TLS: &authenticationv1alpha1.TLSSpec{ + CertificateAuthorityDataSource: &authenticationv1alpha1.CABundleSource{ + Kind: "Secret", + Name: "secret-with-ca", + Key: "ca.crt", + }, + }, + } + someSecretWithCA := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "secret-with-ca", + Namespace: "concierge", + }, + Type: corev1.SecretTypeOpaque, + Data: map[string][]byte{ + "ca.crt": goodOIDCIssuerServerCAPEM, + }, + } + someJWTAuthenticatorSpecWithCAInConfigMap := &authenticationv1alpha1.JWTAuthenticatorSpec{ + Issuer: goodIssuer, + Audience: goodAudience, + TLS: &authenticationv1alpha1.TLSSpec{ + CertificateAuthorityDataSource: &authenticationv1alpha1.CABundleSource{ + Kind: "ConfigMap", + Name: "configmap-with-ca", + Key: "ca.crt", + }, + }, + } + someConfigMapWithCA := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "configmap-with-ca", + Namespace: "concierge", + }, + Data: map[string]string{ + "ca.crt": string(goodOIDCIssuerServerCAPEM), + }, } someJWTAuthenticatorSpecWithUsernameClaim := &authenticationv1alpha1.JWTAuthenticatorSpec{ Issuer: goodIssuer, Audience: goodAudience, - TLS: conciergetestutil.TLSSpecFromTLSConfig(goodOIDCIssuerServer.TLS), + TLS: goodOIDCIssuerServerTLSSpec, Claims: authenticationv1alpha1.JWTTokenClaims{ Username: "my-custom-username-claim", }, @@ -298,7 +355,7 @@ func TestController(t *testing.T) { someJWTAuthenticatorSpecWithGroupsClaim := &authenticationv1alpha1.JWTAuthenticatorSpec{ Issuer: goodIssuer, Audience: goodAudience, - TLS: conciergetestutil.TLSSpecFromTLSConfig(goodOIDCIssuerServer.TLS), + TLS: goodOIDCIssuerServerTLSSpec, Claims: authenticationv1alpha1.JWTTokenClaims{ Groups: customGroupsClaim, }, @@ -323,12 +380,12 @@ func TestController(t *testing.T) { invalidIssuerJWTAuthenticatorSpec := &authenticationv1alpha1.JWTAuthenticatorSpec{ Issuer: "https://.café .com/café/café/café/coffee", Audience: goodAudience, - TLS: conciergetestutil.TLSSpecFromTLSConfig(goodOIDCIssuerServer.TLS), + TLS: goodOIDCIssuerServerTLSSpec, } invalidIssuerSchemeJWTAuthenticatorSpec := &authenticationv1alpha1.JWTAuthenticatorSpec{ Issuer: "http://.café.com/café/café/café/coffee", Audience: goodAudience, - TLS: conciergetestutil.TLSSpecFromTLSConfig(goodOIDCIssuerServer.TLS), + TLS: goodOIDCIssuerServerTLSSpec, } validIssuerURLButDoesNotExistJWTAuthenticatorSpec := &authenticationv1alpha1.JWTAuthenticatorSpec{ Issuer: goodIssuer + "/foo/bar/baz/shizzle", @@ -337,22 +394,22 @@ func TestController(t *testing.T) { badIssuerJWKSURIJWTAuthenticatorSpec := &authenticationv1alpha1.JWTAuthenticatorSpec{ Issuer: badOIDCIssuerServerInvalidJWKSURIServer.URL, Audience: goodAudience, - TLS: conciergetestutil.TLSSpecFromTLSConfig(badOIDCIssuerServerInvalidJWKSURIServer.TLS), + TLS: badOIDCIssuerServerInvalidJWKSURIServerTLSSpec, } badIssuerJWKSURISchemeJWTAuthenticatorSpec := &authenticationv1alpha1.JWTAuthenticatorSpec{ Issuer: badOIDCIssuerServerInvalidJWKSURISchemeServer.URL, Audience: goodAudience, - TLS: conciergetestutil.TLSSpecFromTLSConfig(badOIDCIssuerServerInvalidJWKSURISchemeServer.TLS), + TLS: badOIDCIssuerServerInvalidJWKSURISchemeServerTLSSpec, } badIssuerUsesOurHardcodedPrivateKeyJWTAuthenticatorSpec := &authenticationv1alpha1.JWTAuthenticatorSpec{ Issuer: badOIDCIssuerUsesOurHardcodedPrivateKeyServer.URL, Audience: goodAudience, - TLS: conciergetestutil.TLSSpecFromTLSConfig(badOIDCIssuerUsesOurHardcodedPrivateKeyServer.TLS), + TLS: badOIDCIssuerUsesOurHardcodedPrivateKeyServerTLSSpec, } jwksFetchShouldFailJWTAuthenticatorSpec := &authenticationv1alpha1.JWTAuthenticatorSpec{ Issuer: jwksFetchShouldFailServer.URL, Audience: goodAudience, - TLS: conciergetestutil.TLSSpecFromTLSConfig(jwksFetchShouldFailServer.TLS), + TLS: jwksFetchShouldFailServerTLSSpec, } happyReadyCondition := func(time metav1.Time, observedGeneration int64) metav1.Condition { @@ -644,10 +701,11 @@ func TestController(t *testing.T) { Kind: "JWTAuthenticator", } tests := []struct { - name string - cache func(*testing.T, *authncache.Cache, bool) - syncKey controllerlib.Key - jwtAuthenticators []runtime.Object + name string + cache func(*testing.T, *authncache.Cache, bool) + syncKey controllerlib.Key + jwtAuthenticators []runtime.Object + secretsAndConfigMaps []runtime.Object // for modifying the clients to hack in arbitrary api responses configClient func(*conciergefake.Clientset) wantClose bool @@ -664,7 +722,7 @@ func TestController(t *testing.T) { runTestsOnResultingAuthenticator bool }{ { - name: "404: JWTAuthenticator not found will abort sync loop, no status conditions.", + name: "Sync: JWTAuthenticator not found will abort sync loop, no status conditions", syncKey: controllerlib.Key{Name: "test-name"}, wantLogs: []map[string]any{ { @@ -728,7 +786,7 @@ func TestController(t *testing.T) { Conditions: conditionstestutil.Replace( allHappyConditionsSuccess(goodIssuer, frozenMetav1Now, 1233), []metav1.Condition{ - // sad and unknwn will update with new statuses and timestamps + // sad and unknown will update with new statuses and timestamps sadReadyCondition(frozenTimeInThePast, 1232), sadDiscoveryURLValidx509(goodIssuer, frozenTimeInThePast, 1231), unknownAuthenticatorValid(frozenTimeInThePast, 1232), @@ -780,7 +838,7 @@ func TestController(t *testing.T) { wantCacheEntries: 1, }, { - name: "Sync: valid JWTAuthenticator with CA: loop will complete successfully and update status conditions.", + name: "Sync: valid JWTAuthenticator with CA: loop will complete successfully and update status conditions", syncKey: controllerlib.Key{Name: "test-name"}, jwtAuthenticators: []runtime.Object{ &authenticationv1alpha1.JWTAuthenticator{ @@ -822,7 +880,97 @@ func TestController(t *testing.T) { runTestsOnResultingAuthenticator: true, }, { - name: "Sync: JWTAuthenticator with custom username claim: loop will complete successfully and update status conditions.", + name: "Sync: valid JWTAuthenticator with CA from Secret: loop will complete successfully and update status conditions", + syncKey: controllerlib.Key{Name: "test-name"}, + jwtAuthenticators: []runtime.Object{ + &authenticationv1alpha1.JWTAuthenticator{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-name", + }, + Spec: *someJWTAuthenticatorSpecWithCAInSecret, + }, + }, + secretsAndConfigMaps: []runtime.Object{ + someSecretWithCA, + }, + wantLogs: []map[string]any{{ + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "jwtcachefiller-controller", + "message": "added new jwt authenticator", + "issuer": goodIssuer, + "jwtAuthenticator": map[string]any{ + "name": "test-name", + }, + }}, + wantActions: func() []coretesting.Action { + updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-name", + }, + Spec: *someJWTAuthenticatorSpecWithCAInSecret, + Status: authenticationv1alpha1.JWTAuthenticatorStatus{ + Conditions: allHappyConditionsSuccess(goodIssuer, frozenMetav1Now, 0), + Phase: "Ready", + }, + }) + updateStatusAction.Subresource = "status" + return []coretesting.Action{ + coretesting.NewListAction(jwtAuthenticatorsGVR, jwtAUthenticatorGVK, "", metav1.ListOptions{}), + coretesting.NewWatchAction(jwtAuthenticatorsGVR, "", metav1.ListOptions{}), + updateStatusAction, + } + }, + wantCacheEntries: 1, + runTestsOnResultingAuthenticator: true, + }, + { + name: "Sync: valid JWTAuthenticator with CA from ConfigMap: loop will complete successfully and update status conditions", + syncKey: controllerlib.Key{Name: "test-name"}, + jwtAuthenticators: []runtime.Object{ + &authenticationv1alpha1.JWTAuthenticator{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-name", + }, + Spec: *someJWTAuthenticatorSpecWithCAInConfigMap, + }, + }, + secretsAndConfigMaps: []runtime.Object{ + someConfigMapWithCA, + }, + wantLogs: []map[string]any{{ + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "jwtcachefiller-controller", + "message": "added new jwt authenticator", + "issuer": goodIssuer, + "jwtAuthenticator": map[string]any{ + "name": "test-name", + }, + }}, + wantActions: func() []coretesting.Action { + updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-name", + }, + Spec: *someJWTAuthenticatorSpecWithCAInConfigMap, + Status: authenticationv1alpha1.JWTAuthenticatorStatus{ + Conditions: allHappyConditionsSuccess(goodIssuer, frozenMetav1Now, 0), + Phase: "Ready", + }, + }) + updateStatusAction.Subresource = "status" + return []coretesting.Action{ + coretesting.NewListAction(jwtAuthenticatorsGVR, jwtAUthenticatorGVK, "", metav1.ListOptions{}), + coretesting.NewWatchAction(jwtAuthenticatorsGVR, "", metav1.ListOptions{}), + updateStatusAction, + } + }, + wantCacheEntries: 1, + runTestsOnResultingAuthenticator: true, + }, + { + name: "Sync: JWTAuthenticator with custom username claim: loop will complete successfully and update status conditions", syncKey: controllerlib.Key{Name: "test-name"}, jwtAuthenticators: []runtime.Object{ &authenticationv1alpha1.JWTAuthenticator{ @@ -865,7 +1013,7 @@ func TestController(t *testing.T) { runTestsOnResultingAuthenticator: true, }, { - name: "Sync: JWTAuthenticator with custom groups claim: loop will complete successfully and update status conditions.", + name: "Sync: JWTAuthenticator with custom groups claim: loop will complete successfully and update status conditions", syncKey: controllerlib.Key{Name: "test-name"}, jwtAuthenticators: []runtime.Object{ &authenticationv1alpha1.JWTAuthenticator{ @@ -908,15 +1056,17 @@ func TestController(t *testing.T) { runTestsOnResultingAuthenticator: true, }, { - name: "Sync: JWTAuthenticator with new fields: loop will close previous instance of JWTAuthenticator and complete successfully and update status conditions.", + name: "Sync: JWTAuthenticator with new spec fields: loop will close previous instance of JWTAuthenticator and complete successfully and update status conditions", cache: func(t *testing.T, cache *authncache.Cache, wantClose bool) { + oldCA, err := base64.StdEncoding.DecodeString(otherJWTAuthenticatorSpec.TLS.CertificateAuthorityData) + require.NoError(t, err) cache.Store( authncache.Key{ Name: "test-name", Kind: "JWTAuthenticator", APIGroup: authenticationv1alpha1.SchemeGroupVersion.Group, }, - newCacheValue(t, *otherJWTAuthenticatorSpec, wantClose), + newCacheValue(t, *otherJWTAuthenticatorSpec, string(oldCA), wantClose), ) }, wantClose: true, @@ -961,7 +1111,7 @@ func TestController(t *testing.T) { runTestsOnResultingAuthenticator: true, }, { - name: "Sync: JWTAuthenticator with no change: loop will abort early and not update status conditions.", + name: "Sync: JWTAuthenticator with external and changed CA bundle: loop will close previous instance of JWTAuthenticator and complete successfully and update status conditions", cache: func(t *testing.T, cache *authncache.Cache, wantClose bool) { cache.Store( authncache.Key{ @@ -969,7 +1119,65 @@ func TestController(t *testing.T) { Kind: "JWTAuthenticator", APIGroup: authenticationv1alpha1.SchemeGroupVersion.Group, }, - newCacheValue(t, *someJWTAuthenticatorSpec, wantClose), + newCacheValue(t, *someJWTAuthenticatorSpecWithCAInSecret, "some-stale-ca-bundle-pem-content-from-secret", wantClose), + ) + }, + wantClose: true, + syncKey: controllerlib.Key{Name: "test-name"}, + jwtAuthenticators: []runtime.Object{ + &authenticationv1alpha1.JWTAuthenticator{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-name", + }, + Spec: *someJWTAuthenticatorSpecWithCAInSecret, + }, + }, + secretsAndConfigMaps: []runtime.Object{ + someSecretWithCA, + }, + wantLogs: []map[string]any{{ + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "jwtcachefiller-controller", + "message": "added new jwt authenticator", + "issuer": goodIssuer, + "jwtAuthenticator": map[string]any{ + "name": "test-name", + }, + }}, + wantActions: func() []coretesting.Action { + updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-name", + }, + Spec: *someJWTAuthenticatorSpecWithCAInSecret, + Status: authenticationv1alpha1.JWTAuthenticatorStatus{ + Conditions: allHappyConditionsSuccess(goodIssuer, frozenMetav1Now, 0), + Phase: "Ready", + }, + }) + updateStatusAction.Subresource = "status" + return []coretesting.Action{ + coretesting.NewListAction(jwtAuthenticatorsGVR, jwtAUthenticatorGVK, "", metav1.ListOptions{}), + coretesting.NewWatchAction(jwtAuthenticatorsGVR, "", metav1.ListOptions{}), + updateStatusAction, + } + }, + wantCacheEntries: 1, + runTestsOnResultingAuthenticator: true, + }, + { + name: "Sync: JWTAuthenticator with no change: loop will abort early and not update status conditions", + cache: func(t *testing.T, cache *authncache.Cache, wantClose bool) { + oldCA, err := base64.StdEncoding.DecodeString(someJWTAuthenticatorSpec.TLS.CertificateAuthorityData) + require.NoError(t, err) + cache.Store( + authncache.Key{ + Name: "test-name", + Kind: "JWTAuthenticator", + APIGroup: authenticationv1alpha1.SchemeGroupVersion.Group, + }, + newCacheValue(t, *someJWTAuthenticatorSpec, string(oldCA), wantClose), ) }, wantClose: false, @@ -1002,7 +1210,7 @@ func TestController(t *testing.T) { runTestsOnResultingAuthenticator: false, // skip the tests because the authenticator left in the cache is the mock version that was added above }, { - name: "Sync: authenticator update when cached authenticator is the wrong data type, which should never really happen: loop will complete successfully and update status conditions.", + name: "Sync: authenticator update when cached authenticator is the wrong data type, which should never really happen: loop will complete successfully and update status conditions", cache: func(t *testing.T, cache *authncache.Cache, wantClose bool) { cache.Store( authncache.Key{ @@ -1151,7 +1359,8 @@ func TestController(t *testing.T) { wantCacheEntries: 0, }, { - name: "previously valid cached authenticator's spec changes and becomes invalid (e.g. spec.issuer URL is invalid): loop will fail sync, will write failed and unknown status conditions, and will remove authenticator from cache", + name: "previously valid cached authenticator (which did not specify a CA bundle) changes and becomes invalid due to any problem with the CA bundle: loop will fail sync, will write failed and unknown status conditions, and will remove authenticator from cache", + syncKey: controllerlib.Key{Name: "test-name"}, cache: func(t *testing.T, cache *authncache.Cache, wantClose bool) { cache.Store( authncache.Key{ @@ -1159,7 +1368,64 @@ func TestController(t *testing.T) { Kind: "JWTAuthenticator", APIGroup: authenticationv1alpha1.SchemeGroupVersion.Group, }, - newCacheValue(t, *someJWTAuthenticatorSpec, wantClose), + // Force an invalid spec into the cache, which is not very realistic, but it simulates a case + // where the CA bundle goes from being cached as empty to being an error during validation, + // without causing any changes in the spec. This test wants to prove that the rest of the + // validations get run and the resource is update, just in case that can happen somehow. + newCacheValue(t, *invalidTLSJWTAuthenticatorSpec, "", wantClose), + ) + }, + jwtAuthenticators: []runtime.Object{ + &authenticationv1alpha1.JWTAuthenticator{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-name", + }, + Spec: *invalidTLSJWTAuthenticatorSpec, + }, + }, + wantActions: func() []coretesting.Action { + updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-name", + }, + Spec: *invalidTLSJWTAuthenticatorSpec, + Status: authenticationv1alpha1.JWTAuthenticatorStatus{ + Conditions: conditionstestutil.Replace( + allHappyConditionsSuccess(someOtherIssuer, frozenMetav1Now, 0), + []metav1.Condition{ + sadReadyCondition(frozenMetav1Now, 0), + sadTLSConfigurationValid(frozenMetav1Now, 0), + unknownDiscoveryURLValid(frozenMetav1Now, 0), + unknownAuthenticatorValid(frozenMetav1Now, 0), + unknownJWKSURLValid(frozenMetav1Now, 0), + unknownJWKSFetch(frozenMetav1Now, 0), + }, + ), + Phase: "Error", + }, + }) + updateStatusAction.Subresource = "status" + return []coretesting.Action{ + coretesting.NewListAction(jwtAuthenticatorsGVR, jwtAUthenticatorGVK, "", metav1.ListOptions{}), + coretesting.NewWatchAction(jwtAuthenticatorsGVR, "", metav1.ListOptions{}), + updateStatusAction, + } + }, + wantClose: true, + wantCacheEntries: 0, + }, + { + name: "previously valid cached authenticator's spec changes and becomes invalid for any other reason (this test uses an invalid spec.issuer URL): loop will fail sync, will write failed and unknown status conditions, and will remove authenticator from cache", + cache: func(t *testing.T, cache *authncache.Cache, wantClose bool) { + oldCA, err := base64.StdEncoding.DecodeString(someJWTAuthenticatorSpec.TLS.CertificateAuthorityData) + require.NoError(t, err) + cache.Store( + authncache.Key{ + Name: "test-name", + Kind: "JWTAuthenticator", + APIGroup: authenticationv1alpha1.SchemeGroupVersion.Group, + }, + newCacheValue(t, *someJWTAuthenticatorSpec, string(oldCA), wantClose), ) }, jwtAuthenticators: []runtime.Object{ @@ -1292,7 +1558,7 @@ func TestController(t *testing.T) { Spec: authenticationv1alpha1.JWTAuthenticatorSpec{ Issuer: "https://www.example.com/foo/bar/#do-not-include-fragment", Audience: goodAudience, - TLS: conciergetestutil.TLSSpecFromTLSConfig(goodOIDCIssuerServer.TLS), + TLS: goodOIDCIssuerServerTLSSpec, }, }, }, @@ -1305,7 +1571,7 @@ func TestController(t *testing.T) { Spec: authenticationv1alpha1.JWTAuthenticatorSpec{ Issuer: "https://www.example.com/foo/bar/#do-not-include-fragment", Audience: goodAudience, - TLS: conciergetestutil.TLSSpecFromTLSConfig(goodOIDCIssuerServer.TLS), + TLS: goodOIDCIssuerServerTLSSpec, }, Status: authenticationv1alpha1.JWTAuthenticatorStatus{ Conditions: conditionstestutil.Replace( @@ -1340,7 +1606,7 @@ func TestController(t *testing.T) { Spec: authenticationv1alpha1.JWTAuthenticatorSpec{ Issuer: "https://www.example.com/foo/bar/?query-params=not-allowed", Audience: goodAudience, - TLS: conciergetestutil.TLSSpecFromTLSConfig(goodOIDCIssuerServer.TLS), + TLS: goodOIDCIssuerServerTLSSpec, }, }, }, @@ -1353,7 +1619,7 @@ func TestController(t *testing.T) { Spec: authenticationv1alpha1.JWTAuthenticatorSpec{ Issuer: "https://www.example.com/foo/bar/?query-params=not-allowed", Audience: goodAudience, - TLS: conciergetestutil.TLSSpecFromTLSConfig(goodOIDCIssuerServer.TLS), + TLS: goodOIDCIssuerServerTLSSpec, }, Status: authenticationv1alpha1.JWTAuthenticatorStatus{ Conditions: conditionstestutil.Replace( @@ -1388,7 +1654,7 @@ func TestController(t *testing.T) { Spec: authenticationv1alpha1.JWTAuthenticatorSpec{ Issuer: "https://www.example.com/foo/bar/.well-known/openid-configuration", Audience: goodAudience, - TLS: conciergetestutil.TLSSpecFromTLSConfig(goodOIDCIssuerServer.TLS), + TLS: goodOIDCIssuerServerTLSSpec, }, }, }, @@ -1401,7 +1667,7 @@ func TestController(t *testing.T) { Spec: authenticationv1alpha1.JWTAuthenticatorSpec{ Issuer: "https://www.example.com/foo/bar/.well-known/openid-configuration", Audience: goodAudience, - TLS: conciergetestutil.TLSSpecFromTLSConfig(goodOIDCIssuerServer.TLS), + TLS: goodOIDCIssuerServerTLSSpec, }, Status: authenticationv1alpha1.JWTAuthenticatorStatus{ Conditions: conditionstestutil.Replace( @@ -1478,7 +1744,7 @@ func TestController(t *testing.T) { Spec: authenticationv1alpha1.JWTAuthenticatorSpec{ Issuer: goodIssuer + "/path/to/not/found", Audience: goodAudience, - TLS: conciergetestutil.TLSSpecFromTLSConfig(goodOIDCIssuerServer.TLS), + TLS: goodOIDCIssuerServerTLSSpec, }, }, }, @@ -1491,7 +1757,7 @@ func TestController(t *testing.T) { Spec: authenticationv1alpha1.JWTAuthenticatorSpec{ Issuer: goodIssuer + "/path/to/not/found", Audience: goodAudience, - TLS: conciergetestutil.TLSSpecFromTLSConfig(goodOIDCIssuerServer.TLS), + TLS: goodOIDCIssuerServerTLSSpec, }, Status: authenticationv1alpha1.JWTAuthenticatorStatus{ Conditions: conditionstestutil.Replace( @@ -1846,8 +2112,7 @@ func TestController(t *testing.T) { tt.configClient(pinnipedAPIClient) } pinnipedInformers := conciergeinformers.NewSharedInformerFactory(pinnipedAPIClient, 0) - kubeInformers := kubeinformers.NewSharedInformerFactory(kubernetesfake.NewSimpleClientset(), 0) - observableInformers := testutil.NewObservableWithInformerOption() + kubeInformers := kubeinformers.NewSharedInformerFactory(kubernetesfake.NewSimpleClientset(tt.secretsAndConfigMaps...), 0) cache := authncache.New() var log bytes.Buffer @@ -1864,7 +2129,7 @@ func TestController(t *testing.T) { pinnipedInformers.Authentication().V1alpha1().JWTAuthenticators(), kubeInformers.Core().V1().Secrets(), kubeInformers.Core().V1().ConfigMaps(), - observableInformers.WithInformer, + controllerlib.WithInformer, frozenClock, logger) @@ -1873,7 +2138,6 @@ func TestController(t *testing.T) { pinnipedInformers.Start(ctx.Done()) kubeInformers.Start(ctx.Done()) - controllerlib.TestRunSynchronously(t, controller) syncCtx := controllerlib.Context{Context: ctx, Key: tt.syncKey} @@ -1884,6 +2148,15 @@ func TestController(t *testing.T) { require.NoError(t, err) } + if !assert.ElementsMatch(t, tt.wantActions(), pinnipedAPIClient.Actions()) { + // cmp.Diff is superior to require.ElementsMatch in terms of readability here. + // require.ElementsMatch will handle pointers better than require.Equal, but + // the timestamps are still incredibly verbose. + require.Fail(t, cmp.Diff(tt.wantActions(), pinnipedAPIClient.Actions()), "actions should be exactly the expected number of actions and also contain the correct resources") + } + + require.Equal(t, tt.wantCacheEntries, len(cache.Keys()), fmt.Sprintf("expected cache entries is incorrect. wanted:%d, got: %d, keys: %v", tt.wantCacheEntries, len(cache.Keys()), cache.Keys())) + actualLogLines := testutil.SplitByNewline(log.String()) require.Equal(t, len(tt.wantLogs), len(actualLogLines), "log line count should be correct") @@ -1910,15 +2183,6 @@ func TestController(t *testing.T) { } } - if !assert.ElementsMatch(t, tt.wantActions(), pinnipedAPIClient.Actions()) { - // cmp.Diff is superior to require.ElementsMatch in terms of readability here. - // require.ElementsMatch will handle pointers better than require.Equal, but - // the timestamps are still incredibly verbose. - require.Fail(t, cmp.Diff(tt.wantActions(), pinnipedAPIClient.Actions()), "actions should be exactly the expected number of actions and also contain the correct resources") - } - - require.Equal(t, tt.wantCacheEntries, len(cache.Keys()), fmt.Sprintf("expected cache entries is incorrect. wanted:%d, got: %d, keys: %v", tt.wantCacheEntries, len(cache.Keys()), cache.Keys())) - if !tt.runTestsOnResultingAuthenticator { return // end of test unless we wanted to run tests on the resulting authenticator from the cache } @@ -2266,7 +2530,7 @@ func createJWT( return jwt } -func newCacheValue(t *testing.T, spec authenticationv1alpha1.JWTAuthenticatorSpec, wantClose bool) authncache.Value { +func newCacheValue(t *testing.T, spec authenticationv1alpha1.JWTAuthenticatorSpec, caBundle string, wantClose bool) authncache.Value { t.Helper() wasClosed := false @@ -2275,7 +2539,8 @@ func newCacheValue(t *testing.T, spec authenticationv1alpha1.JWTAuthenticatorSpe }) return &cachedJWTAuthenticator{ - spec: &spec, + spec: &spec, + caBundlePEMSHA256: sha256.Sum256([]byte(caBundle)), cancel: func() { wasClosed = true }, diff --git a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go index 2c1353861..b2aa7f482 100644 --- a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go +++ b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go @@ -42,7 +42,6 @@ import ( "go.pinniped.dev/internal/crypto/ptls" "go.pinniped.dev/internal/plog" "go.pinniped.dev/internal/testutil" - "go.pinniped.dev/internal/testutil/conciergetestutil" "go.pinniped.dev/internal/testutil/conditionstestutil" "go.pinniped.dev/internal/testutil/tlsserver" ) @@ -122,12 +121,14 @@ func TestController(t *testing.T) { w.WriteHeader(http.StatusNotFound) _, _ = fmt.Fprint(w, "404 nothing here") })) - hostGoodDefaultServingCertServer, _ := tlsserver.TestServerIPv4(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + hostGoodDefaultServingCertServer, hostGoodDefaultServingCertServerCAPEM := tlsserver.TestServerIPv4(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { mux.ServeHTTP(w, r) }), func(s *httptest.Server) { tlsserver.AssertEveryTLSHello(t, s, ptls.Default) // assert on every hello because we are only expecting dials }) - + hostGoodDefaultServingCertServerTLSSpec := &authenticationv1alpha1.TLSSpec{ + CertificateAuthorityData: base64.StdEncoding.EncodeToString(hostGoodDefaultServingCertServerCAPEM), + } goodWebhookDefaultServingCertEndpoint := hostGoodDefaultServingCertServer.URL goodWebhookDefaultServingCertEndpointBut404 := goodWebhookDefaultServingCertEndpoint + "/nothing/here" @@ -146,7 +147,7 @@ func TestController(t *testing.T) { goodWebhookAuthenticatorSpecWithCA := authenticationv1alpha1.WebhookAuthenticatorSpec{ Endpoint: goodWebhookDefaultServingCertEndpoint, - TLS: conciergetestutil.TLSSpecFromTLSConfig(hostGoodDefaultServingCertServer.TLS), + TLS: hostGoodDefaultServingCertServerTLSSpec, } localWithExampleDotComWeebhookAuthenticatorSpec := authenticationv1alpha1.WebhookAuthenticatorSpec{ // CA for example.com, TLS serving cert for example.com, but endpoint is still localhost @@ -162,7 +163,7 @@ func TestController(t *testing.T) { } goodWebhookAuthenticatorSpecWith404Endpoint := authenticationv1alpha1.WebhookAuthenticatorSpec{ Endpoint: goodWebhookDefaultServingCertEndpointBut404, - TLS: conciergetestutil.TLSSpecFromTLSConfig(hostGoodDefaultServingCertServer.TLS), + TLS: hostGoodDefaultServingCertServerTLSSpec, } badWebhookAuthenticatorSpecInvalidTLS := authenticationv1alpha1.WebhookAuthenticatorSpec{ Endpoint: goodWebhookDefaultServingCertEndpoint, @@ -475,7 +476,7 @@ func TestController(t *testing.T) { wantCacheEntries: 1, }, { - name: "Sync: authenticator update when cached authenticator is the wrong data type, which should never really happen: loop will complete successfully and update status conditions.", + name: "Sync: authenticator update when cached authenticator is the wrong data type, which should never really happen: loop will complete successfully and update status conditions", cache: func(t *testing.T, cache *authncache.Cache) { cache.Store( authncache.Key{ @@ -1493,7 +1494,6 @@ func TestController(t *testing.T) { } informers := conciergeinformers.NewSharedInformerFactory(pinnipedAPIClient, 0) kubeInformers := kubeinformers.NewSharedInformerFactory(kubernetesfake.NewSimpleClientset(), 0) - observableInformers := testutil.NewObservableWithInformerOption() cache := authncache.New() var log bytes.Buffer @@ -1510,7 +1510,7 @@ func TestController(t *testing.T) { informers.Authentication().V1alpha1().WebhookAuthenticators(), kubeInformers.Core().V1().Secrets(), kubeInformers.Core().V1().ConfigMaps(), - observableInformers.WithInformer, + controllerlib.WithInformer, frozenClock, logger) diff --git a/internal/controller/tlsconfigutil/tls_config_util.go b/internal/controller/tlsconfigutil/tls_config_util.go index cf7261fac..cb996db96 100644 --- a/internal/controller/tlsconfigutil/tls_config_util.go +++ b/internal/controller/tlsconfigutil/tls_config_util.go @@ -160,19 +160,14 @@ func ValidateTLSConfig( secretInformer corev1informers.SecretInformer, configMapInformer corev1informers.ConfigMapInformer, ) (*metav1.Condition, []byte, *x509.CertPool) { - // try to build a x509 cert pool using the ca data specified in the tlsSpec. certPool, bundle, err := getCertPool(tlsSpec, conditionPrefix, namespace, secretInformer, configMapInformer) if err != nil { - // an error encountered during building a certpool using the ca data from the tlsSpec results in an invalid - // TLS condition. return invalidTLSCondition(err.Error()), nil, nil } - // for us, an empty or nil ca bundle read is results in a valid TLS condition, but we do want to convey that - // no ca data was supplied. if bundle == nil { - return validTLSCondition(fmt.Sprintf("%s is valid: %s", conditionPrefix, noTLSConfigurationMessage)), bundle, certPool + // An empty or nil CA bundle results in a valid TLS condition which indicates that no CA data was supplied. + return validTLSCondition(fmt.Sprintf("%s is valid: %s", conditionPrefix, noTLSConfigurationMessage)), nil, nil } - return validTLSCondition(fmt.Sprintf("%s is valid: %s", conditionPrefix, loadedTLSConfigurationMessage)), bundle, certPool } @@ -201,6 +196,7 @@ func readCABundleFromK8sSecret(namespace string, name string, key string, secret } // ca bundle in the secret is expected to exist in a specific key, if that key does not exist, then it is an error if val, exists := s.Data[key]; exists { + // TODO: if val is an empty string, it should be an error return string(val), nil } return "", fmt.Errorf("key %q not found in secret %q", key, namespacedName) @@ -216,6 +212,7 @@ func readCABundleFromK8sConfigMap(namespace string, name string, key string, con // ca bundle in the secret is expected to exist in a specific key, if that key does not exist, then it is an error if val, exists := c.Data[key]; exists { + // TODO: if val is an empty string, it should be an error return val, nil } return "", fmt.Errorf("key %q not found in configmap %q", key, namespacedName) diff --git a/internal/controller/tlsconfigutil/tls_config_util_test.go b/internal/controller/tlsconfigutil/tls_config_util_test.go index 89d7490f7..8d94a9bc4 100644 --- a/internal/controller/tlsconfigutil/tls_config_util_test.go +++ b/internal/controller/tlsconfigutil/tls_config_util_test.go @@ -453,8 +453,7 @@ func TestReadCABundleFromK8sSecret(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { t.Parallel() - fakeClient := fake.NewSimpleClientset(tt.k8sObjects...) - sharedInformers := informers.NewSharedInformerFactory(fakeClient, 0) + sharedInformers := informers.NewSharedInformerFactory(fake.NewSimpleClientset(tt.k8sObjects...), 0) secretsInformer := sharedInformers.Core().V1().Secrets() // calling the .Informer function registers this informer in the sharedinformer. diff --git a/internal/testutil/conciergetestutil/tlstestutil.go b/internal/testutil/conciergetestutil/tlstestutil.go deleted file mode 100644 index 1f275c89f..000000000 --- a/internal/testutil/conciergetestutil/tlstestutil.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2024 the Pinniped contributors. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -package conciergetestutil - -import ( - "crypto/tls" - "encoding/base64" - "encoding/pem" - - authenticationv1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/authentication/v1alpha1" -) - -func TLSSpecFromTLSConfig(tls *tls.Config) *authenticationv1alpha1.TLSSpec { - pemData := make([]byte, 0) - for _, certificate := range tls.Certificates { - // this is the public part of the certificate, the private is the certificate.PrivateKey - for _, reallyCertificate := range certificate.Certificate { - pemData = append(pemData, pem.EncodeToMemory(&pem.Block{ - Type: "CERTIFICATE", - Bytes: reallyCertificate, - })...) - } - } - return &authenticationv1alpha1.TLSSpec{ - CertificateAuthorityData: base64.StdEncoding.EncodeToString(pemData), - } -} diff --git a/test/integration/concierge_webhookauthenticator_status_test.go b/test/integration/concierge_webhookauthenticator_status_test.go index 88106e984..2841f93b8 100644 --- a/test/integration/concierge_webhookauthenticator_status_test.go +++ b/test/integration/concierge_webhookauthenticator_status_test.go @@ -31,7 +31,7 @@ func TestConciergeWebhookAuthenticatorStatus_Parallel(t *testing.T) { run func(t *testing.T) }{ { - name: "Basic test to see if the WebhookAuthenticator wakes up or not.", + name: "basic test to see if the WebhookAuthenticator wakes up or not", spec: func() *authenticationv1alpha1.WebhookAuthenticatorSpec { return &testlib.IntegrationEnv(t).TestWebhook }, From 920b519ebf7fe0e79c374e9f39ded350067834a7 Mon Sep 17 00:00:00 2001 From: Ryan Richard Date: Fri, 19 Jul 2024 16:18:52 -0700 Subject: [PATCH 20/99] error when CA bundle from Secret or ConfigMap is empty Co-authored-by: Joshua Casey --- .../cachecleaner/cachecleaner_test.go | 1 - .../jwtcachefiller/jwtcachefiller.go | 2 +- .../webhookcachefiller/webhookcachefiller.go | 2 +- .../tlsconfigutil/tls_config_util.go | 80 +-- .../tlsconfigutil/tls_config_util_test.go | 466 ++++++++---------- 5 files changed, 238 insertions(+), 313 deletions(-) diff --git a/internal/controller/authenticator/cachecleaner/cachecleaner_test.go b/internal/controller/authenticator/cachecleaner/cachecleaner_test.go index 3602c0b21..214e65f30 100644 --- a/internal/controller/authenticator/cachecleaner/cachecleaner_test.go +++ b/internal/controller/authenticator/cachecleaner/cachecleaner_test.go @@ -155,7 +155,6 @@ func TestController(t *testing.T) { defer cancel() informers.Start(ctx.Done()) - informers.WaitForCacheSync(ctx.Done()) controllerlib.TestRunSynchronously(t, controller) syncCtx := controllerlib.Context{ diff --git a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go index 906e6a894..7e1d6cc48 100644 --- a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go +++ b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go @@ -306,7 +306,7 @@ func (c *jwtCacheFillerController) cacheValueAsJWTAuthenticator(value authncache func (c *jwtCacheFillerController) validateTLSBundle(tlsSpec *authenticationv1alpha1.TLSSpec, conditions []*metav1.Condition) (*x509.CertPool, []*metav1.Condition, []byte, bool) { condition, pemBundle, rootCAs := tlsconfigutil.ValidateTLSConfig( - tlsconfigutil.TlsSpecForConcierge(tlsSpec), + tlsconfigutil.TLSSpecForConcierge(tlsSpec), "spec.tls", c.namespace, c.secretInformer, diff --git a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go index 40d7c66bc..2074795ff 100644 --- a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go +++ b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go @@ -341,7 +341,7 @@ func (c *webhookCacheFillerController) validateConnection(certPool *x509.CertPoo func (c *webhookCacheFillerController) validateTLSBundle(tlsSpec *authenticationv1alpha1.TLSSpec, conditions []*metav1.Condition) (*x509.CertPool, []byte, []*metav1.Condition, bool) { condition, pemBytes, rootCAs := tlsconfigutil.ValidateTLSConfig( - tlsconfigutil.TlsSpecForConcierge(tlsSpec), + tlsconfigutil.TLSSpecForConcierge(tlsSpec), "spec.tls", c.namespace, c.secretInformer, diff --git a/internal/controller/tlsconfigutil/tls_config_util.go b/internal/controller/tlsconfigutil/tls_config_util.go index cb996db96..121609517 100644 --- a/internal/controller/tlsconfigutil/tls_config_util.go +++ b/internal/controller/tlsconfigutil/tls_config_util.go @@ -64,8 +64,8 @@ func TLSSpecForSupervisor(source *idpv1alpha1.TLSSpec) *TLSSpec { return dest } -// TlsSpecForConcierge is a helper function to convert the Concierge's TLSSpec to the unified TLSSpec. -func TlsSpecForConcierge(source *authenticationv1alpha1.TLSSpec) *TLSSpec { +// TLSSpecForConcierge is a helper function to convert the Concierge's TLSSpec to the unified TLSSpec. +func TLSSpecForConcierge(source *authenticationv1alpha1.TLSSpec) *TLSSpec { if source == nil { return nil } @@ -82,6 +82,30 @@ func TlsSpecForConcierge(source *authenticationv1alpha1.TLSSpec) *TLSSpec { return dest } +// ValidateTLSConfig reads ca bundle in the tlsSpec, supplied either inline using the CertificateAuthorityDate +// or as a reference to a kubernetes secret or configmap using the CertificateAuthorityDataSource, and returns +// - a condition of type TLSConfigurationValid based on the validity of the ca bundle, +// - a pem encoded ca bundle +// - a X509 cert pool with the ca bundle +// TODO: should this show the resource version of the Secret/ConfigMap to the user on all conditions? +func ValidateTLSConfig( + tlsSpec *TLSSpec, + conditionPrefix string, + namespace string, + secretInformer corev1informers.SecretInformer, + configMapInformer corev1informers.ConfigMapInformer, +) (*metav1.Condition, []byte, *x509.CertPool) { + certPool, bundle, err := getCertPool(tlsSpec, conditionPrefix, namespace, secretInformer, configMapInformer) + if err != nil { + return invalidTLSCondition(err.Error()), nil, nil + } + if bundle == nil { + // An empty or nil CA bundle results in a valid TLS condition which indicates that no CA data was supplied. + return validTLSCondition(fmt.Sprintf("%s is valid: %s", conditionPrefix, noTLSConfigurationMessage)), nil, nil + } + return validTLSCondition(fmt.Sprintf("%s is valid: %s", conditionPrefix, loadedTLSConfigurationMessage)), bundle, certPool +} + // getCertPool reads the unified tlsSpec and returns an X509 cert pool with the CA data that is read either from // the inline tls.certificateAuthorityData or from a kubernetes secret or a config map as specified in the // tls.certificateAuthorityDataSource. @@ -147,30 +171,6 @@ func getCertPool( return ca, bundleBytes, nil } -// ValidateTLSConfig reads ca bundle in the tlsSpec, supplied either inline using the CertificateAuthorityDate -// or as a reference to a kubernetes secret or configmap using the CertificateAuthorityDataSource, and returns -// - a condition of type TLSConfigurationValid based on the validity of the ca bundle, -// - a pem encoded ca bundle -// - a X509 cert pool with the ca bundle -// TODO: should this show the resource version of the Secret/ConfigMap to the user on all conditions? -func ValidateTLSConfig( - tlsSpec *TLSSpec, - conditionPrefix string, - namespace string, - secretInformer corev1informers.SecretInformer, - configMapInformer corev1informers.ConfigMapInformer, -) (*metav1.Condition, []byte, *x509.CertPool) { - certPool, bundle, err := getCertPool(tlsSpec, conditionPrefix, namespace, secretInformer, configMapInformer) - if err != nil { - return invalidTLSCondition(err.Error()), nil, nil - } - if bundle == nil { - // An empty or nil CA bundle results in a valid TLS condition which indicates that no CA data was supplied. - return validTLSCondition(fmt.Sprintf("%s is valid: %s", conditionPrefix, noTLSConfigurationMessage)), nil, nil - } - return validTLSCondition(fmt.Sprintf("%s is valid: %s", conditionPrefix, loadedTLSConfigurationMessage)), bundle, certPool -} - func readCABundleFromSource(source *caBundleSource, namespace string, secretInformer corev1informers.SecretInformer, configMapInformer corev1informers.ConfigMapInformer) (string, error) { switch source.Kind { case "Secret": @@ -189,17 +189,21 @@ func readCABundleFromK8sSecret(namespace string, name string, key string, secret if err != nil { return "", errors.Wrapf(err, "failed to get secret %q", namespacedName) } - // for kubernetes secrets to be used as a certificate authority data source, the secret should be of type + + // For Secrets to be used as a certificate authority data source, the secret should be of type // kubernetes.io/tls or Opaque. It is an error to use a secret that is of any other type. if s.Type != corev1.SecretTypeTLS && s.Type != corev1.SecretTypeOpaque { return "", fmt.Errorf("secret %q of type %q cannot be used as a certificate authority data source", namespacedName, s.Type) } - // ca bundle in the secret is expected to exist in a specific key, if that key does not exist, then it is an error - if val, exists := s.Data[key]; exists { - // TODO: if val is an empty string, it should be an error - return string(val), nil + + val, exists := s.Data[key] + if !exists { + return "", fmt.Errorf("key %q not found in secret %q", key, namespacedName) } - return "", fmt.Errorf("key %q not found in secret %q", key, namespacedName) + if len(val) == 0 { + return "", fmt.Errorf("key %q has empty value in secret %q", key, namespacedName) + } + return string(val), nil } func readCABundleFromK8sConfigMap(namespace string, name string, key string, configMapInformer corev1informers.ConfigMapInformer) (string, error) { @@ -210,12 +214,14 @@ func readCABundleFromK8sConfigMap(namespace string, name string, key string, con return "", errors.Wrapf(err, "failed to get configmap %q", namespacedName) } - // ca bundle in the secret is expected to exist in a specific key, if that key does not exist, then it is an error - if val, exists := c.Data[key]; exists { - // TODO: if val is an empty string, it should be an error - return val, nil + val, exists := c.Data[key] + if !exists { + return "", fmt.Errorf("key %q not found in configmap %q", key, namespacedName) } - return "", fmt.Errorf("key %q not found in configmap %q", key, namespacedName) + if len(val) == 0 { + return "", fmt.Errorf("key %q has empty value in configmap %q", key, namespacedName) + } + return val, nil } func validTLSCondition(message string) *metav1.Condition { diff --git a/internal/controller/tlsconfigutil/tls_config_util_test.go b/internal/controller/tlsconfigutil/tls_config_util_test.go index 8d94a9bc4..25b11f1e8 100644 --- a/internal/controller/tlsconfigutil/tls_config_util_test.go +++ b/internal/controller/tlsconfigutil/tls_config_util_test.go @@ -5,6 +5,7 @@ package tlsconfigutil import ( "context" + "crypto/x509" "encoding/base64" "testing" "time" @@ -27,12 +28,17 @@ func TestValidateTLSConfig(t *testing.T) { testCA, err := certauthority.New("Test CA", 1*time.Hour) require.NoError(t, err) bundle := testCA.Bundle() + certPool := x509.NewCertPool() + require.True(t, certPool.AppendCertsFromPEM(bundle)) base64EncodedBundle := base64.StdEncoding.EncodeToString(bundle) + tests := []struct { name string tlsSpec *TLSSpec namespace string k8sObjects []runtime.Object + expectedBundle []byte + expectedCertPool *x509.CertPool expectedCondition *metav1.Condition }{ { @@ -60,6 +66,8 @@ func TestValidateTLSConfig(t *testing.T) { tlsSpec: &TLSSpec{ CertificateAuthorityData: base64EncodedBundle, }, + expectedBundle: bundle, + expectedCertPool: certPool, expectedCondition: &metav1.Condition{ Type: typeTLSConfigurationValid, Status: metav1.ConditionTrue, @@ -130,6 +138,8 @@ func TestValidateTLSConfig(t *testing.T) { }, }, }, + expectedBundle: bundle, + expectedCertPool: certPool, expectedCondition: &metav1.Condition{ Type: typeTLSConfigurationValid, Status: metav1.ConditionTrue, @@ -159,6 +169,8 @@ func TestValidateTLSConfig(t *testing.T) { }, }, }, + expectedBundle: bundle, + expectedCertPool: certPool, expectedCondition: &metav1.Condition{ Type: typeTLSConfigurationValid, Status: metav1.ConditionTrue, @@ -166,7 +178,6 @@ func TestValidateTLSConfig(t *testing.T) { Message: "tls is valid: loaded TLS configuration", }, }, - { name: "should return invalid condition when a secrets not of type tls or opaque are used as ca data source", tlsSpec: &TLSSpec{ @@ -196,7 +207,120 @@ func TestValidateTLSConfig(t *testing.T) { Message: `tls.certificateAuthorityDataSource is invalid: secret "awesome-namespace/awesome-secret-ba" of type "kubernetes.io/basic-auth" cannot be used as a certificate authority data source`, }, }, - + { + name: "should return invalid condition when a secret does not have the configured key", + tlsSpec: &TLSSpec{ + CertificateAuthorityDataSource: &caBundleSource{ + Kind: "Secret", + Name: "awesome-secret", + Key: "ca-bundle", + }, + }, + namespace: "awesome-namespace", + k8sObjects: []runtime.Object{ + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "awesome-secret", + Namespace: "awesome-namespace", + }, + Type: corev1.SecretTypeOpaque, + Data: map[string][]byte{ + "wrong-key": bundle, + }, + }, + }, + expectedCondition: &metav1.Condition{ + Type: typeTLSConfigurationValid, + Status: metav1.ConditionFalse, + Reason: ReasonInvalidTLSConfig, + Message: `tls.certificateAuthorityDataSource is invalid: key "ca-bundle" not found in secret "awesome-namespace/awesome-secret"`, + }, + }, + { + name: "should return invalid condition when a secret has the configured key but its value is empty", + tlsSpec: &TLSSpec{ + CertificateAuthorityDataSource: &caBundleSource{ + Kind: "Secret", + Name: "awesome-secret", + Key: "ca-bundle", + }, + }, + namespace: "awesome-namespace", + k8sObjects: []runtime.Object{ + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "awesome-secret", + Namespace: "awesome-namespace", + }, + Type: corev1.SecretTypeOpaque, + Data: map[string][]byte{ + "ca-bundle": []byte(""), + }, + }, + }, + expectedCondition: &metav1.Condition{ + Type: typeTLSConfigurationValid, + Status: metav1.ConditionFalse, + Reason: ReasonInvalidTLSConfig, + Message: `tls.certificateAuthorityDataSource is invalid: key "ca-bundle" has empty value in secret "awesome-namespace/awesome-secret"`, + }, + }, + { + name: "should return invalid condition when a configmap does not have the configured key", + tlsSpec: &TLSSpec{ + CertificateAuthorityDataSource: &caBundleSource{ + Kind: "ConfigMap", + Name: "awesome-configmap", + Key: "ca-bundle", + }, + }, + namespace: "awesome-namespace", + k8sObjects: []runtime.Object{ + &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "awesome-configmap", + Namespace: "awesome-namespace", + }, + Data: map[string]string{ + "wrong-key": string(bundle), + }, + }, + }, + expectedCondition: &metav1.Condition{ + Type: typeTLSConfigurationValid, + Status: metav1.ConditionFalse, + Reason: ReasonInvalidTLSConfig, + Message: `tls.certificateAuthorityDataSource is invalid: key "ca-bundle" not found in configmap "awesome-namespace/awesome-configmap"`, + }, + }, + { + name: "should return invalid condition when a configmap has the configured key but its value is empty", + tlsSpec: &TLSSpec{ + CertificateAuthorityDataSource: &caBundleSource{ + Kind: "ConfigMap", + Name: "awesome-configmap", + Key: "ca-bundle", + }, + }, + namespace: "awesome-namespace", + k8sObjects: []runtime.Object{ + &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "awesome-configmap", + Namespace: "awesome-namespace", + }, + Data: map[string]string{ + "ca-bundle": "", + }, + }, + }, + expectedCondition: &metav1.Condition{ + Type: typeTLSConfigurationValid, + Status: metav1.ConditionFalse, + Reason: ReasonInvalidTLSConfig, + Message: `tls.certificateAuthorityDataSource is invalid: key "ca-bundle" has empty value in configmap "awesome-namespace/awesome-configmap"`, + }, + }, { name: "should return ca bundle from kubernetes configMap", tlsSpec: &TLSSpec{ @@ -218,6 +342,8 @@ func TestValidateTLSConfig(t *testing.T) { }, }, }, + expectedBundle: bundle, + expectedCertPool: certPool, expectedCondition: &metav1.Condition{ Type: typeTLSConfigurationValid, Status: metav1.ConditionTrue, @@ -234,18 +360,8 @@ func TestValidateTLSConfig(t *testing.T) { Key: "does-not-matter", }, }, - namespace: "awesome-namespace", - k8sObjects: []runtime.Object{ - &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: "awesome-cm", - Namespace: "awesome-namespace", - }, - Data: map[string]string{ - "ca-bundle": string(bundle), - }, - }, - }, + namespace: "awesome-namespace", + k8sObjects: []runtime.Object{}, expectedCondition: &metav1.Condition{ Type: typeTLSConfigurationValid, Status: metav1.ConditionFalse, @@ -262,18 +378,8 @@ func TestValidateTLSConfig(t *testing.T) { Key: "does-not-matter", }, }, - namespace: "awesome-namespace", - k8sObjects: []runtime.Object{ - &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: "awesome-cm", - Namespace: "awesome-namespace", - }, - Data: map[string]string{ - "ca-bundle": string(bundle), - }, - }, - }, + namespace: "awesome-namespace", + k8sObjects: []runtime.Object{}, expectedCondition: &metav1.Condition{ Type: typeTLSConfigurationValid, Status: metav1.ConditionFalse, @@ -317,265 +423,40 @@ func TestValidateTLSConfig(t *testing.T) { var secretsInformer corev1informers.SecretInformer var configMapInformer corev1informers.ConfigMapInformer - if len(tt.k8sObjects) > 0 { - fakeClient := fake.NewSimpleClientset(tt.k8sObjects...) - sharedInformers := informers.NewSharedInformerFactory(fakeClient, 0) - configMapInformer = sharedInformers.Core().V1().ConfigMaps() - secretsInformer = sharedInformers.Core().V1().Secrets() + fakeClient := fake.NewSimpleClientset(tt.k8sObjects...) + sharedInformers := informers.NewSharedInformerFactory(fakeClient, 0) + configMapInformer = sharedInformers.Core().V1().ConfigMaps() + secretsInformer = sharedInformers.Core().V1().Secrets() - // calling the .Informer function registers this informer in the sharedinformer. - // doing this will ensure that this informer will be sync'd when Start is called next. - configMapInformer.Informer() - secretsInformer.Informer() - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - sharedInformers.Start(ctx.Done()) - sharedInformers.WaitForCacheSync(ctx.Done()) - } - actualCondition, _, _ := ValidateTLSConfig(tt.tlsSpec, "tls", tt.namespace, secretsInformer, configMapInformer) - require.Equal(t, tt.expectedCondition, actualCondition) - }) - } -} - -func TestReadCABundleFromK8sSecret(t *testing.T) { - tests := []struct { - name string - secretNamespace string - secretName string - secretKey string - k8sObjects []runtime.Object - expectedData string - expectError string - }{ - { - name: "should return data from existing tls secret and existing key", - secretNamespace: "awesome-namespace", - secretName: "awesome-secret", - secretKey: "awesome", - k8sObjects: []runtime.Object{ - &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "awesome-secret", - Namespace: "awesome-namespace", - }, - Type: corev1.SecretTypeTLS, - Data: map[string][]byte{ - "awesome": []byte("pinniped-is-awesome"), - }, - }, - }, - expectedData: "pinniped-is-awesome", - expectError: "", - }, - { - name: "should return data from existing opaque secret and existing key", - secretNamespace: "awesome-namespace", - secretName: "awesome-secret", - secretKey: "awesome", - k8sObjects: []runtime.Object{ - &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "awesome-secret", - Namespace: "awesome-namespace", - }, - Type: corev1.SecretTypeOpaque, - Data: map[string][]byte{ - "awesome": []byte("pinniped-is-awesome"), - }, - }, - }, - expectedData: "pinniped-is-awesome", - expectError: "", - }, - { - name: "should return error reading a non-existent secret", - secretNamespace: "awesome-namespace", - secretName: "does-not-exist", - secretKey: "does-not-matter", - k8sObjects: []runtime.Object{ - &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "awesome-secret", - Namespace: "awesome-namespace", - }, - Data: map[string][]byte{ - "awesome": []byte("pinniped-is-awesome"), - }, - }, - }, - expectedData: "", - expectError: `failed to get secret "awesome-namespace/does-not-exist": secret "does-not-exist" not found`, - }, - { - name: "should return error when secret has wrong type", - secretNamespace: "awesome-namespace", - secretName: "awesome-secret", - secretKey: "awesome", - k8sObjects: []runtime.Object{ - &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "awesome-secret", - Namespace: "awesome-namespace", - }, - Type: "other-type", - Data: map[string][]byte{ - "awesome": []byte("pinniped-is-awesome"), - }, - }, - }, - - expectError: `secret "awesome-namespace/awesome-secret" of type "other-type" cannot be used as a certificate authority data source`, - }, - { - name: "should return error reading a non-existing key in an existing secret", - secretNamespace: "awesome-namespace", - secretName: "awesome-secret", - secretKey: "something-else", - k8sObjects: []runtime.Object{ - &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "awesome-secret", - Namespace: "awesome-namespace", - }, - Type: corev1.SecretTypeOpaque, - Data: map[string][]byte{ - "awesome": []byte("pinniped-is-awesome"), - }, - }, - }, - expectedData: "", - expectError: `key "something-else" not found in secret "awesome-namespace/awesome-secret"`, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - t.Parallel() - sharedInformers := informers.NewSharedInformerFactory(fake.NewSimpleClientset(tt.k8sObjects...), 0) - secretsInformer := sharedInformers.Core().V1().Secrets() - - // calling the .Informer function registers this informer in the sharedinformer. - // doing this will ensure that this informer will be sync'd when Start is called next. + // Calling the Informer() function registers this informer in the sharedinformer. + // Doing this will ensure that this informer will be sync'd when Start() is called. + // This is needed in this test because we are not using the controller library here, + // which would do these same calls for us. + configMapInformer.Informer() secretsInformer.Informer() ctx, cancel := context.WithCancel(context.Background()) defer cancel() sharedInformers.Start(ctx.Done()) + // This is needed in this test because we are not using the controller library here, + // which would do this same call for us. sharedInformers.WaitForCacheSync(ctx.Done()) - // now the objects from kubernetes should be sync'd into the informer cache. - actualData, actualError := readCABundleFromK8sSecret(tt.secretNamespace, tt.secretName, tt.secretKey, secretsInformer) - if tt.expectError != "" { - require.ErrorContains(t, actualError, tt.expectError) - } else { - require.NoError(t, actualError) - } - require.Equal(t, tt.expectedData, actualData) + + actualCondition, actualBundle, actualCertPool := ValidateTLSConfig(tt.tlsSpec, "tls", tt.namespace, secretsInformer, configMapInformer) + + require.Equal(t, tt.expectedCondition, actualCondition) + require.Equal(t, tt.expectedBundle, actualBundle) + require.True(t, tt.expectedCertPool.Equal(actualCertPool), "expectedCertPool did not equal actualCertPool") }) } } -func TestReadCABundleFromK8sConfigMap(t *testing.T) { - tests := []struct { - name string - configMapNamespace string - configMapName string - configMapKey string - k8sObjects []runtime.Object - expectedData string - expectError string - }{ - { - name: "should return expected data from an existing key in an existing configMap", - configMapNamespace: "awesome-namespace", - configMapName: "awesome-configmap", - configMapKey: "awesome", - k8sObjects: []runtime.Object{ - &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: "awesome-configmap", - Namespace: "awesome-namespace", - }, - Data: map[string]string{ - "awesome": "pinniped-is-awesome", - }, - }, - }, - expectedData: "pinniped-is-awesome", - }, - { - name: "should return error reading a non-existent configMap", - configMapNamespace: "awesome-namespace", - configMapName: "does-not-exist", - configMapKey: "does-not-matter", - k8sObjects: []runtime.Object{ - &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: "awesome-configmap", - Namespace: "awesome-namespace", - }, - Data: map[string]string{ - "awesome": "pinniped-is-awesome", - }, - }, - }, - expectedData: "", - expectError: `failed to get configmap "awesome-namespace/does-not-exist": configmap "does-not-exist" not found`, - }, - { - name: "should return error reading a non-existing key in an existing configMap", - configMapNamespace: "awesome-namespace", - configMapName: "awesome-configmap", - configMapKey: "does-not-exist", - k8sObjects: []runtime.Object{ - &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: "awesome-configmap", - Namespace: "awesome-namespace", - }, - Data: map[string]string{ - "awesome": "pinniped-is-awesome", - }, - }, - }, - expectedData: "", - expectError: `key "does-not-exist" not found in configmap "awesome-namespace/awesome-configmap"`, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - t.Parallel() - fakeClient := fake.NewSimpleClientset(tt.k8sObjects...) - - sharedInformers := informers.NewSharedInformerFactory(fakeClient, 0) - configMapInformer := sharedInformers.Core().V1().ConfigMaps() - - // calling the .Informer function registers this informer in the sharedinformer. - // doing this will ensure that this informer will be sync'd when Start is called next. - configMapInformer.Informer() - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - sharedInformers.Start(ctx.Done()) - sharedInformers.WaitForCacheSync(ctx.Done()) - actualData, actualError := readCABundleFromK8sConfigMap(tt.configMapNamespace, tt.configMapName, tt.configMapKey, configMapInformer) - if tt.expectError != "" { - require.ErrorContains(t, actualError, tt.expectError) - } else { - require.NoError(t, actualError) - } - require.Equal(t, tt.expectedData, actualData) - }) - } -} - -func TestNewCommonTLSSpecForSupervisor(t *testing.T) { +func TestTLSSpecForSupervisor(t *testing.T) { testCA, err := certauthority.New("Test CA", 1*time.Hour) require.NoError(t, err) bundle := testCA.Bundle() base64EncodedBundle := base64.StdEncoding.EncodeToString(bundle) + tests := []struct { name string supervisorTLSSpec *idpv1alpha1.TLSSpec @@ -614,6 +495,25 @@ func TestNewCommonTLSSpecForSupervisor(t *testing.T) { }, }, }, + { + name: "should return tls spec when source has all fields filled", + supervisorTLSSpec: &idpv1alpha1.TLSSpec{ + CertificateAuthorityData: base64EncodedBundle, + CertificateAuthorityDataSource: &idpv1alpha1.CABundleSource{ + Kind: "Secret", + Name: "awesome-secret", + Key: "ca-bundle", + }, + }, + expected: &TLSSpec{ + CertificateAuthorityData: base64EncodedBundle, + CertificateAuthorityDataSource: &caBundleSource{ + Kind: "Secret", + Name: "awesome-secret", + Key: "ca-bundle", + }, + }, + }, } for _, tt := range tests { @@ -625,18 +525,19 @@ func TestNewCommonTLSSpecForSupervisor(t *testing.T) { } } -func TestNewCommonTlsSpecForConcierge(t *testing.T) { +func TestTLSSpecForConcierge(t *testing.T) { testCA, err := certauthority.New("Test CA", 1*time.Hour) require.NoError(t, err) bundle := testCA.Bundle() base64EncodedBundle := base64.StdEncoding.EncodeToString(bundle) + tests := []struct { name string conciergeTLSSpec *authenticationv1alpha1.TLSSpec expected *TLSSpec }{ { - name: "should return nil spec when supervisorTLSSpec is nil", + name: "should return nil spec when TLSSpec is nil", conciergeTLSSpec: nil, expected: nil, }, @@ -668,12 +569,31 @@ func TestNewCommonTlsSpecForConcierge(t *testing.T) { }, }, }, + { + name: "should return tls spec when source has all fields filled", + conciergeTLSSpec: &authenticationv1alpha1.TLSSpec{ + CertificateAuthorityData: base64EncodedBundle, + CertificateAuthorityDataSource: &authenticationv1alpha1.CABundleSource{ + Kind: "Secret", + Name: "awesome-secret", + Key: "ca-bundle", + }, + }, + expected: &TLSSpec{ + CertificateAuthorityData: base64EncodedBundle, + CertificateAuthorityDataSource: &caBundleSource{ + Kind: "Secret", + Name: "awesome-secret", + Key: "ca-bundle", + }, + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { t.Parallel() - actual := TlsSpecForConcierge(tt.conciergeTLSSpec) + actual := TLSSpecForConcierge(tt.conciergeTLSSpec) require.Equal(t, tt.expected, actual) }) } From 2d5943b21a5540375916177e803b4d626c923f79 Mon Sep 17 00:00:00 2001 From: Joshua Casey Date: Mon, 22 Jul 2024 10:53:03 -0500 Subject: [PATCH 21/99] Move conditions reason Success to conditions_util --- .../jwtcachefiller/jwtcachefiller.go | 13 ++++++------ .../webhookcachefiller/webhookcachefiller.go | 9 ++++---- .../federation_domain_watcher.go | 21 +++++++++---------- .../oidcclientvalidator.go | 8 +++---- 4 files changed, 24 insertions(+), 27 deletions(-) diff --git a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go index 7e1d6cc48..2808c9913 100644 --- a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go +++ b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go @@ -56,7 +56,6 @@ const ( typeJWKSFetchValid = "JWKSFetchValid" typeAuthenticatorValid = "AuthenticatorValid" - reasonSuccess = "Success" reasonNotReady = "NotReady" reasonUnableToValidate = "UnableToValidate" reasonInvalidIssuerURL = "InvalidIssuerURL" @@ -376,7 +375,7 @@ func (c *jwtCacheFillerController) validateIssuer(issuer string, conditions []*m conditions = append(conditions, &metav1.Condition{ Type: typeIssuerURLValid, Status: metav1.ConditionTrue, - Reason: reasonSuccess, + Reason: conditionsutil.ReasonSuccess, Message: "issuer is a valid URL", }) return issuerURL, conditions, true @@ -411,7 +410,7 @@ func (c *jwtCacheFillerController) validateProviderDiscovery(ctx context.Context conditions = append(conditions, &metav1.Condition{ Type: typeDiscoveryValid, Status: metav1.ConditionTrue, - Reason: reasonSuccess, + Reason: conditionsutil.ReasonSuccess, Message: msg, }) return pJSON, provider, conditions, nil @@ -470,7 +469,7 @@ func (c *jwtCacheFillerController) validateProviderJWKSURL(provider *coreosoidc. conditions = append(conditions, &metav1.Condition{ Type: typeJWKSURLValid, Status: metav1.ConditionTrue, - Reason: reasonSuccess, + Reason: conditionsutil.ReasonSuccess, Message: "jwks_uri is a valid URL", }) return pJSON.JWKSURL, conditions, nil @@ -529,7 +528,7 @@ func (c *jwtCacheFillerController) validateJWKSFetch(ctx context.Context, jwksUR conditions = append(conditions, &metav1.Condition{ Type: typeJWKSFetchValid, Status: metav1.ConditionTrue, - Reason: reasonSuccess, + Reason: conditionsutil.ReasonSuccess, Message: "successfully fetched jwks", }) return keySet, conditions, nil @@ -619,7 +618,7 @@ func (c *jwtCacheFillerController) newCachedJWTAuthenticator( conditions = append(conditions, &metav1.Condition{ Type: typeAuthenticatorValid, Status: metav1.ConditionTrue, - Reason: reasonSuccess, + Reason: conditionsutil.ReasonSuccess, Message: msg, }) return &cachedJWTAuthenticator{ @@ -650,7 +649,7 @@ func (c *jwtCacheFillerController) updateStatus( conditions = append(conditions, &metav1.Condition{ Type: typeReady, Status: metav1.ConditionTrue, - Reason: reasonSuccess, + Reason: conditionsutil.ReasonSuccess, Message: "the JWTAuthenticator is ready", }) } diff --git a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go index 2074795ff..c4ca47609 100644 --- a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go +++ b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go @@ -47,7 +47,6 @@ const ( typeWebhookConnectionValid = "WebhookConnectionValid" typeEndpointURLValid = "EndpointURLValid" typeAuthenticatorValid = "AuthenticatorValid" - reasonSuccess = "Success" reasonNotReady = "NotReady" reasonUnableToValidate = "UnableToValidate" reasonUnableToCreateClient = "UnableToCreateClient" @@ -291,7 +290,7 @@ func newWebhookAuthenticator( conditions = append(conditions, &metav1.Condition{ Type: typeAuthenticatorValid, Status: metav1.ConditionTrue, - Reason: reasonSuccess, + Reason: conditionsutil.ReasonSuccess, Message: msg, }) @@ -333,7 +332,7 @@ func (c *webhookCacheFillerController) validateConnection(certPool *x509.CertPoo conditions = append(conditions, &metav1.Condition{ Type: typeWebhookConnectionValid, Status: metav1.ConditionTrue, - Reason: reasonSuccess, + Reason: conditionsutil.ReasonSuccess, Message: "successfully dialed webhook server", }) return conditions, nil @@ -391,7 +390,7 @@ func (c *webhookCacheFillerController) validateEndpoint(endpoint string, conditi conditions = append(conditions, &metav1.Condition{ Type: typeEndpointURLValid, Status: metav1.ConditionTrue, - Reason: reasonSuccess, + Reason: conditionsutil.ReasonSuccess, Message: "spec.endpoint is a valid URL", }) return &endpointHostPort, conditions, true @@ -417,7 +416,7 @@ func (c *webhookCacheFillerController) updateStatus( conditions = append(conditions, &metav1.Condition{ Type: typeReady, Status: metav1.ConditionTrue, - Reason: reasonSuccess, + Reason: conditionsutil.ReasonSuccess, Message: "the WebhookAuthenticator is ready", }) } diff --git a/internal/controller/supervisorconfig/federation_domain_watcher.go b/internal/controller/supervisorconfig/federation_domain_watcher.go index b15173819..cfd91a3c4 100644 --- a/internal/controller/supervisorconfig/federation_domain_watcher.go +++ b/internal/controller/supervisorconfig/federation_domain_watcher.go @@ -48,7 +48,6 @@ const ( typeTransformsExpressionsValid = "TransformsExpressionsValid" typeTransformsExamplesPassed = "TransformsExamplesPassed" - reasonSuccess = "Success" reasonNotReady = "NotReady" reasonUnableToValidate = "UnableToValidate" reasonInvalidIssuerURL = "InvalidIssuerURL" @@ -673,7 +672,7 @@ func appendIdentityProviderObjectRefKindCondition(expectedKinds []string, badSuf conditions = append(conditions, &metav1.Condition{ Type: typeIdentityProvidersObjectRefKindValid, Status: metav1.ConditionTrue, - Reason: reasonSuccess, + Reason: conditionsutil.ReasonSuccess, Message: "the kinds specified by .spec.identityProviders[].objectRef.kind are recognized", }) } @@ -701,7 +700,7 @@ func appendIdentityProvidersFoundCondition( conditions = append(conditions, &metav1.Condition{ Type: typeIdentityProvidersFound, Status: metav1.ConditionTrue, - Reason: reasonSuccess, + Reason: conditionsutil.ReasonSuccess, Message: "the resources specified by .spec.identityProviders[].objectRef were found", }) } @@ -721,7 +720,7 @@ func appendIdentityProviderObjectRefAPIGroupSuffixCondition(expectedSuffixName s conditions = append(conditions, &metav1.Condition{ Type: typeIdentityProvidersAPIGroupSuffixValid, Status: metav1.ConditionTrue, - Reason: reasonSuccess, + Reason: conditionsutil.ReasonSuccess, Message: "the API groups specified by .spec.identityProviders[].objectRef.apiGroup are recognized", }) } @@ -740,7 +739,7 @@ func appendTransformsExpressionsValidCondition(messages []string, conditions []* conditions = append(conditions, &metav1.Condition{ Type: typeTransformsExpressionsValid, Status: metav1.ConditionTrue, - Reason: reasonSuccess, + Reason: conditionsutil.ReasonSuccess, Message: "the expressions specified by .spec.identityProviders[].transforms.expressions[] are valid", }) } @@ -759,7 +758,7 @@ func appendTransformsExamplesPassedCondition(messages []string, conditions []*me conditions = append(conditions, &metav1.Condition{ Type: typeTransformsExamplesPassed, Status: metav1.ConditionTrue, - Reason: reasonSuccess, + Reason: conditionsutil.ReasonSuccess, Message: "the examples specified by .spec.identityProviders[].transforms.examples[] had no errors", }) } @@ -779,7 +778,7 @@ func appendIdentityProviderDuplicateDisplayNamesCondition(duplicateDisplayNames conditions = append(conditions, &metav1.Condition{ Type: typeIdentityProvidersDisplayNamesUnique, Status: metav1.ConditionTrue, - Reason: reasonSuccess, + Reason: conditionsutil.ReasonSuccess, Message: "the names specified by .spec.identityProviders[].displayName are unique", }) } @@ -800,7 +799,7 @@ func appendIssuerURLValidCondition(err error, conditions []*metav1.Condition) [] conditions = append(conditions, &metav1.Condition{ Type: typeIssuerURLValid, Status: metav1.ConditionTrue, - Reason: reasonSuccess, + Reason: conditionsutil.ReasonSuccess, Message: "spec.issuer is a valid URL", }) } @@ -827,7 +826,7 @@ func (c *federationDomainWatcherController) updateStatus( conditions = append(conditions, &metav1.Condition{ Type: typeReady, Status: metav1.ConditionTrue, - Reason: reasonSuccess, + Reason: conditionsutil.ReasonSuccess, Message: fmt.Sprintf("the FederationDomain is ready and its endpoints are available: "+ "the discovery endpoint is %s/.well-known/openid-configuration", federationDomain.Spec.Issuer), }) @@ -909,7 +908,7 @@ func (v *crossFederationDomainConfigValidator) Validate(federationDomain *superv conditions = append(conditions, &metav1.Condition{ Type: typeIssuerIsUnique, Status: metav1.ConditionTrue, - Reason: reasonSuccess, + Reason: conditionsutil.ReasonSuccess, Message: "spec.issuer is unique among all FederationDomains", }) } @@ -925,7 +924,7 @@ func (v *crossFederationDomainConfigValidator) Validate(federationDomain *superv conditions = append(conditions, &metav1.Condition{ Type: typeOneTLSSecretPerIssuerHostname, Status: metav1.ConditionTrue, - Reason: reasonSuccess, + Reason: conditionsutil.ReasonSuccess, Message: "all FederationDomains are using the same TLS secret when using the same hostname in the spec.issuer URL", }) } diff --git a/internal/federationdomain/oidcclientvalidator/oidcclientvalidator.go b/internal/federationdomain/oidcclientvalidator/oidcclientvalidator.go index 3058f24ea..5917c2304 100644 --- a/internal/federationdomain/oidcclientvalidator/oidcclientvalidator.go +++ b/internal/federationdomain/oidcclientvalidator/oidcclientvalidator.go @@ -13,6 +13,7 @@ import ( supervisorconfigv1alpha1 "go.pinniped.dev/generated/latest/apis/supervisor/config/v1alpha1" oidcapi "go.pinniped.dev/generated/latest/apis/supervisor/oidc" + "go.pinniped.dev/internal/controller/conditionsutil" "go.pinniped.dev/internal/oidcclientsecretstorage" ) @@ -23,7 +24,6 @@ const ( allowedGrantTypesValid = "AllowedGrantTypesValid" allowedScopesValid = "AllowedScopesValid" - reasonSuccess = "Success" reasonMissingRequiredValue = "MissingRequiredValue" reasonNoClientSecretFound = "NoClientSecretFound" reasonInvalidClientSecretFound = "InvalidClientSecretFound" @@ -79,7 +79,7 @@ func validateAllowedScopes(oidcClient *supervisorconfigv1alpha1.OIDCClient, cond conditions = append(conditions, &metav1.Condition{ Type: allowedScopesValid, Status: metav1.ConditionTrue, - Reason: reasonSuccess, + Reason: conditionsutil.ReasonSuccess, Message: fmt.Sprintf("%q is valid", allowedScopesFieldName), }) } else { @@ -115,7 +115,7 @@ func validateAllowedGrantTypes(oidcClient *supervisorconfigv1alpha1.OIDCClient, conditions = append(conditions, &metav1.Condition{ Type: allowedGrantTypesValid, Status: metav1.ConditionTrue, - Reason: reasonSuccess, + Reason: conditionsutil.ReasonSuccess, Message: fmt.Sprintf("%q is valid", allowedGrantTypesFieldName), }) } else { @@ -201,7 +201,7 @@ func validateSecret(secret *corev1.Secret, conditions []*metav1.Condition, minBc conditions = append(conditions, &metav1.Condition{ Type: clientSecretExists, Status: metav1.ConditionTrue, - Reason: reasonSuccess, + Reason: conditionsutil.ReasonSuccess, Message: fmt.Sprintf("%d client secret(s) found", storedClientSecretsCount), }) return conditions, storedClientSecrets From 66401b42d874ebe0c12784c269c30092d046553e Mon Sep 17 00:00:00 2001 From: Joshua Casey Date: Mon, 22 Jul 2024 14:44:37 -0500 Subject: [PATCH 22/99] Add GitHubIDP tests for a CA bundle in a Secret or a ConfigMap --- .../github_upstream_watcher_test.go | 338 +++++++++++++----- internal/controller/utils.go | 46 --- 2 files changed, 256 insertions(+), 128 deletions(-) diff --git a/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher_test.go b/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher_test.go index d076246d7..6a48663e1 100644 --- a/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher_test.go +++ b/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher_test.go @@ -7,6 +7,7 @@ import ( "bytes" "context" "crypto/tls" + "crypto/x509" "encoding/base64" "fmt" "net" @@ -34,7 +35,6 @@ import ( supervisorfake "go.pinniped.dev/generated/latest/client/supervisor/clientset/versioned/fake" supervisorinformers "go.pinniped.dev/generated/latest/client/supervisor/informers/externalversions" "go.pinniped.dev/internal/certauthority" - pinnipedcontroller "go.pinniped.dev/internal/controller" "go.pinniped.dev/internal/controller/conditionsutil" "go.pinniped.dev/internal/controller/supervisorconfig/upstreamwatchers" "go.pinniped.dev/internal/controllerlib" @@ -65,11 +65,15 @@ func TestController(t *testing.T) { }), tlsserver.RecordTLSHello) goodServerDomain, _ := strings.CutPrefix(goodServer.URL, "https://") goodServerCAB64 := base64.StdEncoding.EncodeToString(goodServerCA) + goodServerCertPool := x509.NewCertPool() + goodServerCertPool.AppendCertsFromPEM(goodServerCA) goodServerIPv6, goodServerIPv6CA := tlsserver.TestServerIPv6(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { }), tlsserver.RecordTLSHello) goodServerIPv6Domain, _ := strings.CutPrefix(goodServerIPv6.URL, "https://") goodServerIPv6CAB64 := base64.StdEncoding.EncodeToString(goodServerIPv6CA) + goodServerIPv6CertPool := x509.NewCertPool() + goodServerIPv6CertPool.AppendCertsFromPEM(goodServerCA) caForUnknownServer, err := certauthority.New("Some Unknown CA", time.Hour) require.NoError(t, err) @@ -87,7 +91,7 @@ func TestController(t *testing.T) { frozenClockForLastTransitionTime := clocktesting.NewFakeClock(wantFrozenTime) wantLastTransitionTime := metav1.Time{Time: wantFrozenTime} - goodSecret := &corev1.Secret{ + goodClientCredentialsSecret := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: "some-secret-name", Namespace: namespace, @@ -99,6 +103,27 @@ func TestController(t *testing.T) { }, } + goodCABundleSecret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "ca-bundle-secret-name", + Namespace: namespace, + }, + Type: corev1.SecretTypeOpaque, + Data: map[string][]byte{ + "ca.crt": goodServerCA, + }, + } + + goodCABundleConfigMap := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "ca-bundle-secret-name", + Namespace: namespace, + }, + Data: map[string]string{ + "ca.crt": string(goodServerCA), + }, + } + validMinimalIDP := &idpv1alpha1.GitHubIdentityProvider{ ObjectMeta: metav1.ObjectMeta{ Name: "minimal-idp-name", @@ -114,7 +139,7 @@ func TestController(t *testing.T) { }, }, Client: idpv1alpha1.GitHubClientSpec{ - SecretName: goodSecret.Name, + SecretName: goodClientCredentialsSecret.Name, }, // These claims are optional when using the actual Kubernetes CRD. // However, they are required here because CRD defaulting/validation does not occur during testing. @@ -155,7 +180,7 @@ func TestController(t *testing.T) { }, }, Client: idpv1alpha1.GitHubClientSpec{ - SecretName: goodSecret.Name, + SecretName: goodClientCredentialsSecret.Name, }, }, } @@ -377,7 +402,7 @@ func TestController(t *testing.T) { tests := []struct { name string githubIdentityProviders []runtime.Object - secrets []runtime.Object + secretsAndConfigMaps []runtime.Object mockDialer func(network, addr string, config *tls.Config) (*tls.Conn, error) wantErr string wantLogs []string @@ -391,8 +416,8 @@ func TestController(t *testing.T) { wantLogs: []string{}, }, { - name: "happy path with all fields", - secrets: []runtime.Object{goodSecret}, + name: "happy path with all fields", + secretsAndConfigMaps: []runtime.Object{goodClientCredentialsSecret}, githubIdentityProviders: []runtime.Object{ validFilledOutIDP, }, @@ -416,7 +441,7 @@ func TestController(t *testing.T) { Scopes: []string{"read:user", "read:org"}, }, AllowedOrganizations: setutil.NewCaseInsensitiveSet("organization1", "org2"), - HttpClient: nil, // let the test runner populate this for us + HttpClient: phttp.Default(goodServerCertPool), }, }, wantResultingUpstreams: []idpv1alpha1.GitHubIdentityProvider{ @@ -447,8 +472,8 @@ func TestController(t *testing.T) { }, }, { - name: "happy path with minimal fields", - secrets: []runtime.Object{goodSecret}, + name: "happy path with minimal fields", + secretsAndConfigMaps: []runtime.Object{goodClientCredentialsSecret}, githubIdentityProviders: []runtime.Object{ validMinimalIDP, }, @@ -472,7 +497,7 @@ func TestController(t *testing.T) { Scopes: []string{"read:user", "read:org"}, }, AllowedOrganizations: setutil.NewCaseInsensitiveSet(), - HttpClient: nil, // let the test runner populate this for us + HttpClient: phttp.Default(goodServerCertPool), }, }, wantResultingUpstreams: []idpv1alpha1.GitHubIdentityProvider{ @@ -503,8 +528,8 @@ func TestController(t *testing.T) { }, }, { - name: "happy path using github.com", - secrets: []runtime.Object{goodSecret}, + name: "happy path using github.com", + secretsAndConfigMaps: []runtime.Object{goodClientCredentialsSecret}, githubIdentityProviders: []runtime.Object{ func() runtime.Object { githubIDP := validMinimalIDP.DeepCopy() @@ -516,10 +541,8 @@ func TestController(t *testing.T) { mockDialer: func(network, addr string, config *tls.Config) (*tls.Conn, error) { require.Equal(t, "github.com:443", addr) // don't actually dial github.com to avoid making external network calls in unit test - certPool, _, err := pinnipedcontroller.BuildCertPoolIDP(validMinimalIDP.Spec.GitHubAPI.TLS) - require.NoError(t, err) configClone := config.Clone() - configClone.RootCAs = certPool + configClone.RootCAs = goodServerCertPool return tls.Dial(network, goodServerDomain, configClone) }, wantResultingCache: []*upstreamgithub.ProviderConfig{ @@ -542,7 +565,7 @@ func TestController(t *testing.T) { Scopes: []string{"read:user", "read:org"}, }, AllowedOrganizations: setutil.NewCaseInsensitiveSet(), - HttpClient: nil, // let the test runner populate this for us + HttpClient: phttp.Default(goodServerCertPool), }, }, wantResultingUpstreams: []idpv1alpha1.GitHubIdentityProvider{ @@ -578,8 +601,8 @@ func TestController(t *testing.T) { }, }, { - name: "happy path with IPv6", - secrets: []runtime.Object{goodSecret}, + name: "happy path with IPv6", + secretsAndConfigMaps: []runtime.Object{goodClientCredentialsSecret}, githubIdentityProviders: []runtime.Object{ func() runtime.Object { ipv6IDP := validMinimalIDP.DeepCopy() @@ -610,7 +633,7 @@ func TestController(t *testing.T) { Scopes: []string{"read:user", "read:org"}, }, AllowedOrganizations: setutil.NewCaseInsensitiveSet(), - HttpClient: nil, // let the test runner populate this for us + HttpClient: phttp.Default(goodServerIPv6CertPool), }, }, wantResultingUpstreams: []idpv1alpha1.GitHubIdentityProvider{ @@ -650,10 +673,10 @@ func TestController(t *testing.T) { }, { name: "multiple idps - two good, one invalid", - secrets: []runtime.Object{ - goodSecret, + secretsAndConfigMaps: []runtime.Object{ + goodClientCredentialsSecret, func() runtime.Object { - otherSecret := goodSecret.DeepCopy() + otherSecret := goodClientCredentialsSecret.DeepCopy() otherSecret.Name = "other-secret-name" otherSecret.Data["clientID"] = []byte("other-client-id") otherSecret.Data["clientSecret"] = []byte("other-client-secret") @@ -667,7 +690,7 @@ func TestController(t *testing.T) { otherIDP.Name = "other-idp-name" otherIDP.Spec.Client.SecretName = "other-secret-name" - // No other test happens to that this particular value passes validation + // No other test happens to verify that this particular value passes validation otherIDP.Spec.Claims.Username = ptr.To(idpv1alpha1.GitHubUsernameLoginAndID) return otherIDP }(), @@ -698,7 +721,7 @@ func TestController(t *testing.T) { Scopes: []string{"read:user", "read:org"}, }, AllowedOrganizations: setutil.NewCaseInsensitiveSet("organization1", "org2"), - HttpClient: nil, // let the test runner populate this for us + HttpClient: phttp.Default(goodServerCertPool), }, { Name: "other-idp-name", @@ -719,7 +742,7 @@ func TestController(t *testing.T) { Scopes: []string{"read:user", "read:org"}, }, AllowedOrganizations: setutil.NewCaseInsensitiveSet("organization1", "org2"), - HttpClient: nil, // let the test runner populate this for us + HttpClient: phttp.Default(goodServerCertPool), }, }, wantResultingUpstreams: []idpv1alpha1.GitHubIdentityProvider{ @@ -819,8 +842,165 @@ func TestController(t *testing.T) { }, }, { - name: "Host error - missing spec.githubAPI.host", - secrets: []runtime.Object{goodSecret}, + name: "happy path for external TLS configuration - one secret and one configmap", + secretsAndConfigMaps: []runtime.Object{ + goodClientCredentialsSecret, + goodCABundleSecret, + goodCABundleConfigMap, + }, + // Note that the order here does not match the order below. + // GitHubIDPs are processed in lexical order. + githubIdentityProviders: []runtime.Object{ + func() runtime.Object { + otherIDP := validFilledOutIDP.DeepCopy() + otherIDP.Name = "idp-with-tls-in-secret" + otherIDP.Spec.GitHubAPI.TLS = &idpv1alpha1.TLSSpec{ + CertificateAuthorityDataSource: &idpv1alpha1.CABundleSource{ + Kind: "Secret", + Name: goodCABundleSecret.Name, + Key: "ca.crt", + }, + } + return otherIDP + }(), + func() runtime.Object { + otherIDP := validFilledOutIDP.DeepCopy() + otherIDP.Name = "idp-with-tls-in-config-map" + otherIDP.Spec.GitHubAPI.TLS = &idpv1alpha1.TLSSpec{ + CertificateAuthorityDataSource: &idpv1alpha1.CABundleSource{ + Kind: "ConfigMap", + Name: goodCABundleConfigMap.Name, + Key: "ca.crt", + }, + } + return otherIDP + }(), + }, + wantResultingCache: []*upstreamgithub.ProviderConfig{ + { + Name: "idp-with-tls-in-secret", + ResourceUID: "some-resource-uid", + APIBaseURL: fmt.Sprintf("https://%s/api/v3", *validFilledOutIDP.Spec.GitHubAPI.Host), + UsernameAttribute: "id", + GroupNameAttribute: "name", + OAuth2Config: &oauth2.Config{ + ClientID: "some-client-id", + ClientSecret: "some-client-secret", + Endpoint: oauth2.Endpoint{ + AuthURL: fmt.Sprintf("https://%s/login/oauth/authorize", *validFilledOutIDP.Spec.GitHubAPI.Host), + DeviceAuthURL: "", // not used + TokenURL: fmt.Sprintf("https://%s/login/oauth/access_token", *validFilledOutIDP.Spec.GitHubAPI.Host), + AuthStyle: oauth2.AuthStyleInParams, + }, + RedirectURL: "", // not used + Scopes: []string{"read:user", "read:org"}, + }, + AllowedOrganizations: setutil.NewCaseInsensitiveSet("organization1", "org2"), + HttpClient: phttp.Default(goodServerCertPool), + }, + { + Name: "idp-with-tls-in-config-map", + ResourceUID: "some-resource-uid", + APIBaseURL: fmt.Sprintf("https://%s/api/v3", *validFilledOutIDP.Spec.GitHubAPI.Host), + UsernameAttribute: "id", + GroupNameAttribute: "name", + OAuth2Config: &oauth2.Config{ + ClientID: "some-client-id", + ClientSecret: "some-client-secret", + Endpoint: oauth2.Endpoint{ + AuthURL: fmt.Sprintf("https://%s/login/oauth/authorize", *validFilledOutIDP.Spec.GitHubAPI.Host), + DeviceAuthURL: "", // not used + TokenURL: fmt.Sprintf("https://%s/login/oauth/access_token", *validFilledOutIDP.Spec.GitHubAPI.Host), + AuthStyle: oauth2.AuthStyleInParams, + }, + RedirectURL: "", // not used + Scopes: []string{"read:user", "read:org"}, + }, + AllowedOrganizations: setutil.NewCaseInsensitiveSet("organization1", "org2"), + HttpClient: phttp.Default(goodServerCertPool), + }, + }, + wantResultingUpstreams: []idpv1alpha1.GitHubIdentityProvider{ + { + ObjectMeta: func() metav1.ObjectMeta { + otherMeta := validFilledOutIDP.ObjectMeta.DeepCopy() + otherMeta.Name = "idp-with-tls-in-config-map" + return *otherMeta + }(), + Spec: func() idpv1alpha1.GitHubIdentityProviderSpec { + otherSpec := validFilledOutIDP.Spec.DeepCopy() + otherSpec.GitHubAPI.TLS = &idpv1alpha1.TLSSpec{ + CertificateAuthorityDataSource: &idpv1alpha1.CABundleSource{ + Kind: "ConfigMap", + Name: goodCABundleSecret.Name, + Key: "ca.crt", + }, + } + return *otherSpec + }(), + Status: idpv1alpha1.GitHubIdentityProviderStatus{ + Phase: idpv1alpha1.GitHubPhaseReady, + Conditions: []metav1.Condition{ + buildClaimsValidatedTrue(t), + buildClientCredentialsSecretValidTrue(t, "some-secret-name"), + buildGitHubConnectionValidTrue(t, *validFilledOutIDP.Spec.GitHubAPI.Host), + buildHostValidTrue(t, *validFilledOutIDP.Spec.GitHubAPI.Host), + buildOrganizationsPolicyValidTrue(t, *validFilledOutIDP.Spec.AllowAuthentication.Organizations.Policy), + buildTLSConfigurationValidTrue(t), + }, + }, + }, + { + ObjectMeta: func() metav1.ObjectMeta { + otherMeta := validFilledOutIDP.ObjectMeta.DeepCopy() + otherMeta.Name = "idp-with-tls-in-secret" + return *otherMeta + }(), + Spec: func() idpv1alpha1.GitHubIdentityProviderSpec { + otherSpec := validFilledOutIDP.Spec.DeepCopy() + otherSpec.GitHubAPI.TLS = &idpv1alpha1.TLSSpec{ + CertificateAuthorityDataSource: &idpv1alpha1.CABundleSource{ + Kind: "Secret", + Name: goodCABundleSecret.Name, + Key: "ca.crt", + }, + } + return *otherSpec + }(), + Status: idpv1alpha1.GitHubIdentityProviderStatus{ + Phase: idpv1alpha1.GitHubPhaseReady, + Conditions: []metav1.Condition{ + buildClaimsValidatedTrue(t), + buildClientCredentialsSecretValidTrue(t, "some-secret-name"), + buildGitHubConnectionValidTrue(t, *validFilledOutIDP.Spec.GitHubAPI.Host), + buildHostValidTrue(t, *validFilledOutIDP.Spec.GitHubAPI.Host), + buildOrganizationsPolicyValidTrue(t, *validFilledOutIDP.Spec.AllowAuthentication.Organizations.Policy), + buildTLSConfigurationValidTrue(t), + }, + }, + }, + }, + wantLogs: []string{ + buildLogForUpdatingClientCredentialsSecretValid("idp-with-tls-in-config-map", "True", "Success", fmt.Sprintf(`clientID and clientSecret have been read from spec.client.SecretName (\"%s\")`, validFilledOutIDP.Spec.Client.SecretName)), + buildLogForUpdatingClaimsValidTrue("idp-with-tls-in-config-map"), + buildLogForUpdatingOrganizationPolicyValid("idp-with-tls-in-config-map", "True", "Success", fmt.Sprintf(`spec.allowAuthentication.organizations.policy (\"%s\") is valid`, string(*validFilledOutIDP.Spec.AllowAuthentication.Organizations.Policy))), + buildLogForUpdatingHostValid("idp-with-tls-in-config-map", "True", "Success", `spec.githubAPI.host (\"%s\") is valid`, *validFilledOutIDP.Spec.GitHubAPI.Host), + buildLogForUpdatingTLSConfigurationValid("idp-with-tls-in-config-map", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), + buildLogForUpdatingGitHubConnectionValid("idp-with-tls-in-config-map", "True", "Success", `spec.githubAPI.host (\"%s\") is reachable and TLS verification succeeds`, *validFilledOutIDP.Spec.GitHubAPI.Host), + buildLogForUpdatingPhase("idp-with-tls-in-config-map", "Ready"), + + buildLogForUpdatingClientCredentialsSecretValid("idp-with-tls-in-secret", "True", "Success", fmt.Sprintf(`clientID and clientSecret have been read from spec.client.SecretName (\"%s\")`, validFilledOutIDP.Spec.Client.SecretName)), + buildLogForUpdatingClaimsValidTrue("idp-with-tls-in-secret"), + buildLogForUpdatingOrganizationPolicyValid("idp-with-tls-in-secret", "True", "Success", fmt.Sprintf(`spec.allowAuthentication.organizations.policy (\"%s\") is valid`, string(*validFilledOutIDP.Spec.AllowAuthentication.Organizations.Policy))), + buildLogForUpdatingHostValid("idp-with-tls-in-secret", "True", "Success", `spec.githubAPI.host (\"%s\") is valid`, *validFilledOutIDP.Spec.GitHubAPI.Host), + buildLogForUpdatingTLSConfigurationValid("idp-with-tls-in-secret", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), + buildLogForUpdatingGitHubConnectionValid("idp-with-tls-in-secret", "True", "Success", `spec.githubAPI.host (\"%s\") is reachable and TLS verification succeeds`, *validFilledOutIDP.Spec.GitHubAPI.Host), + buildLogForUpdatingPhase("idp-with-tls-in-secret", "Ready"), + }, + }, + { + name: "Host error - missing spec.githubAPI.host", + secretsAndConfigMaps: []runtime.Object{goodClientCredentialsSecret}, githubIdentityProviders: []runtime.Object{ func() runtime.Object { badIDP := validFilledOutIDP.DeepCopy() @@ -860,8 +1040,8 @@ func TestController(t *testing.T) { }, }, { - name: "Host error - protocol/schema is specified", - secrets: []runtime.Object{goodSecret}, + name: "Host error - protocol/schema is specified", + secretsAndConfigMaps: []runtime.Object{goodClientCredentialsSecret}, githubIdentityProviders: []runtime.Object{ func() runtime.Object { badIDP := validMinimalIDP.DeepCopy() @@ -901,8 +1081,8 @@ func TestController(t *testing.T) { }, }, { - name: "Host error - path is specified", - secrets: []runtime.Object{goodSecret}, + name: "Host error - path is specified", + secretsAndConfigMaps: []runtime.Object{goodClientCredentialsSecret}, githubIdentityProviders: []runtime.Object{ func() runtime.Object { badIDP := validMinimalIDP.DeepCopy() @@ -942,8 +1122,8 @@ func TestController(t *testing.T) { }, }, { - name: "Host error - userinfo is specified", - secrets: []runtime.Object{goodSecret}, + name: "Host error - userinfo is specified", + secretsAndConfigMaps: []runtime.Object{goodClientCredentialsSecret}, githubIdentityProviders: []runtime.Object{ func() runtime.Object { badIDP := validMinimalIDP.DeepCopy() @@ -983,8 +1163,8 @@ func TestController(t *testing.T) { }, }, { - name: "Host error - query is specified", - secrets: []runtime.Object{goodSecret}, + name: "Host error - query is specified", + secretsAndConfigMaps: []runtime.Object{goodClientCredentialsSecret}, githubIdentityProviders: []runtime.Object{ func() runtime.Object { badIDP := validMinimalIDP.DeepCopy() @@ -1024,8 +1204,8 @@ func TestController(t *testing.T) { }, }, { - name: "Host error - fragment is specified", - secrets: []runtime.Object{goodSecret}, + name: "Host error - fragment is specified", + secretsAndConfigMaps: []runtime.Object{goodClientCredentialsSecret}, githubIdentityProviders: []runtime.Object{ func() runtime.Object { badIDP := validMinimalIDP.DeepCopy() @@ -1065,8 +1245,8 @@ func TestController(t *testing.T) { }, }, { - name: "TLS error - invalid bundle", - secrets: []runtime.Object{goodSecret}, + name: "TLS error - invalid bundle", + secretsAndConfigMaps: []runtime.Object{goodClientCredentialsSecret}, githubIdentityProviders: []runtime.Object{ func() runtime.Object { badIDP := validFilledOutIDP.DeepCopy() @@ -1110,8 +1290,8 @@ func TestController(t *testing.T) { }, }, { - name: "Connection error - no such host", - secrets: []runtime.Object{goodSecret}, + name: "Connection error - no such host", + secretsAndConfigMaps: []runtime.Object{goodClientCredentialsSecret}, githubIdentityProviders: []runtime.Object{ func() runtime.Object { badIDP := validMinimalIDP.DeepCopy() @@ -1152,8 +1332,8 @@ func TestController(t *testing.T) { }, }, { - name: "Connection error - ipv6 without brackets", - secrets: []runtime.Object{goodSecret}, + name: "Connection error - ipv6 without brackets", + secretsAndConfigMaps: []runtime.Object{goodClientCredentialsSecret}, githubIdentityProviders: []runtime.Object{ func() runtime.Object { badIDP := validMinimalIDP.DeepCopy() @@ -1193,8 +1373,8 @@ func TestController(t *testing.T) { }, }, { - name: "Connection error - host not trusted by system certs", - secrets: []runtime.Object{goodSecret}, + name: "Connection error - host not trusted by system certs", + secretsAndConfigMaps: []runtime.Object{goodClientCredentialsSecret}, githubIdentityProviders: []runtime.Object{ func() runtime.Object { badIDP := validFilledOutIDP.DeepCopy() @@ -1235,8 +1415,8 @@ func TestController(t *testing.T) { }, }, { - name: "Connection error - host not trusted by provided CA bundle", - secrets: []runtime.Object{goodSecret}, + name: "Connection error - host not trusted by provided CA bundle", + secretsAndConfigMaps: []runtime.Object{goodClientCredentialsSecret}, githubIdentityProviders: []runtime.Object{ func() runtime.Object { badIDP := validFilledOutIDP.DeepCopy() @@ -1281,8 +1461,8 @@ func TestController(t *testing.T) { }, }, { - name: "Organization Policy error - missing spec.allowAuthentication.organizations.policy", - secrets: []runtime.Object{goodSecret}, + name: "Organization Policy error - missing spec.allowAuthentication.organizations.policy", + secretsAndConfigMaps: []runtime.Object{goodClientCredentialsSecret}, githubIdentityProviders: []runtime.Object{ func() runtime.Object { badIDP := validFilledOutIDP.DeepCopy() @@ -1322,8 +1502,8 @@ func TestController(t *testing.T) { }, }, { - name: "Organization Policy error - invalid spec.allowAuthentication.organizations.policy", - secrets: []runtime.Object{goodSecret}, + name: "Organization Policy error - invalid spec.allowAuthentication.organizations.policy", + secretsAndConfigMaps: []runtime.Object{goodClientCredentialsSecret}, githubIdentityProviders: []runtime.Object{ func() runtime.Object { badIDP := validFilledOutIDP.DeepCopy() @@ -1363,8 +1543,8 @@ func TestController(t *testing.T) { }, }, { - name: "Organization Policy error - spec.allowAuthentication.organizations.policy must be 'OnlyUsersFromAllowedOrganizations' when spec.allowAuthentication.organizations.allowed has organizations listed", - secrets: []runtime.Object{goodSecret}, + name: "Organization Policy error - spec.allowAuthentication.organizations.policy must be 'OnlyUsersFromAllowedOrganizations' when spec.allowAuthentication.organizations.allowed has organizations listed", + secretsAndConfigMaps: []runtime.Object{goodClientCredentialsSecret}, githubIdentityProviders: []runtime.Object{ func() runtime.Object { badIDP := validFilledOutIDP.DeepCopy() @@ -1404,8 +1584,8 @@ func TestController(t *testing.T) { }, }, { - name: "Organization Policy error - spec.allowAuthentication.organizations.policy must be 'AllGitHubUsers' when spec.allowAuthentication.organizations.allowed is empty", - secrets: []runtime.Object{goodSecret}, + name: "Organization Policy error - spec.allowAuthentication.organizations.policy must be 'AllGitHubUsers' when spec.allowAuthentication.organizations.allowed is empty", + secretsAndConfigMaps: []runtime.Object{goodClientCredentialsSecret}, githubIdentityProviders: []runtime.Object{ func() runtime.Object { badIDP := validFilledOutIDP.DeepCopy() @@ -1445,8 +1625,8 @@ func TestController(t *testing.T) { }, }, { - name: "Invalid Claims - missing spec.claims.username", - secrets: []runtime.Object{goodSecret}, + name: "Invalid Claims - missing spec.claims.username", + secretsAndConfigMaps: []runtime.Object{goodClientCredentialsSecret}, githubIdentityProviders: []runtime.Object{ func() runtime.Object { badIDP := validFilledOutIDP.DeepCopy() @@ -1486,8 +1666,8 @@ func TestController(t *testing.T) { }, }, { - name: "Invalid Claims - invalid spec.claims.username", - secrets: []runtime.Object{goodSecret}, + name: "Invalid Claims - invalid spec.claims.username", + secretsAndConfigMaps: []runtime.Object{goodClientCredentialsSecret}, githubIdentityProviders: []runtime.Object{ func() runtime.Object { badIDP := validFilledOutIDP.DeepCopy() @@ -1527,8 +1707,8 @@ func TestController(t *testing.T) { }, }, { - name: "Invalid Claims - missing spec.claims.groups", - secrets: []runtime.Object{goodSecret}, + name: "Invalid Claims - missing spec.claims.groups", + secretsAndConfigMaps: []runtime.Object{goodClientCredentialsSecret}, githubIdentityProviders: []runtime.Object{ func() runtime.Object { badIDP := validFilledOutIDP.DeepCopy() @@ -1568,8 +1748,8 @@ func TestController(t *testing.T) { }, }, { - name: "Invalid Claims - invalid spec.claims.groups", - secrets: []runtime.Object{goodSecret}, + name: "Invalid Claims - invalid spec.claims.groups", + secretsAndConfigMaps: []runtime.Object{goodClientCredentialsSecret}, githubIdentityProviders: []runtime.Object{ func() runtime.Object { badIDP := validFilledOutIDP.DeepCopy() @@ -1610,9 +1790,9 @@ func TestController(t *testing.T) { }, { name: "Client Secret error - in different namespace", - secrets: []runtime.Object{ + secretsAndConfigMaps: []runtime.Object{ func() runtime.Object { - badSecret := goodSecret.DeepCopy() + badSecret := goodClientCredentialsSecret.DeepCopy() badSecret.Namespace = "other-namespace" return badSecret }(), @@ -1653,9 +1833,9 @@ func TestController(t *testing.T) { }, { name: "Client Secret error - wrong type", - secrets: []runtime.Object{ + secretsAndConfigMaps: []runtime.Object{ func() runtime.Object { - badSecret := goodSecret.DeepCopy() + badSecret := goodClientCredentialsSecret.DeepCopy() badSecret.Type = "other-type" return badSecret }(), @@ -1696,9 +1876,9 @@ func TestController(t *testing.T) { }, { name: "Client Secret error - missing clientId", - secrets: []runtime.Object{ + secretsAndConfigMaps: []runtime.Object{ func() runtime.Object { - badSecret := goodSecret.DeepCopy() + badSecret := goodClientCredentialsSecret.DeepCopy() delete(badSecret.Data, "clientID") return badSecret }(), @@ -1739,9 +1919,9 @@ func TestController(t *testing.T) { }, { name: "Client Secret error - missing clientSecret", - secrets: []runtime.Object{ + secretsAndConfigMaps: []runtime.Object{ func() runtime.Object { - badSecret := goodSecret.DeepCopy() + badSecret := goodClientCredentialsSecret.DeepCopy() delete(badSecret.Data, "clientSecret") return badSecret }(), @@ -1782,9 +1962,9 @@ func TestController(t *testing.T) { }, { name: "Client Secret error - additional data", - secrets: []runtime.Object{ + secretsAndConfigMaps: []runtime.Object{ func() runtime.Object { - badSecret := goodSecret.DeepCopy() + badSecret := goodClientCredentialsSecret.DeepCopy() badSecret.Data["foo"] = []byte("bar") return badSecret }(), @@ -1832,7 +2012,7 @@ func TestController(t *testing.T) { fakeSupervisorClient := supervisorfake.NewSimpleClientset(tt.githubIdentityProviders...) supervisorInformers := supervisorinformers.NewSharedInformerFactory(fakeSupervisorClient, 0) - fakeKubeClient := kubernetesfake.NewSimpleClientset(tt.secrets...) + fakeKubeClient := kubernetesfake.NewSimpleClientset(tt.secretsAndConfigMaps...) kubeInformers := k8sinformers.NewSharedInformerFactoryWithOptions(fakeKubeClient, 0) cache := dynamicupstreamprovider.NewDynamicUpstreamIDPProvider() @@ -1903,13 +2083,7 @@ func TestController(t *testing.T) { require.Equal(t, tt.wantResultingCache[i].UsernameAttribute, actualProvider.GetUsernameAttribute()) require.Equal(t, tt.wantResultingCache[i].AllowedOrganizations, actualProvider.GetAllowedOrganizations()) - require.GreaterOrEqual(t, len(tt.githubIdentityProviders), i+1, "there must be at least as many input identity providers as items in the cache") - githubIDP, ok := tt.githubIdentityProviders[i].(*idpv1alpha1.GitHubIdentityProvider) - require.True(t, ok) - certPool, _, err := pinnipedcontroller.BuildCertPoolIDP(githubIDP.Spec.GitHubAPI.TLS) - require.NoError(t, err) - - compareTLSClientConfigWithinHttpClients(t, phttp.Default(certPool), actualProvider.GetConfig().HttpClient) + compareTLSClientConfigWithinHttpClients(t, tt.wantResultingCache[i].HttpClient, actualProvider.GetConfig().HttpClient) require.Equal(t, tt.wantResultingCache[i].OAuth2Config, actualProvider.GetConfig().OAuth2Config) require.Contains(t, tt.wantResultingCache[i].APIBaseURL, actualProvider.GetConfig().APIBaseURL) } diff --git a/internal/controller/utils.go b/internal/controller/utils.go index 193448562..a1600e705 100644 --- a/internal/controller/utils.go +++ b/internal/controller/utils.go @@ -4,17 +4,11 @@ package controller import ( - "crypto/x509" - "encoding/base64" - "fmt" "slices" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/util/cert" - authenticationv1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/authentication/v1alpha1" - idpv1alpha1 "go.pinniped.dev/generated/latest/apis/supervisor/idp/v1alpha1" "go.pinniped.dev/internal/controllerlib" ) @@ -114,43 +108,3 @@ type WithInformerOptionFunc func( // Same signature as controllerlib.WithInitialEvent(). type WithInitialEventOptionFunc func(key controllerlib.Key) controllerlib.Option - -// BuildCertPoolAuth returns a PEM-encoded CA bundle from the provided spec. If the provided spec is nil, a -// nil CA bundle will be returned. If the provided spec contains a CA bundle that is not properly -// encoded, an error will be returned. -func BuildCertPoolAuth(spec *authenticationv1alpha1.TLSSpec) (*x509.CertPool, []byte, error) { - if spec == nil { - return nil, nil, nil - } - - return buildCertPool(spec.CertificateAuthorityData) -} - -// BuildCertPoolIDP returns a PEM-encoded CA bundle from the provided spec. If the provided spec is nil, a -// nil CA bundle will be returned. If the provided spec contains a CA bundle that is not properly -// encoded, an error will be returned. -func BuildCertPoolIDP(spec *idpv1alpha1.TLSSpec) (*x509.CertPool, []byte, error) { - if spec == nil { - return nil, nil, nil - } - - return buildCertPool(spec.CertificateAuthorityData) -} - -func buildCertPool(certificateAuthorityData string) (*x509.CertPool, []byte, error) { - if len(certificateAuthorityData) == 0 { - return nil, nil, nil - } - - pem, err := base64.StdEncoding.DecodeString(certificateAuthorityData) - if err != nil { - return nil, nil, err - } - - rootCAs, err := cert.NewPoolFromBytes(pem) - if err != nil { - return nil, nil, fmt.Errorf("certificateAuthorityData is not valid PEM: %w", err) - } - - return rootCAs, pem, nil -} From 373713f7e0c0d2fa4265d90fd0f9aaaadde9bc72 Mon Sep 17 00:00:00 2001 From: Ryan Richard Date: Mon, 22 Jul 2024 12:28:50 -0700 Subject: [PATCH 23/99] webhook controller redoes validations when external CA bundle changes --- .../jwtcachefiller/jwtcachefiller.go | 14 +- .../webhookcachefiller/webhookcachefiller.go | 71 +++-- .../webhookcachefiller_test.go | 294 +++++++++++++++++- 3 files changed, 325 insertions(+), 54 deletions(-) diff --git a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go index 2808c9913..ed61a436c 100644 --- a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go +++ b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go @@ -209,7 +209,7 @@ func (c *jwtCacheFillerController) Sync(ctx controllerlib.Context) error { } conditions := make([]*metav1.Condition, 0) - rootCAs, conditions, caBundlePEM, tlsOk := c.validateTLSBundle(obj.Spec.TLS, conditions) + certPool, caBundlePEM, conditions, tlsBundleOk := c.validateTLSBundle(obj.Spec.TLS, conditions) caBundlePEMSHA256 := sha256.Sum256(caBundlePEM) // note that this will always return the same hash for nil input // Only revalidate and update the cache if the cached authenticator is different from the desired authenticator. @@ -225,7 +225,7 @@ func (c *jwtCacheFillerController) Sync(ctx controllerlib.Context) error { jwtAuthenticatorFromCache = c.cacheValueAsJWTAuthenticator(valueFromCache) if jwtAuthenticatorFromCache != nil && reflect.DeepEqual(jwtAuthenticatorFromCache.spec, &obj.Spec) && - tlsOk && // if there was any error while validating the CA bundle, then run remaining validations and update status + tlsBundleOk && // if there was any error while validating the CA bundle, then run remaining validations and update status jwtAuthenticatorFromCache.caBundlePEMSHA256 == caBundlePEMSHA256 { c.log.WithValues("jwtAuthenticator", klog.KObj(obj), "issuer", obj.Spec.Issuer). Info("actual jwt authenticator and desired jwt authenticator are the same") @@ -236,9 +236,9 @@ func (c *jwtCacheFillerController) Sync(ctx controllerlib.Context) error { var errs []error _, conditions, issuerOk := c.validateIssuer(obj.Spec.Issuer, conditions) - okSoFar := tlsOk && issuerOk + okSoFar := tlsBundleOk && issuerOk - client := phttp.Default(rootCAs) + client := phttp.Default(certPool) client.Timeout = 30 * time.Second // copied from Kube OIDC code coreOSCtx := coreosoidc.ClientContext(context.Background(), client) @@ -303,8 +303,8 @@ func (c *jwtCacheFillerController) cacheValueAsJWTAuthenticator(value authncache return jwtAuthenticator } -func (c *jwtCacheFillerController) validateTLSBundle(tlsSpec *authenticationv1alpha1.TLSSpec, conditions []*metav1.Condition) (*x509.CertPool, []*metav1.Condition, []byte, bool) { - condition, pemBundle, rootCAs := tlsconfigutil.ValidateTLSConfig( +func (c *jwtCacheFillerController) validateTLSBundle(tlsSpec *authenticationv1alpha1.TLSSpec, conditions []*metav1.Condition) (*x509.CertPool, []byte, []*metav1.Condition, bool) { + condition, pemBundle, certPool := tlsconfigutil.ValidateTLSConfig( tlsconfigutil.TLSSpecForConcierge(tlsSpec), "spec.tls", c.namespace, @@ -312,7 +312,7 @@ func (c *jwtCacheFillerController) validateTLSBundle(tlsSpec *authenticationv1al c.configMapInformer) conditions = append(conditions, condition) - return rootCAs, conditions, pemBundle, condition.Status == metav1.ConditionTrue + return certPool, pemBundle, conditions, condition.Status == metav1.ConditionTrue } func (c *jwtCacheFillerController) validateIssuer(issuer string, conditions []*metav1.Condition) (*url.URL, []*metav1.Condition, bool) { diff --git a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go index c4ca47609..ac6c615cc 100644 --- a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go +++ b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go @@ -6,6 +6,7 @@ package webhookcachefiller import ( "context" + "crypto/sha256" "crypto/tls" "crypto/x509" "fmt" @@ -42,11 +43,13 @@ import ( ) const ( - controllerName = "webhookcachefiller-controller" - typeReady = "Ready" - typeWebhookConnectionValid = "WebhookConnectionValid" - typeEndpointURLValid = "EndpointURLValid" - typeAuthenticatorValid = "AuthenticatorValid" + controllerName = "webhookcachefiller-controller" + + typeReady = "Ready" + typeWebhookConnectionValid = "WebhookConnectionValid" + typeEndpointURLValid = "EndpointURLValid" + typeAuthenticatorValid = "AuthenticatorValid" + reasonNotReady = "NotReady" reasonUnableToValidate = "UnableToValidate" reasonUnableToCreateClient = "UnableToCreateClient" @@ -54,12 +57,14 @@ const ( reasonInvalidEndpointURL = "InvalidEndpointURL" reasonInvalidEndpointURLScheme = "InvalidEndpointURLScheme" reasonUnableToDialServer = "UnableToDialServer" - msgUnableToValidate = "unable to validate; see other conditions for details" + + msgUnableToValidate = "unable to validate; see other conditions for details" ) type cachedWebhookAuthenticator struct { authenticator.Token - spec *authenticationv1alpha1.WebhookAuthenticatorSpec + spec *authenticationv1alpha1.WebhookAuthenticatorSpec + caBundlePEMSHA256 [32]byte } // New instantiates a new controllerlib.Controller which will populate the provided authncache.Cache. @@ -113,8 +118,8 @@ func New( } type webhookCacheFillerController struct { - cache *authncache.Cache namespace string + cache *authncache.Cache webhooks authinformers.WebhookAuthenticatorInformer secretInformer corev1informers.SecretInformer configMapInformer corev1informers.ConfigMapInformer @@ -141,6 +146,10 @@ func (c *webhookCacheFillerController) Sync(ctx controllerlib.Context) error { Name: ctx.Key.Name, } + conditions := make([]*metav1.Condition, 0) + certPool, caBundlePEM, conditions, tlsBundleOk := c.validateTLSBundle(obj.Spec.TLS, conditions) + caBundlePEMSHA256 := sha256.Sum256(caBundlePEM) // note that this will always return the same hash for nil input + // Only revalidate and update the cache if the cached authenticator is different from the desired authenticator. // There is no need to repeat validations for a spec that was already successfully validated. We are making a // design decision to avoid repeating the validation which dials the server, even though the server's TLS @@ -151,7 +160,10 @@ func (c *webhookCacheFillerController) Sync(ctx controllerlib.Context) error { // than to constantly monitor for external issues. if valueFromCache := c.cache.Get(cacheKey); valueFromCache != nil { webhookAuthenticatorFromCache := c.cacheValueAsWebhookAuthenticator(valueFromCache) - if webhookAuthenticatorFromCache != nil && reflect.DeepEqual(webhookAuthenticatorFromCache.spec, &obj.Spec) { + if webhookAuthenticatorFromCache != nil && + reflect.DeepEqual(webhookAuthenticatorFromCache.spec, &obj.Spec) && + tlsBundleOk && // if there was any error while validating the CA bundle, then run remaining validations and update status + webhookAuthenticatorFromCache.caBundlePEMSHA256 == caBundlePEMSHA256 { c.log.WithValues("webhookAuthenticator", klog.KObj(obj), "endpoint", obj.Spec.Endpoint). Info("actual webhook authenticator and desired webhook authenticator are the same") // Stop, no more work to be done. This authenticator is already validated and cached. @@ -159,10 +171,7 @@ func (c *webhookCacheFillerController) Sync(ctx controllerlib.Context) error { } } - conditions := make([]*metav1.Condition, 0) var errs []error - - certPool, pemBytes, conditions, tlsBundleOk := c.validateTLSBundle(obj.Spec.TLS, conditions) endpointHostPort, conditions, endpointOk := c.validateEndpoint(obj.Spec.Endpoint, conditions) okSoFar := tlsBundleOk && endpointOk @@ -174,7 +183,7 @@ func (c *webhookCacheFillerController) Sync(ctx controllerlib.Context) error { // Note that we use the whole URL when constructing the webhook client, // not just the host and port that we validated above. We need the path, etc. obj.Spec.Endpoint, - pemBytes, + caBundlePEM, conditions, okSoFar, ) @@ -187,8 +196,9 @@ func (c *webhookCacheFillerController) Sync(ctx controllerlib.Context) error { c.cache.Delete(cacheKey) } else { c.cache.Store(cacheKey, &cachedWebhookAuthenticator{ - Token: newWebhookAuthenticatorForCache, - spec: obj.Spec.DeepCopy(), // deep copy to avoid caching original object + Token: newWebhookAuthenticatorForCache, + spec: obj.Spec.DeepCopy(), // deep copy to avoid caching original object + caBundlePEMSHA256: caBundlePEMSHA256, }) c.log.WithValues("webhook", klog.KObj(obj), "endpoint", obj.Spec.Endpoint). Info("added new webhook authenticator") @@ -197,10 +207,10 @@ func (c *webhookCacheFillerController) Sync(ctx controllerlib.Context) error { err = c.updateStatus(ctx.Context, obj, conditions) errs = append(errs, err) - // sync loop errors: - // - should not be configuration errors. config errors a user must correct belong on the .Status + // Sync loop errors: + // - Should not be configuration errors. Config errors a user must correct belong on the .Status // object. The controller simply must wait for a user to correct before running again. - // - other errors, such as networking errors, etc. are the types of errors that should return here + // - Other errors, such as networking errors, etc. are the types of errors that should return here // and signal the controller to retry the sync loop. These may be corrected by machines. return utilerrors.NewAggregate(errs) } @@ -218,6 +228,18 @@ func (c *webhookCacheFillerController) cacheValueAsWebhookAuthenticator(value au return webhookAuthenticator } +func (c *webhookCacheFillerController) validateTLSBundle(tlsSpec *authenticationv1alpha1.TLSSpec, conditions []*metav1.Condition) (*x509.CertPool, []byte, []*metav1.Condition, bool) { + condition, pemBundle, certPool := tlsconfigutil.ValidateTLSConfig( + tlsconfigutil.TLSSpecForConcierge(tlsSpec), + "spec.tls", + c.namespace, + c.secretInformer, + c.configMapInformer) + + conditions = append(conditions, condition) + return certPool, pemBundle, conditions, condition.Status == metav1.ConditionTrue +} + // newWebhookAuthenticator creates a webhook from the provided API server url and caBundle // used to validate TLS connections. func newWebhookAuthenticator( @@ -338,18 +360,6 @@ func (c *webhookCacheFillerController) validateConnection(certPool *x509.CertPoo return conditions, nil } -func (c *webhookCacheFillerController) validateTLSBundle(tlsSpec *authenticationv1alpha1.TLSSpec, conditions []*metav1.Condition) (*x509.CertPool, []byte, []*metav1.Condition, bool) { - condition, pemBytes, rootCAs := tlsconfigutil.ValidateTLSConfig( - tlsconfigutil.TLSSpecForConcierge(tlsSpec), - "spec.tls", - c.namespace, - c.secretInformer, - c.configMapInformer) - - conditions = append(conditions, condition) - return rootCAs, pemBytes, conditions, condition.Status == metav1.ConditionTrue -} - func (c *webhookCacheFillerController) validateEndpoint(endpoint string, conditions []*metav1.Condition) (*endpointaddr.HostPort, []*metav1.Condition, bool) { endpointURL, err := url.Parse(endpoint) if err != nil { @@ -432,7 +442,6 @@ func (c *webhookCacheFillerController) updateStatus( if equality.Semantic.DeepEqual(original, updated) { return nil } - _, err := c.client.AuthenticationV1alpha1().WebhookAuthenticators().UpdateStatus(ctx, updated, metav1.UpdateOptions{}) return err } diff --git a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go index b2aa7f482..f1dd871d4 100644 --- a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go +++ b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go @@ -6,6 +6,7 @@ package webhookcachefiller import ( "bytes" "context" + "crypto/sha256" "crypto/tls" "encoding/base64" "encoding/json" @@ -149,6 +150,45 @@ func TestController(t *testing.T) { Endpoint: goodWebhookDefaultServingCertEndpoint, TLS: hostGoodDefaultServingCertServerTLSSpec, } + goodWebhookAuthenticatorSpecWithCAFromSecret := authenticationv1alpha1.WebhookAuthenticatorSpec{ + Endpoint: goodWebhookDefaultServingCertEndpoint, + TLS: &authenticationv1alpha1.TLSSpec{ + CertificateAuthorityDataSource: &authenticationv1alpha1.CABundleSource{ + Kind: "Secret", + Name: "secret-with-ca", + Key: "ca.crt", + }, + }, + } + someSecretWithCA := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "secret-with-ca", + Namespace: "concierge", + }, + Type: corev1.SecretTypeOpaque, + Data: map[string][]byte{ + "ca.crt": hostGoodDefaultServingCertServerCAPEM, + }, + } + goodWebhookAuthenticatorSpecWithCAFromConfigMap := authenticationv1alpha1.WebhookAuthenticatorSpec{ + Endpoint: goodWebhookDefaultServingCertEndpoint, + TLS: &authenticationv1alpha1.TLSSpec{ + CertificateAuthorityDataSource: &authenticationv1alpha1.CABundleSource{ + Kind: "ConfigMap", + Name: "configmap-with-ca", + Key: "ca.crt", + }, + }, + } + someConfigMapWithCA := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "configmap-with-ca", + Namespace: "concierge", + }, + Data: map[string]string{ + "ca.crt": string(hostGoodDefaultServingCertServerCAPEM), + }, + } localWithExampleDotComWeebhookAuthenticatorSpec := authenticationv1alpha1.WebhookAuthenticatorSpec{ // CA for example.com, TLS serving cert for example.com, but endpoint is still localhost Endpoint: hostLocalWithExampleDotComCertServer.URL, @@ -365,10 +405,11 @@ func TestController(t *testing.T) { } tests := []struct { - name string - cache func(*testing.T, *authncache.Cache) - syncKey controllerlib.Key - webhooks []runtime.Object + name string + cache func(*testing.T, *authncache.Cache) + syncKey controllerlib.Key + webhooks []runtime.Object + secretsAndConfigMaps []runtime.Object // for modifying the clients to hack in arbitrary api responses configClient func(*conciergefake.Clientset) wantSyncLoopErr testutil.RequireErrorStringFunc @@ -431,7 +472,100 @@ func TestController(t *testing.T) { wantCacheEntries: 1, }, { - name: "Sync: valid and unchanged WebhookAuthenticator which was already cached: skips any updates to status or cache", + name: "Sync: valid WebhookAuthenticator with CA from Secret: loop will complete successfully and update status conditions", + syncKey: controllerlib.Key{Name: "test-name"}, + webhooks: []runtime.Object{ + &authenticationv1alpha1.WebhookAuthenticator{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-name", + }, + Spec: goodWebhookAuthenticatorSpecWithCAFromSecret, + }, + }, + secretsAndConfigMaps: []runtime.Object{ + someSecretWithCA, + }, + wantLogs: []map[string]any{ + { + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "webhookcachefiller-controller", + "message": "added new webhook authenticator", + "endpoint": goodWebhookDefaultServingCertEndpoint, + "webhook": map[string]any{ + "name": "test-name", + }, + }, + }, + wantActions: func() []coretesting.Action { + updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-name", + }, + Spec: goodWebhookAuthenticatorSpecWithCAFromSecret, + Status: authenticationv1alpha1.WebhookAuthenticatorStatus{ + Conditions: allHappyConditionsSuccess(goodWebhookDefaultServingCertEndpoint, frozenMetav1Now, 0), + Phase: "Ready", + }, + }) + updateStatusAction.Subresource = "status" + return []coretesting.Action{ + coretesting.NewListAction(webhookAuthenticatorGVR, webhookAuthenticatorGVK, "", metav1.ListOptions{}), + coretesting.NewWatchAction(webhookAuthenticatorGVR, "", metav1.ListOptions{}), + updateStatusAction, + } + }, + wantCacheEntries: 1, + }, + { + name: "Sync: valid WebhookAuthenticator with CA from ConfigMap: loop will complete successfully and update status conditions", + syncKey: controllerlib.Key{Name: "test-name"}, + webhooks: []runtime.Object{ + &authenticationv1alpha1.WebhookAuthenticator{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-name", + }, + Spec: goodWebhookAuthenticatorSpecWithCAFromConfigMap, + }, + }, + secretsAndConfigMaps: []runtime.Object{ + someConfigMapWithCA, + }, + wantLogs: []map[string]any{ + { + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "webhookcachefiller-controller", + "message": "added new webhook authenticator", + "endpoint": goodWebhookDefaultServingCertEndpoint, + "webhook": map[string]any{ + "name": "test-name", + }, + }, + }, + wantActions: func() []coretesting.Action { + updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-name", + }, + Spec: goodWebhookAuthenticatorSpecWithCAFromConfigMap, + Status: authenticationv1alpha1.WebhookAuthenticatorStatus{ + Conditions: allHappyConditionsSuccess(goodWebhookDefaultServingCertEndpoint, frozenMetav1Now, 0), + Phase: "Ready", + }, + }) + updateStatusAction.Subresource = "status" + return []coretesting.Action{ + coretesting.NewListAction(webhookAuthenticatorGVR, webhookAuthenticatorGVK, "", metav1.ListOptions{}), + coretesting.NewWatchAction(webhookAuthenticatorGVR, "", metav1.ListOptions{}), + updateStatusAction, + } + }, + wantCacheEntries: 1, + }, + { + name: "Sync: valid WebhookAuthenticator with external and changed CA bundle: loop will complete successfully and update status conditions", + syncKey: controllerlib.Key{Name: "test-name"}, cache: func(t *testing.T, cache *authncache.Cache) { cache.Store( authncache.Key{ @@ -439,7 +573,117 @@ func TestController(t *testing.T) { Kind: "WebhookAuthenticator", APIGroup: authenticationv1alpha1.SchemeGroupVersion.Group, }, - &cachedWebhookAuthenticator{spec: &goodWebhookAuthenticatorSpecWithCA}, + newCacheValue(t, goodWebhookAuthenticatorSpecWithCAFromConfigMap, "some-stale-ca-bundle-pem-content-from-secret"), + ) + }, + webhooks: []runtime.Object{ + &authenticationv1alpha1.WebhookAuthenticator{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-name", + }, + Spec: goodWebhookAuthenticatorSpecWithCAFromConfigMap, + }, + }, + secretsAndConfigMaps: []runtime.Object{ + someConfigMapWithCA, + }, + wantLogs: []map[string]any{ + { + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "webhookcachefiller-controller", + "message": "added new webhook authenticator", + "endpoint": goodWebhookDefaultServingCertEndpoint, + "webhook": map[string]any{ + "name": "test-name", + }, + }, + }, + wantActions: func() []coretesting.Action { + updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-name", + }, + Spec: goodWebhookAuthenticatorSpecWithCAFromConfigMap, + Status: authenticationv1alpha1.WebhookAuthenticatorStatus{ + Conditions: allHappyConditionsSuccess(goodWebhookDefaultServingCertEndpoint, frozenMetav1Now, 0), + Phase: "Ready", + }, + }) + updateStatusAction.Subresource = "status" + return []coretesting.Action{ + coretesting.NewListAction(webhookAuthenticatorGVR, webhookAuthenticatorGVK, "", metav1.ListOptions{}), + coretesting.NewWatchAction(webhookAuthenticatorGVR, "", metav1.ListOptions{}), + updateStatusAction, + } + }, + wantCacheEntries: 1, + }, + { + name: "previously valid cached authenticator (which did not specify a CA bundle) changes and becomes invalid due to any problem with the CA bundle: loop will fail sync, will write failed and unknown status conditions, and will remove authenticator from cache", + syncKey: controllerlib.Key{Name: "test-name"}, + cache: func(t *testing.T, cache *authncache.Cache) { + cache.Store( + authncache.Key{ + Name: "test-name", + Kind: "WebhookAuthenticator", + APIGroup: authenticationv1alpha1.SchemeGroupVersion.Group, + }, + // Force an invalid spec into the cache, which is not very realistic, but it simulates a case + // where the CA bundle goes from being cached as empty to being an error during validation, + // without causing any changes in the spec. This test wants to prove that the rest of the + // validations get run and the resource is update, just in case that can happen somehow. + newCacheValue(t, badWebhookAuthenticatorSpecInvalidTLS, ""), + ) + }, + webhooks: []runtime.Object{ + &authenticationv1alpha1.WebhookAuthenticator{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-name", + }, + Spec: badWebhookAuthenticatorSpecInvalidTLS, + }, + }, + wantActions: func() []coretesting.Action { + updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-name", + }, + Spec: badWebhookAuthenticatorSpecInvalidTLS, + Status: authenticationv1alpha1.WebhookAuthenticatorStatus{ + Conditions: conditionstestutil.Replace( + allHappyConditionsSuccess(goodWebhookDefaultServingCertEndpoint, frozenMetav1Now, 0), + []metav1.Condition{ + sadTLSConfigurationValid(frozenMetav1Now, 0), + unknownWebhookConnectionValid(frozenMetav1Now, 0), + unknownAuthenticatorValid(frozenMetav1Now, 0), + sadReadyCondition(frozenMetav1Now, 0), + }, + ), + Phase: "Error", + }, + }) + updateStatusAction.Subresource = "status" + return []coretesting.Action{ + coretesting.NewListAction(webhookAuthenticatorGVR, webhookAuthenticatorGVK, "", metav1.ListOptions{}), + coretesting.NewWatchAction(webhookAuthenticatorGVR, "", metav1.ListOptions{}), + updateStatusAction, + } + }, + wantCacheEntries: 0, + }, + { + name: "Sync: valid and unchanged WebhookAuthenticator which was already cached: skips any updates to status or cache", + cache: func(t *testing.T, cache *authncache.Cache) { + oldCA, err := base64.StdEncoding.DecodeString(goodWebhookAuthenticatorSpecWithCA.TLS.CertificateAuthorityData) + require.NoError(t, err) + cache.Store( + authncache.Key{ + Name: "test-name", + Kind: "WebhookAuthenticator", + APIGroup: authenticationv1alpha1.SchemeGroupVersion.Group, + }, + newCacheValue(t, goodWebhookAuthenticatorSpecWithCA, string(oldCA)), ) }, syncKey: controllerlib.Key{Name: "test-name"}, @@ -541,13 +785,15 @@ func TestController(t *testing.T) { { name: "Sync: changed WebhookAuthenticator: loop will update timestamps only on relevant statuses", cache: func(t *testing.T, cache *authncache.Cache) { + oldCA, err := base64.StdEncoding.DecodeString(goodWebhookAuthenticatorSpecWith404Endpoint.TLS.CertificateAuthorityData) + require.NoError(t, err) cache.Store( authncache.Key{ Name: "test-name", Kind: "WebhookAuthenticator", APIGroup: authenticationv1alpha1.SchemeGroupVersion.Group, }, - &cachedWebhookAuthenticator{spec: &goodWebhookAuthenticatorSpecWith404Endpoint}, + newCacheValue(t, goodWebhookAuthenticatorSpecWith404Endpoint, string(oldCA)), ) }, syncKey: controllerlib.Key{Name: "test-name"}, @@ -790,13 +1036,15 @@ func TestController(t *testing.T) { { name: "previously valid cached authenticator's spec changes and becomes invalid (e.g. spec.issuer URL is invalid): loop will fail sync, will write failed and unknown status conditions, and will remove authenticator from cache", cache: func(t *testing.T, cache *authncache.Cache) { + oldCA, err := base64.StdEncoding.DecodeString(goodWebhookAuthenticatorSpecWithCA.TLS.CertificateAuthorityData) + require.NoError(t, err) cache.Store( authncache.Key{ Name: "test-name", Kind: "WebhookAuthenticator", APIGroup: authenticationv1alpha1.SchemeGroupVersion.Group, }, - &cachedWebhookAuthenticator{spec: &goodWebhookAuthenticatorSpecWithCA}, + newCacheValue(t, goodWebhookAuthenticatorSpecWithCA, string(oldCA)), ) }, syncKey: controllerlib.Key{Name: "test-name"}, @@ -1492,8 +1740,8 @@ func TestController(t *testing.T) { if tt.configClient != nil { tt.configClient(pinnipedAPIClient) } - informers := conciergeinformers.NewSharedInformerFactory(pinnipedAPIClient, 0) - kubeInformers := kubeinformers.NewSharedInformerFactory(kubernetesfake.NewSimpleClientset(), 0) + pinnipedInformers := conciergeinformers.NewSharedInformerFactory(pinnipedAPIClient, 0) + kubeInformers := kubeinformers.NewSharedInformerFactory(kubernetesfake.NewSimpleClientset(tt.secretsAndConfigMaps...), 0) cache := authncache.New() var log bytes.Buffer @@ -1507,7 +1755,7 @@ func TestController(t *testing.T) { "concierge", // namespace for controller cache, pinnipedAPIClient, - informers.Authentication().V1alpha1().WebhookAuthenticators(), + pinnipedInformers.Authentication().V1alpha1().WebhookAuthenticators(), kubeInformers.Core().V1().Secrets(), kubeInformers.Core().V1().ConfigMaps(), controllerlib.WithInformer, @@ -1517,7 +1765,7 @@ func TestController(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - informers.Start(ctx.Done()) + pinnipedInformers.Start(ctx.Done()) kubeInformers.Start(ctx.Done()) controllerlib.TestRunSynchronously(t, controller) @@ -1528,6 +1776,11 @@ func TestController(t *testing.T) { } else { require.NoError(t, err) } + + require.NotEmpty(t, tt.wantActions, "wantActions is required for test %s", tt.name) + require.Equal(t, tt.wantActions(), pinnipedAPIClient.Actions()) + require.Equal(t, tt.wantCacheEntries, len(cache.Keys()), fmt.Sprintf("expected cache entries is incorrect. wanted:%d, got: %d, keys: %v", tt.wantCacheEntries, len(cache.Keys()), cache.Keys())) + actualLogLines := testutil.SplitByNewline(log.String()) require.Equal(t, len(tt.wantLogs), len(actualLogLines), "log line count should be correct") @@ -1536,6 +1789,7 @@ func TestController(t *testing.T) { var lineStruct map[string]any err := json.Unmarshal([]byte(logLine), &lineStruct) require.NoError(t, err) + require.Equal(t, tt.wantLogs[logLineNum]["level"], lineStruct["level"], fmt.Sprintf("log line (%d) log level should be correct (in: %s)", logLineNum, lineStruct)) require.Equal(t, tt.wantLogs[logLineNum]["timestamp"], lineStruct["timestamp"], fmt.Sprintf("log line (%d) timestamp should be correct (in: %s)", logLineNum, lineStruct)) @@ -1548,11 +1802,10 @@ func TestController(t *testing.T) { if lineStruct["endpoint"] != nil { require.Equal(t, tt.wantLogs[logLineNum]["endpoint"], lineStruct["endpoint"], fmt.Sprintf("log line (%d) endpoint should be correct", logLineNum)) } + if lineStruct["actualType"] != nil { + require.Equal(t, tt.wantLogs[logLineNum]["actualType"], lineStruct["actualType"], fmt.Sprintf("log line (%d) actualType should be correct", logLineNum)) + } } - - require.NotEmpty(t, tt.wantActions, "wantActions is required for test %s", tt.name) - require.Equal(t, tt.wantActions(), pinnipedAPIClient.Actions()) - require.Equal(t, tt.wantCacheEntries, len(cache.Keys()), fmt.Sprintf("expected cache entries is incorrect. wanted:%d, got: %d, keys: %v", tt.wantCacheEntries, len(cache.Keys()), cache.Keys())) }) } } @@ -1683,6 +1936,15 @@ func TestNewWebhookAuthenticator(t *testing.T) { } } +func newCacheValue(t *testing.T, spec authenticationv1alpha1.WebhookAuthenticatorSpec, caBundle string) authncache.Value { + t.Helper() + + return &cachedWebhookAuthenticator{ + spec: &spec, + caBundlePEMSHA256: sha256.Sum256([]byte(caBundle)), + } +} + func TestControllerFilterSecret(t *testing.T) { tests := []struct { name string From 8060e827452165972270ccd7ccd3d518d810fff1 Mon Sep 17 00:00:00 2001 From: Ryan Richard Date: Mon, 22 Jul 2024 14:59:16 -0700 Subject: [PATCH 24/99] include external CA bundles in the cache key in oidc_upstream_watcher.go --- .../oidc_upstream_watcher.go | 115 +++++++++++------- 1 file changed, 70 insertions(+), 45 deletions(-) diff --git a/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher.go b/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher.go index ea7503253..9a73aea91 100644 --- a/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher.go +++ b/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher.go @@ -6,6 +6,7 @@ package oidcupstreamwatcher import ( "context" + "crypto/sha256" "crypto/x509" "fmt" "net/http" @@ -93,33 +94,44 @@ type UpstreamOIDCIdentityProviderICache interface { SetOIDCIdentityProviders([]upstreamprovider.UpstreamOIDCIdentityProviderI) } -// lruValidatorCache caches the *coreosoidc.Provider associated with a particular issuer/TLS configuration. -type lruValidatorCache struct{ cache *cache.Expiring } +// oidcDiscoveryCacheKey is the type of keys in an oidcDiscoveryCache. +type oidcDiscoveryCacheKey struct { + issuer string + caBundleHash [32]byte +} -type lruValidatorCacheEntry struct { +// oidcDiscoveryCacheValue is the type of cache entries in an oidcDiscoveryCache. +type oidcDiscoveryCacheValue struct { provider *coreosoidc.Provider client *http.Client } -func (c *lruValidatorCache) getProvider(spec *idpv1alpha1.OIDCIdentityProviderSpec) (*coreosoidc.Provider, *http.Client) { - if result, ok := c.cache.Get(c.cacheKey(spec)); ok { - entry := result.(*lruValidatorCacheEntry) - return entry.provider, entry.client - } - return nil, nil +// oidcDiscoveryCache caches the discovered provider along with the http Client to use for making calls to that provider, +// for a particular combination OIDC issuer and CA bundle for that issuer. +type oidcDiscoveryCache interface { + getProvider(*oidcDiscoveryCacheKey) *oidcDiscoveryCacheValue + putProvider(*oidcDiscoveryCacheKey, *oidcDiscoveryCacheValue) } -func (c *lruValidatorCache) putProvider(spec *idpv1alpha1.OIDCIdentityProviderSpec, provider *coreosoidc.Provider, client *http.Client) { - c.cache.Set(c.cacheKey(spec), &lruValidatorCacheEntry{provider: provider, client: client}, oidcValidatorCacheTTL) +// ttlProviderCache caches the *coreosoidc.Provider associated with a particular issuer/TLS configuration, +// for a limited time (TTL). +type ttlProviderCache struct{ cache *cache.Expiring } + +// ttlProviderCache implements the oidcDiscoveryCache interface. +var _ oidcDiscoveryCache = (*ttlProviderCache)(nil) + +// getProvider gets an entry from the ttlProviderCache. +func (c *ttlProviderCache) getProvider(key *oidcDiscoveryCacheKey) *oidcDiscoveryCacheValue { + if result, ok := c.cache.Get(key); ok { + entry := result.(*oidcDiscoveryCacheValue) + return entry + } + return nil } -func (c *lruValidatorCache) cacheKey(spec *idpv1alpha1.OIDCIdentityProviderSpec) any { - var key struct{ issuer, caBundle string } - key.issuer = spec.Issuer - if spec.TLS != nil { - key.caBundle = spec.TLS.CertificateAuthorityData - } - return key +// putProvider adds to the ttlProviderCache for a limited period of time. +func (c *ttlProviderCache) putProvider(key *oidcDiscoveryCacheKey, value *oidcDiscoveryCacheValue) { + c.cache.Set(key, value, oidcValidatorCacheTTL) } type oidcWatcherController struct { @@ -129,10 +141,7 @@ type oidcWatcherController struct { oidcIdentityProviderInformer idpinformers.OIDCIdentityProviderInformer secretInformer corev1informers.SecretInformer configMapInformer corev1informers.ConfigMapInformer - validatorCache interface { - getProvider(*idpv1alpha1.OIDCIdentityProviderSpec) (*coreosoidc.Provider, *http.Client) - putProvider(*idpv1alpha1.OIDCIdentityProviderSpec, *coreosoidc.Provider, *http.Client) - } + validatorCache oidcDiscoveryCache } // New instantiates a new controllerlib.Controller which will populate the provided UpstreamOIDCIdentityProviderICache. @@ -152,7 +161,7 @@ func New( oidcIdentityProviderInformer: oidcIdentityProviderInformer, secretInformer: secretInformer, configMapInformer: configMapInformer, - validatorCache: &lruValidatorCache{cache: cache.NewExpiring()}, + validatorCache: &ttlProviderCache{cache: cache.NewExpiring()}, } return controllerlib.New( controllerlib.Config{Name: oidcControllerName, Syncer: &c}, @@ -324,32 +333,46 @@ func (c *oidcWatcherController) validateSecret(upstream *idpv1alpha1.OIDCIdentit // validateIssuer validates the .spec.issuer field, performs OIDC discovery, and returns the appropriate OIDCDiscoverySucceeded condition. func (c *oidcWatcherController) validateIssuer(ctx context.Context, upstream *idpv1alpha1.OIDCIdentityProvider, result *upstreamoidc.ProviderConfig) []*metav1.Condition { - tlsCondition, _, certPool := tlsconfigutil.ValidateTLSConfig( + tlsCondition, caBundlePEM, certPool := tlsconfigutil.ValidateTLSConfig( tlsconfigutil.TLSSpecForSupervisor(upstream.Spec.TLS), "spec.tls", upstream.Namespace, c.secretInformer, c.configMapInformer) - // TODO: If either the spec or the CA bundle has changed, then we need to redo the validations below. So maybe the cache key should be the combination of spec and bundle (or hash of bundle)? - // Get the provider and HTTP Client from cache if possible. - discoveredProvider, httpClient := c.validatorCache.getProvider(&upstream.Spec) + // When the TLS config is invalid, return some error conditions. + if tlsCondition.Reason != conditionsutil.ReasonSuccess { + return []*metav1.Condition{ + { + Type: typeOIDCDiscoverySucceeded, + Status: metav1.ConditionFalse, + Reason: tlsconfigutil.ReasonInvalidTLSConfig, + Message: tlsCondition.Message, + }, + tlsCondition, + } + } + + var discoveredProvider *coreosoidc.Provider + var httpClient *http.Client + + // Get the discovered provider and HTTP client from cache, if they are found in the cache. + cacheKey := &oidcDiscoveryCacheKey{ + issuer: upstream.Spec.Issuer, + caBundleHash: sha256.Sum256(caBundlePEM), // note that this will always return the same hash for nil input + } + if cacheEntry := c.validatorCache.getProvider(cacheKey); cacheEntry != nil { + discoveredProvider = cacheEntry.provider + httpClient = cacheEntry.client + c.log.WithValues( + "namespace", upstream.Namespace, + "name", upstream.Name, + "issuer", upstream.Spec.Issuer, + ).Debug("found previous OIDC discovery result in cache") + } // If the provider does not exist in the cache, do a fresh discovery lookup and save to the cache. if discoveredProvider == nil { - var err error - if tlsCondition.Reason != conditionsutil.ReasonSuccess { - return []*metav1.Condition{ - { - Type: typeOIDCDiscoverySucceeded, - Status: metav1.ConditionFalse, - Reason: tlsconfigutil.ReasonInvalidTLSConfig, - Message: tlsCondition.Message, - }, - tlsCondition, - } - } - httpClient = defaultClientShortTimeout(certPool) _, issuerURLCondition := validateHTTPSURL(upstream.Spec.Issuer, "issuer", reasonUnreachable) @@ -357,6 +380,7 @@ func (c *oidcWatcherController) validateIssuer(ctx context.Context, upstream *id return []*metav1.Condition{issuerURLCondition, tlsCondition} } + var err error discoveredProvider, err = coreosoidc.NewProvider(coreosoidc.ClientContext(ctx, httpClient), upstream.Spec.Issuer) if err != nil { c.log.WithValues( @@ -366,17 +390,18 @@ func (c *oidcWatcherController) validateIssuer(ctx context.Context, upstream *id ).Error("failed to perform OIDC discovery", err) return []*metav1.Condition{ { - Type: typeOIDCDiscoverySucceeded, - Status: metav1.ConditionFalse, - Reason: reasonUnreachable, - Message: fmt.Sprintf("failed to perform OIDC discovery against %q:\n%s", upstream.Spec.Issuer, pinnipedcontroller.TruncateMostLongErr(err)), + Type: typeOIDCDiscoverySucceeded, + Status: metav1.ConditionFalse, + Reason: reasonUnreachable, + Message: fmt.Sprintf("failed to perform OIDC discovery against %q:\n%s", + upstream.Spec.Issuer, pinnipedcontroller.TruncateMostLongErr(err)), }, tlsCondition, } } // Update the cache with the newly discovered value. - c.validatorCache.putProvider(&upstream.Spec, discoveredProvider, httpClient) + c.validatorCache.putProvider(cacheKey, &oidcDiscoveryCacheValue{provider: discoveredProvider, client: httpClient}) } // Get the revocation endpoint, if there is one. Many providers do not offer a revocation endpoint. From 72745cd8fe1ec9f577bc2c013b9255338cb4cb29 Mon Sep 17 00:00:00 2001 From: Ryan Richard Date: Mon, 22 Jul 2024 15:41:46 -0700 Subject: [PATCH 25/99] run codegen to update copyrights --- .../1.24/apis/concierge/authentication/v1alpha1/types_tls.go | 2 +- .../1.25/apis/concierge/authentication/v1alpha1/types_tls.go | 2 +- .../1.26/apis/concierge/authentication/v1alpha1/types_tls.go | 2 +- .../1.27/apis/concierge/authentication/v1alpha1/types_tls.go | 2 +- .../1.28/apis/concierge/authentication/v1alpha1/types_tls.go | 2 +- .../1.29/apis/concierge/authentication/v1alpha1/types_tls.go | 2 +- .../1.30/apis/concierge/authentication/v1alpha1/types_tls.go | 2 +- .../latest/apis/concierge/authentication/v1alpha1/types_tls.go | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/generated/1.24/apis/concierge/authentication/v1alpha1/types_tls.go b/generated/1.24/apis/concierge/authentication/v1alpha1/types_tls.go index e8916dfa5..ad2278985 100644 --- a/generated/1.24/apis/concierge/authentication/v1alpha1/types_tls.go +++ b/generated/1.24/apis/concierge/authentication/v1alpha1/types_tls.go @@ -1,4 +1,4 @@ -// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved. +// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package v1alpha1 diff --git a/generated/1.25/apis/concierge/authentication/v1alpha1/types_tls.go b/generated/1.25/apis/concierge/authentication/v1alpha1/types_tls.go index e8916dfa5..ad2278985 100644 --- a/generated/1.25/apis/concierge/authentication/v1alpha1/types_tls.go +++ b/generated/1.25/apis/concierge/authentication/v1alpha1/types_tls.go @@ -1,4 +1,4 @@ -// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved. +// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package v1alpha1 diff --git a/generated/1.26/apis/concierge/authentication/v1alpha1/types_tls.go b/generated/1.26/apis/concierge/authentication/v1alpha1/types_tls.go index e8916dfa5..ad2278985 100644 --- a/generated/1.26/apis/concierge/authentication/v1alpha1/types_tls.go +++ b/generated/1.26/apis/concierge/authentication/v1alpha1/types_tls.go @@ -1,4 +1,4 @@ -// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved. +// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package v1alpha1 diff --git a/generated/1.27/apis/concierge/authentication/v1alpha1/types_tls.go b/generated/1.27/apis/concierge/authentication/v1alpha1/types_tls.go index e8916dfa5..ad2278985 100644 --- a/generated/1.27/apis/concierge/authentication/v1alpha1/types_tls.go +++ b/generated/1.27/apis/concierge/authentication/v1alpha1/types_tls.go @@ -1,4 +1,4 @@ -// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved. +// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package v1alpha1 diff --git a/generated/1.28/apis/concierge/authentication/v1alpha1/types_tls.go b/generated/1.28/apis/concierge/authentication/v1alpha1/types_tls.go index e8916dfa5..ad2278985 100644 --- a/generated/1.28/apis/concierge/authentication/v1alpha1/types_tls.go +++ b/generated/1.28/apis/concierge/authentication/v1alpha1/types_tls.go @@ -1,4 +1,4 @@ -// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved. +// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package v1alpha1 diff --git a/generated/1.29/apis/concierge/authentication/v1alpha1/types_tls.go b/generated/1.29/apis/concierge/authentication/v1alpha1/types_tls.go index e8916dfa5..ad2278985 100644 --- a/generated/1.29/apis/concierge/authentication/v1alpha1/types_tls.go +++ b/generated/1.29/apis/concierge/authentication/v1alpha1/types_tls.go @@ -1,4 +1,4 @@ -// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved. +// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package v1alpha1 diff --git a/generated/1.30/apis/concierge/authentication/v1alpha1/types_tls.go b/generated/1.30/apis/concierge/authentication/v1alpha1/types_tls.go index e8916dfa5..ad2278985 100644 --- a/generated/1.30/apis/concierge/authentication/v1alpha1/types_tls.go +++ b/generated/1.30/apis/concierge/authentication/v1alpha1/types_tls.go @@ -1,4 +1,4 @@ -// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved. +// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package v1alpha1 diff --git a/generated/latest/apis/concierge/authentication/v1alpha1/types_tls.go b/generated/latest/apis/concierge/authentication/v1alpha1/types_tls.go index e8916dfa5..ad2278985 100644 --- a/generated/latest/apis/concierge/authentication/v1alpha1/types_tls.go +++ b/generated/latest/apis/concierge/authentication/v1alpha1/types_tls.go @@ -1,4 +1,4 @@ -// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved. +// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package v1alpha1 From 288e092d2ed7f4ee4c459562da600cad36cc9bc7 Mon Sep 17 00:00:00 2001 From: Joshua Casey Date: Mon, 22 Jul 2024 23:47:45 -0500 Subject: [PATCH 26/99] GitHub IDP watcher should not dial an address that has already been validated --- .../github_upstream_watcher.go | 131 ++++++++---- .../github_upstream_watcher_test.go | 187 +++++++++++++++--- internal/supervisor/server/server.go | 2 + 3 files changed, 257 insertions(+), 63 deletions(-) diff --git a/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher.go b/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher.go index 9a58f7e80..7003a4cbf 100644 --- a/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher.go +++ b/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher.go @@ -6,6 +6,7 @@ package githubupstreamwatcher import ( "context" + "crypto/sha256" "crypto/tls" "crypto/x509" "errors" @@ -14,6 +15,7 @@ import ( "net/http" "slices" "strings" + "time" "golang.org/x/oauth2" corev1 "k8s.io/api/core/v1" @@ -21,6 +23,7 @@ import ( apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/util/cache" utilerrors "k8s.io/apimachinery/pkg/util/errors" corev1informers "k8s.io/client-go/informers/core/v1" "k8s.io/utils/clock" @@ -67,6 +70,42 @@ type UpstreamGitHubIdentityProviderICache interface { SetGitHubIdentityProviders([]upstreamprovider.UpstreamGithubIdentityProviderI) } +type GitHubValidatedAPICacheI interface { + MarkAsValidated(address string, caBundlePEM []byte) + IsValid(address string, caBundlePEM []byte) bool +} + +type GitHubValidatedAPICache struct { + cache *cache.Expiring +} + +type GitHubValidatedAPICacheKey struct { + address string + caBundlePEMSHA256 [32]byte +} + +func (g *GitHubValidatedAPICache) MarkAsValidated(address string, caBundlePEM []byte) { + key := GitHubValidatedAPICacheKey{ + address: address, + caBundlePEMSHA256: sha256.Sum256(caBundlePEM), + } + // Existence in the cache means it has been validated + g.cache.Set(key, nil, 24*365*time.Hour) +} + +func (g *GitHubValidatedAPICache) IsValid(address string, caBundlePEM []byte) bool { + key := GitHubValidatedAPICacheKey{ + address: address, + caBundlePEMSHA256: sha256.Sum256(caBundlePEM), + } + _, ok := g.cache.Get(key) + return ok +} + +func NewGitHubValidatedAPICache(cache *cache.Expiring) GitHubValidatedAPICacheI { + return &GitHubValidatedAPICache{cache: cache} +} + type gitHubWatcherController struct { namespace string cache UpstreamGitHubIdentityProviderICache @@ -77,6 +116,7 @@ type gitHubWatcherController struct { configMapInformer corev1informers.ConfigMapInformer clock clock.Clock dialFunc func(network, addr string, config *tls.Config) (*tls.Conn, error) + validatedCache GitHubValidatedAPICacheI } // New instantiates a new controllerlib.Controller which will populate the provided UpstreamGitHubIdentityProviderICache. @@ -91,6 +131,7 @@ func New( withInformer pinnipedcontroller.WithInformerOptionFunc, clock clock.Clock, dialFunc func(network, addr string, config *tls.Config) (*tls.Conn, error), + validatedCache *cache.Expiring, ) controllerlib.Controller { c := gitHubWatcherController{ namespace: namespace, @@ -102,6 +143,7 @@ func New( configMapInformer: configMapInformer, clock: clock, dialFunc: dialFunc, + validatedCache: NewGitHubValidatedAPICache(validatedCache), } return controllerlib.New( @@ -279,19 +321,38 @@ func (c *gitHubWatcherController) validateUpstreamAndUpdateConditions(ctx contro hostCondition, hostPort := validateHost(upstream.Spec.GitHubAPI) conditions = append(conditions, hostCondition) - tlsConfigCondition, certPool := c.validateTLSConfiguration(upstream.Spec.GitHubAPI.TLS) + tlsConfigCondition, caBundlePEM, certPool := tlsconfigutil.ValidateTLSConfig( + tlsconfigutil.TLSSpecForSupervisor(upstream.Spec.GitHubAPI.TLS), + "spec.githubAPI.tls", + c.namespace, + c.secretInformer, + c.configMapInformer) conditions = append(conditions, tlsConfigCondition) - // TODO: skip this if it is already validated for this same spec and CA bundle (or perhaps hash of CA bundle) - githubConnectionCondition, hostURL, httpClient, githubConnectionErr := c.validateGitHubConnection( - hostPort, - certPool, - hostCondition.Status == metav1.ConditionTrue && tlsConfigCondition.Status == metav1.ConditionTrue, - ) - if githubConnectionErr != nil { - applicationErrors = append(applicationErrors, githubConnectionErr) + var hostURL string + var httpClient *http.Client + if hostCondition.Status != metav1.ConditionTrue || tlsConfigCondition.Status != metav1.ConditionTrue { + conditions = append(conditions, &metav1.Condition{ + Type: GitHubConnectionValid, + Status: metav1.ConditionUnknown, + Reason: "UnableToValidate", + Message: "unable to validate; see other conditions for details", + }) + } else { + address := hostPort.Endpoint() + var githubConnectionCondition *metav1.Condition + var githubConnectionErr error + + githubConnectionCondition, hostURL, httpClient, githubConnectionErr = c.validateGitHubConnection( + address, + caBundlePEM, + certPool, + ) + if githubConnectionErr != nil { + applicationErrors = append(applicationErrors, githubConnectionErr) + } + conditions = append(conditions, githubConnectionCondition) } - conditions = append(conditions, githubConnectionCondition) // The critical pattern to maintain is that every run of the sync loop will populate the exact number of the exact // same set of conditions. Conditions depending on other conditions should get Status: metav1.ConditionUnknown, or @@ -373,47 +434,35 @@ func validateHost(gitHubAPIConfig idpv1alpha1.GitHubAPIConfig) (*metav1.Conditio }, &hostPort } -func (c *gitHubWatcherController) validateTLSConfiguration(tlsSpec *idpv1alpha1.TLSSpec) (*metav1.Condition, *x509.CertPool) { - tlsCondition, _, certPool := tlsconfigutil.ValidateTLSConfig( - tlsconfigutil.TLSSpecForSupervisor(tlsSpec), - "spec.githubAPI.tls", - c.namespace, - c.secretInformer, - c.configMapInformer) - - return tlsCondition, certPool -} - func (c *gitHubWatcherController) validateGitHubConnection( - hostPort *endpointaddr.HostPort, + address string, + caBundlePEM []byte, certPool *x509.CertPool, - validSoFar bool, ) (*metav1.Condition, string, *http.Client, error) { - if !validSoFar { - return &metav1.Condition{ - Type: GitHubConnectionValid, - Status: metav1.ConditionUnknown, - Reason: "UnableToValidate", - Message: "unable to validate; see other conditions for details", - }, "", nil, nil + var conn *tls.Conn + var tlsDialErr error + + if !c.validatedCache.IsValid(address, caBundlePEM) { + conn, tlsDialErr = c.dialFunc("tcp", address, ptls.Default(certPool)) + if tlsDialErr != nil { + return &metav1.Condition{ + Type: GitHubConnectionValid, + Status: metav1.ConditionFalse, + Reason: "UnableToDialServer", + Message: fmt.Sprintf("cannot dial server spec.githubAPI.host (%q): %s", address, buildDialErrorMessage(tlsDialErr)), + }, "", nil, tlsDialErr + } + tlsDialErr = conn.Close() } - conn, tlsDialErr := c.dialFunc("tcp", hostPort.Endpoint(), ptls.Default(certPool)) - if tlsDialErr != nil { - return &metav1.Condition{ - Type: GitHubConnectionValid, - Status: metav1.ConditionFalse, - Reason: "UnableToDialServer", - Message: fmt.Sprintf("cannot dial server spec.githubAPI.host (%q): %s", hostPort.Endpoint(), buildDialErrorMessage(tlsDialErr)), - }, "", nil, tlsDialErr - } + c.validatedCache.MarkAsValidated(address, caBundlePEM) return &metav1.Condition{ Type: GitHubConnectionValid, Status: metav1.ConditionTrue, Reason: conditionsutil.ReasonSuccess, - Message: fmt.Sprintf("spec.githubAPI.host (%q) is reachable and TLS verification succeeds", hostPort.Endpoint()), - }, fmt.Sprintf("https://%s", hostPort.Endpoint()), phttp.Default(certPool), conn.Close() + Message: fmt.Sprintf("spec.githubAPI.host (%q) is reachable and TLS verification succeeds", address), + }, fmt.Sprintf("https://%s", address), phttp.Default(certPool), tlsDialErr } // buildDialErrorMessage standardizes DNS error messages that appear differently on different platforms, so that tests and log grepping is uniform. diff --git a/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher_test.go b/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher_test.go index 6a48663e1..15ca58aaf 100644 --- a/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher_test.go +++ b/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher_test.go @@ -6,12 +6,14 @@ package githubupstreamwatcher import ( "bytes" "context" + "crypto/sha256" "crypto/tls" "crypto/x509" "encoding/base64" "fmt" "net" "net/http" + "slices" "strings" "testing" "time" @@ -23,6 +25,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/cache" utilnet "k8s.io/apimachinery/pkg/util/net" k8sinformers "k8s.io/client-go/informers" kubernetesfake "k8s.io/client-go/kubernetes/fake" @@ -400,14 +403,16 @@ func TestController(t *testing.T) { } tests := []struct { - name string - githubIdentityProviders []runtime.Object - secretsAndConfigMaps []runtime.Object - mockDialer func(network, addr string, config *tls.Config) (*tls.Conn, error) - wantErr string - wantLogs []string - wantResultingCache []*upstreamgithub.ProviderConfig - wantResultingUpstreams []idpv1alpha1.GitHubIdentityProvider + name string + githubIdentityProviders []runtime.Object + secretsAndConfigMaps []runtime.Object + mockDialer func(t *testing.T) func(network, addr string, config *tls.Config) (*tls.Conn, error) + preexistingValidatedCache []GitHubValidatedAPICacheKey + wantErr string + wantLogs []string + wantResultingCache []*upstreamgithub.ProviderConfig + wantResultingUpstreams []idpv1alpha1.GitHubIdentityProvider + wantValidatedCache []GitHubValidatedAPICacheKey }{ { name: "no GitHubIdentityProviders", @@ -444,6 +449,12 @@ func TestController(t *testing.T) { HttpClient: phttp.Default(goodServerCertPool), }, }, + wantValidatedCache: []GitHubValidatedAPICacheKey{ + { + address: goodServerDomain, + caBundlePEMSHA256: sha256.Sum256(goodServerCA), + }, + }, wantResultingUpstreams: []idpv1alpha1.GitHubIdentityProvider{ { ObjectMeta: validFilledOutIDP.ObjectMeta, @@ -500,6 +511,12 @@ func TestController(t *testing.T) { HttpClient: phttp.Default(goodServerCertPool), }, }, + wantValidatedCache: []GitHubValidatedAPICacheKey{ + { + address: goodServerDomain, + caBundlePEMSHA256: sha256.Sum256(goodServerCA), + }, + }, wantResultingUpstreams: []idpv1alpha1.GitHubIdentityProvider{ { ObjectMeta: validMinimalIDP.ObjectMeta, @@ -538,12 +555,16 @@ func TestController(t *testing.T) { return githubIDP }(), }, - mockDialer: func(network, addr string, config *tls.Config) (*tls.Conn, error) { - require.Equal(t, "github.com:443", addr) - // don't actually dial github.com to avoid making external network calls in unit test - configClone := config.Clone() - configClone.RootCAs = goodServerCertPool - return tls.Dial(network, goodServerDomain, configClone) + mockDialer: func(t *testing.T) func(network, addr string, config *tls.Config) (*tls.Conn, error) { + t.Helper() + + return func(network, addr string, config *tls.Config) (*tls.Conn, error) { + require.Equal(t, "github.com:443", addr) + // don't actually dial github.com to avoid making external network calls in unit test + configClone := config.Clone() + configClone.RootCAs = goodServerCertPool + return tls.Dial(network, goodServerDomain, configClone) + } }, wantResultingCache: []*upstreamgithub.ProviderConfig{ { @@ -568,6 +589,12 @@ func TestController(t *testing.T) { HttpClient: phttp.Default(goodServerCertPool), }, }, + wantValidatedCache: []GitHubValidatedAPICacheKey{ + { + address: "github.com:443", + caBundlePEMSHA256: sha256.Sum256(goodServerCA), + }, + }, wantResultingUpstreams: []idpv1alpha1.GitHubIdentityProvider{ { ObjectMeta: validMinimalIDP.ObjectMeta, @@ -636,6 +663,12 @@ func TestController(t *testing.T) { HttpClient: phttp.Default(goodServerIPv6CertPool), }, }, + wantValidatedCache: []GitHubValidatedAPICacheKey{ + { + address: goodServerIPv6Domain, + caBundlePEMSHA256: sha256.Sum256(goodServerIPv6CA), + }, + }, wantResultingUpstreams: []idpv1alpha1.GitHubIdentityProvider{ { ObjectMeta: validMinimalIDP.ObjectMeta, @@ -745,6 +778,12 @@ func TestController(t *testing.T) { HttpClient: phttp.Default(goodServerCertPool), }, }, + wantValidatedCache: []GitHubValidatedAPICacheKey{ + { + address: goodServerDomain, + caBundlePEMSHA256: sha256.Sum256(goodServerCA), + }, + }, wantResultingUpstreams: []idpv1alpha1.GitHubIdentityProvider{ { ObjectMeta: func() metav1.ObjectMeta { @@ -920,6 +959,12 @@ func TestController(t *testing.T) { HttpClient: phttp.Default(goodServerCertPool), }, }, + wantValidatedCache: []GitHubValidatedAPICacheKey{ + { + address: goodServerDomain, + caBundlePEMSHA256: sha256.Sum256(goodServerCA), + }, + }, wantResultingUpstreams: []idpv1alpha1.GitHubIdentityProvider{ { ObjectMeta: func() metav1.ObjectMeta { @@ -998,6 +1043,81 @@ func TestController(t *testing.T) { buildLogForUpdatingPhase("idp-with-tls-in-secret", "Ready"), }, }, + { + name: "happy path with previously validated address/CA Bundle does not validate again", + secretsAndConfigMaps: []runtime.Object{goodClientCredentialsSecret}, + githubIdentityProviders: []runtime.Object{validFilledOutIDP}, + mockDialer: func(t *testing.T) func(network, addr string, config *tls.Config) (*tls.Conn, error) { + t.Helper() + + return func(network, addr string, config *tls.Config) (*tls.Conn, error) { + t.Errorf("this test should not perform dial") + t.FailNow() + return nil, nil + } + }, + preexistingValidatedCache: []GitHubValidatedAPICacheKey{ + { + address: goodServerDomain, + caBundlePEMSHA256: sha256.Sum256(goodServerCA), + }, + }, + wantResultingCache: []*upstreamgithub.ProviderConfig{ + { + Name: "some-idp-name", + ResourceUID: "some-resource-uid", + APIBaseURL: fmt.Sprintf("https://%s/api/v3", *validFilledOutIDP.Spec.GitHubAPI.Host), + UsernameAttribute: "id", + GroupNameAttribute: "name", + OAuth2Config: &oauth2.Config{ + ClientID: "some-client-id", + ClientSecret: "some-client-secret", + Endpoint: oauth2.Endpoint{ + AuthURL: fmt.Sprintf("https://%s/login/oauth/authorize", *validFilledOutIDP.Spec.GitHubAPI.Host), + DeviceAuthURL: "", // not used + TokenURL: fmt.Sprintf("https://%s/login/oauth/access_token", *validFilledOutIDP.Spec.GitHubAPI.Host), + AuthStyle: oauth2.AuthStyleInParams, + }, + RedirectURL: "", // not used + Scopes: []string{"read:user", "read:org"}, + }, + AllowedOrganizations: setutil.NewCaseInsensitiveSet("organization1", "org2"), + HttpClient: phttp.Default(goodServerCertPool), + }, + }, + wantValidatedCache: []GitHubValidatedAPICacheKey{ + { + address: goodServerDomain, + caBundlePEMSHA256: sha256.Sum256(goodServerCA), + }, + }, + wantResultingUpstreams: []idpv1alpha1.GitHubIdentityProvider{ + { + ObjectMeta: validFilledOutIDP.ObjectMeta, + Spec: validFilledOutIDP.Spec, + Status: idpv1alpha1.GitHubIdentityProviderStatus{ + Phase: idpv1alpha1.GitHubPhaseReady, + Conditions: []metav1.Condition{ + buildClaimsValidatedTrue(t), + buildClientCredentialsSecretValidTrue(t, validFilledOutIDP.Spec.Client.SecretName), + buildGitHubConnectionValidTrue(t, *validFilledOutIDP.Spec.GitHubAPI.Host), + buildHostValidTrue(t, *validFilledOutIDP.Spec.GitHubAPI.Host), + buildOrganizationsPolicyValidTrue(t, *validFilledOutIDP.Spec.AllowAuthentication.Organizations.Policy), + buildTLSConfigurationValidTrue(t), + }, + }, + }, + }, + wantLogs: []string{ + buildLogForUpdatingClientCredentialsSecretValid("some-idp-name", "True", "Success", fmt.Sprintf(`clientID and clientSecret have been read from spec.client.SecretName (\"%s\")`, validFilledOutIDP.Spec.Client.SecretName)), + buildLogForUpdatingClaimsValidTrue("some-idp-name"), + buildLogForUpdatingOrganizationPolicyValid("some-idp-name", "True", "Success", fmt.Sprintf(`spec.allowAuthentication.organizations.policy (\"%s\") is valid`, string(*validFilledOutIDP.Spec.AllowAuthentication.Organizations.Policy))), + buildLogForUpdatingHostValid("some-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is valid`, *validFilledOutIDP.Spec.GitHubAPI.Host), + buildLogForUpdatingTLSConfigurationValid("some-idp-name", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), + buildLogForUpdatingGitHubConnectionValid("some-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is reachable and TLS verification succeeds`, *validFilledOutIDP.Spec.GitHubAPI.Host), + buildLogForUpdatingPhase("some-idp-name", "Ready"), + }, + }, { name: "Host error - missing spec.githubAPI.host", secretsAndConfigMaps: []runtime.Object{goodClientCredentialsSecret}, @@ -2015,8 +2135,8 @@ func TestController(t *testing.T) { fakeKubeClient := kubernetesfake.NewSimpleClientset(tt.secretsAndConfigMaps...) kubeInformers := k8sinformers.NewSharedInformerFactoryWithOptions(fakeKubeClient, 0) - cache := dynamicupstreamprovider.NewDynamicUpstreamIDPProvider() - cache.SetGitHubIdentityProviders([]upstreamprovider.UpstreamGithubIdentityProviderI{ + idpCache := dynamicupstreamprovider.NewDynamicUpstreamIDPProvider() + idpCache.SetGitHubIdentityProviders([]upstreamprovider.UpstreamGithubIdentityProviderI{ upstreamgithub.New( upstreamgithub.ProviderConfig{Name: "initial-entry-to-remove"}, ), @@ -2027,14 +2147,19 @@ func TestController(t *testing.T) { gitHubIdentityProviderInformer := supervisorInformers.IDP().V1alpha1().GitHubIdentityProviders() - dialer := tt.mockDialer - if dialer == nil { - dialer = tls.Dial + dialer := tls.Dial + if tt.mockDialer != nil { + dialer = tt.mockDialer(t) + } + + validatedCache := cache.NewExpiring() + for _, item := range tt.preexistingValidatedCache { + validatedCache.Set(item, nil, 1*time.Hour) } controller := New( namespace, - cache, + idpCache, fakeSupervisorClient, gitHubIdentityProviderInformer, kubeInformers.Core().V1().Secrets(), @@ -2043,6 +2168,7 @@ func TestController(t *testing.T) { controllerlib.WithInformer, frozenClockForLastTransitionTime, dialer, + validatedCache, ) ctx, cancel := context.WithCancel(context.Background()) @@ -2061,8 +2187,8 @@ func TestController(t *testing.T) { require.NoError(t, err) } - // Verify what's in the cache - actualIDPList := cache.GetGitHubIdentityProviders() + // Verify what's in the IDP cache + actualIDPList := idpCache.GetGitHubIdentityProviders() require.Equal(t, len(tt.wantResultingCache), len(actualIDPList)) for i := range len(tt.wantResultingCache) { // Do not expect any particular order in the cache @@ -2088,6 +2214,19 @@ func TestController(t *testing.T) { require.Contains(t, tt.wantResultingCache[i].APIBaseURL, actualProvider.GetConfig().APIBaseURL) } + // Verify what's in the validated cache + var uniqueAddresses []string + for _, cachedIDP := range tt.wantResultingCache { + if !slices.Contains(uniqueAddresses, cachedIDP.APIBaseURL) { + uniqueAddresses = append(uniqueAddresses, cachedIDP.APIBaseURL) + } + } + require.Equal(t, len(uniqueAddresses), len(tt.wantValidatedCache), "every unique IDP address should have an entry in the validated cache") + for _, item := range tt.wantValidatedCache { + _, ok := validatedCache.Get(item) + require.True(t, ok, "item with address %q must be found in the validated cache", item.address) + } + // Verify the status conditions as reported in Kubernetes allGitHubIDPs, err := fakeSupervisorClient.IDPV1alpha1().GitHubIdentityProviders(namespace).List(ctx, metav1.ListOptions{}) require.NoError(t, err) @@ -2412,6 +2551,7 @@ func TestController_OnlyWantActions(t *testing.T) { controllerlib.WithInformer, frozenClockForLastTransitionTime, tls.Dial, + cache.NewExpiring(), ) ctx, cancel := context.WithCancel(context.Background()) @@ -2535,6 +2675,7 @@ func TestGitHubUpstreamWatcherControllerFilterSecret(t *testing.T) { observableInformers.WithInformer, clock.RealClock{}, tls.Dial, + cache.NewExpiring(), ) unrelated := &corev1.Secret{} @@ -2591,6 +2732,7 @@ func TestGitHubUpstreamWatcherControllerFilterConfigMaps(t *testing.T) { observableInformers.WithInformer, clock.RealClock{}, tls.Dial, + cache.NewExpiring(), ) unrelated := &corev1.ConfigMap{} @@ -2647,6 +2789,7 @@ func TestGitHubUpstreamWatcherControllerFilterGitHubIDP(t *testing.T) { observableInformers.WithInformer, clock.RealClock{}, tls.Dial, + cache.NewExpiring(), ) unrelated := &idpv1alpha1.GitHubIdentityProvider{} diff --git a/internal/supervisor/server/server.go b/internal/supervisor/server/server.go index 41fa312ae..15c75e378 100644 --- a/internal/supervisor/server/server.go +++ b/internal/supervisor/server/server.go @@ -25,6 +25,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/serializer" + "k8s.io/apimachinery/pkg/util/cache" apimachineryversion "k8s.io/apimachinery/pkg/version" genericapifilters "k8s.io/apiserver/pkg/endpoints/filters" openapinamer "k8s.io/apiserver/pkg/endpoints/openapi" @@ -341,6 +342,7 @@ func prepareControllers( controllerlib.WithInformer, clock.RealClock{}, tls.Dial, + cache.NewExpiring(), ), singletonWorker). WithController( From 756966c55bdec7bc10bcc21fd167fd46db8de113 Mon Sep 17 00:00:00 2001 From: Ryan Richard Date: Tue, 23 Jul 2024 08:41:31 -0700 Subject: [PATCH 27/99] add "Status" printer column to JWTAuthenticator and WebhookAuthenticator --- .../authentication/v1alpha1/types_jwtauthenticator.go.tmpl | 1 + .../v1alpha1/types_webhookauthenticator.go.tmpl | 1 + ...thentication.concierge.pinniped.dev_jwtauthenticators.yaml | 3 +++ ...tication.concierge.pinniped.dev_webhookauthenticators.yaml | 3 +++ .../authentication/v1alpha1/types_jwtauthenticator.go | 1 + .../authentication/v1alpha1/types_webhookauthenticator.go | 1 + ...thentication.concierge.pinniped.dev_jwtauthenticators.yaml | 3 +++ ...tication.concierge.pinniped.dev_webhookauthenticators.yaml | 3 +++ .../authentication/v1alpha1/types_jwtauthenticator.go | 1 + .../authentication/v1alpha1/types_webhookauthenticator.go | 1 + ...thentication.concierge.pinniped.dev_jwtauthenticators.yaml | 3 +++ ...tication.concierge.pinniped.dev_webhookauthenticators.yaml | 3 +++ .../authentication/v1alpha1/types_jwtauthenticator.go | 1 + .../authentication/v1alpha1/types_webhookauthenticator.go | 1 + ...thentication.concierge.pinniped.dev_jwtauthenticators.yaml | 3 +++ ...tication.concierge.pinniped.dev_webhookauthenticators.yaml | 3 +++ .../authentication/v1alpha1/types_jwtauthenticator.go | 1 + .../authentication/v1alpha1/types_webhookauthenticator.go | 1 + ...thentication.concierge.pinniped.dev_jwtauthenticators.yaml | 3 +++ ...tication.concierge.pinniped.dev_webhookauthenticators.yaml | 3 +++ .../authentication/v1alpha1/types_jwtauthenticator.go | 1 + .../authentication/v1alpha1/types_webhookauthenticator.go | 1 + ...thentication.concierge.pinniped.dev_jwtauthenticators.yaml | 3 +++ ...tication.concierge.pinniped.dev_webhookauthenticators.yaml | 3 +++ .../authentication/v1alpha1/types_jwtauthenticator.go | 1 + .../authentication/v1alpha1/types_webhookauthenticator.go | 1 + ...thentication.concierge.pinniped.dev_jwtauthenticators.yaml | 3 +++ ...tication.concierge.pinniped.dev_webhookauthenticators.yaml | 3 +++ .../authentication/v1alpha1/types_jwtauthenticator.go | 1 + .../authentication/v1alpha1/types_webhookauthenticator.go | 1 + ...thentication.concierge.pinniped.dev_jwtauthenticators.yaml | 3 +++ ...tication.concierge.pinniped.dev_webhookauthenticators.yaml | 3 +++ .../authentication/v1alpha1/types_jwtauthenticator.go | 1 + .../authentication/v1alpha1/types_webhookauthenticator.go | 1 + test/integration/kube_api_discovery_test.go | 4 ++-- 35 files changed, 68 insertions(+), 2 deletions(-) diff --git a/apis/concierge/authentication/v1alpha1/types_jwtauthenticator.go.tmpl b/apis/concierge/authentication/v1alpha1/types_jwtauthenticator.go.tmpl index f75d50776..eddef774a 100644 --- a/apis/concierge/authentication/v1alpha1/types_jwtauthenticator.go.tmpl +++ b/apis/concierge/authentication/v1alpha1/types_jwtauthenticator.go.tmpl @@ -79,6 +79,7 @@ type JWTTokenClaims struct { // +kubebuilder:resource:categories=pinniped;pinniped-authenticator;pinniped-authenticators,scope=Cluster // +kubebuilder:printcolumn:name="Issuer",type=string,JSONPath=`.spec.issuer` // +kubebuilder:printcolumn:name="Audience",type=string,JSONPath=`.spec.audience` +// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.phase` // +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` // +kubebuilder:subresource:status type JWTAuthenticator struct { diff --git a/apis/concierge/authentication/v1alpha1/types_webhookauthenticator.go.tmpl b/apis/concierge/authentication/v1alpha1/types_webhookauthenticator.go.tmpl index cbe3eeeb0..f05902cb2 100644 --- a/apis/concierge/authentication/v1alpha1/types_webhookauthenticator.go.tmpl +++ b/apis/concierge/authentication/v1alpha1/types_webhookauthenticator.go.tmpl @@ -50,6 +50,7 @@ type WebhookAuthenticatorSpec struct { // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // +kubebuilder:resource:categories=pinniped;pinniped-authenticator;pinniped-authenticators,scope=Cluster // +kubebuilder:printcolumn:name="Endpoint",type=string,JSONPath=`.spec.endpoint` +// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.phase` // +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` // +kubebuilder:subresource:status type WebhookAuthenticator struct { diff --git a/deploy/concierge/authentication.concierge.pinniped.dev_jwtauthenticators.yaml b/deploy/concierge/authentication.concierge.pinniped.dev_jwtauthenticators.yaml index 1e503bda2..5b85a8bd3 100644 --- a/deploy/concierge/authentication.concierge.pinniped.dev_jwtauthenticators.yaml +++ b/deploy/concierge/authentication.concierge.pinniped.dev_jwtauthenticators.yaml @@ -25,6 +25,9 @@ spec: - jsonPath: .spec.audience name: Audience type: string + - jsonPath: .status.phase + name: Status + type: string - jsonPath: .metadata.creationTimestamp name: Age type: date diff --git a/deploy/concierge/authentication.concierge.pinniped.dev_webhookauthenticators.yaml b/deploy/concierge/authentication.concierge.pinniped.dev_webhookauthenticators.yaml index b3b024977..c2dce0c76 100644 --- a/deploy/concierge/authentication.concierge.pinniped.dev_webhookauthenticators.yaml +++ b/deploy/concierge/authentication.concierge.pinniped.dev_webhookauthenticators.yaml @@ -22,6 +22,9 @@ spec: - jsonPath: .spec.endpoint name: Endpoint type: string + - jsonPath: .status.phase + name: Status + type: string - jsonPath: .metadata.creationTimestamp name: Age type: date diff --git a/generated/1.24/apis/concierge/authentication/v1alpha1/types_jwtauthenticator.go b/generated/1.24/apis/concierge/authentication/v1alpha1/types_jwtauthenticator.go index f75d50776..eddef774a 100644 --- a/generated/1.24/apis/concierge/authentication/v1alpha1/types_jwtauthenticator.go +++ b/generated/1.24/apis/concierge/authentication/v1alpha1/types_jwtauthenticator.go @@ -79,6 +79,7 @@ type JWTTokenClaims struct { // +kubebuilder:resource:categories=pinniped;pinniped-authenticator;pinniped-authenticators,scope=Cluster // +kubebuilder:printcolumn:name="Issuer",type=string,JSONPath=`.spec.issuer` // +kubebuilder:printcolumn:name="Audience",type=string,JSONPath=`.spec.audience` +// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.phase` // +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` // +kubebuilder:subresource:status type JWTAuthenticator struct { diff --git a/generated/1.24/apis/concierge/authentication/v1alpha1/types_webhookauthenticator.go b/generated/1.24/apis/concierge/authentication/v1alpha1/types_webhookauthenticator.go index cbe3eeeb0..f05902cb2 100644 --- a/generated/1.24/apis/concierge/authentication/v1alpha1/types_webhookauthenticator.go +++ b/generated/1.24/apis/concierge/authentication/v1alpha1/types_webhookauthenticator.go @@ -50,6 +50,7 @@ type WebhookAuthenticatorSpec struct { // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // +kubebuilder:resource:categories=pinniped;pinniped-authenticator;pinniped-authenticators,scope=Cluster // +kubebuilder:printcolumn:name="Endpoint",type=string,JSONPath=`.spec.endpoint` +// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.phase` // +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` // +kubebuilder:subresource:status type WebhookAuthenticator struct { diff --git a/generated/1.24/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml b/generated/1.24/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml index 1e503bda2..5b85a8bd3 100644 --- a/generated/1.24/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml +++ b/generated/1.24/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml @@ -25,6 +25,9 @@ spec: - jsonPath: .spec.audience name: Audience type: string + - jsonPath: .status.phase + name: Status + type: string - jsonPath: .metadata.creationTimestamp name: Age type: date diff --git a/generated/1.24/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml b/generated/1.24/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml index b3b024977..c2dce0c76 100644 --- a/generated/1.24/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml +++ b/generated/1.24/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml @@ -22,6 +22,9 @@ spec: - jsonPath: .spec.endpoint name: Endpoint type: string + - jsonPath: .status.phase + name: Status + type: string - jsonPath: .metadata.creationTimestamp name: Age type: date diff --git a/generated/1.25/apis/concierge/authentication/v1alpha1/types_jwtauthenticator.go b/generated/1.25/apis/concierge/authentication/v1alpha1/types_jwtauthenticator.go index f75d50776..eddef774a 100644 --- a/generated/1.25/apis/concierge/authentication/v1alpha1/types_jwtauthenticator.go +++ b/generated/1.25/apis/concierge/authentication/v1alpha1/types_jwtauthenticator.go @@ -79,6 +79,7 @@ type JWTTokenClaims struct { // +kubebuilder:resource:categories=pinniped;pinniped-authenticator;pinniped-authenticators,scope=Cluster // +kubebuilder:printcolumn:name="Issuer",type=string,JSONPath=`.spec.issuer` // +kubebuilder:printcolumn:name="Audience",type=string,JSONPath=`.spec.audience` +// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.phase` // +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` // +kubebuilder:subresource:status type JWTAuthenticator struct { diff --git a/generated/1.25/apis/concierge/authentication/v1alpha1/types_webhookauthenticator.go b/generated/1.25/apis/concierge/authentication/v1alpha1/types_webhookauthenticator.go index cbe3eeeb0..f05902cb2 100644 --- a/generated/1.25/apis/concierge/authentication/v1alpha1/types_webhookauthenticator.go +++ b/generated/1.25/apis/concierge/authentication/v1alpha1/types_webhookauthenticator.go @@ -50,6 +50,7 @@ type WebhookAuthenticatorSpec struct { // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // +kubebuilder:resource:categories=pinniped;pinniped-authenticator;pinniped-authenticators,scope=Cluster // +kubebuilder:printcolumn:name="Endpoint",type=string,JSONPath=`.spec.endpoint` +// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.phase` // +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` // +kubebuilder:subresource:status type WebhookAuthenticator struct { diff --git a/generated/1.25/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml b/generated/1.25/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml index 1e503bda2..5b85a8bd3 100644 --- a/generated/1.25/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml +++ b/generated/1.25/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml @@ -25,6 +25,9 @@ spec: - jsonPath: .spec.audience name: Audience type: string + - jsonPath: .status.phase + name: Status + type: string - jsonPath: .metadata.creationTimestamp name: Age type: date diff --git a/generated/1.25/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml b/generated/1.25/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml index b3b024977..c2dce0c76 100644 --- a/generated/1.25/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml +++ b/generated/1.25/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml @@ -22,6 +22,9 @@ spec: - jsonPath: .spec.endpoint name: Endpoint type: string + - jsonPath: .status.phase + name: Status + type: string - jsonPath: .metadata.creationTimestamp name: Age type: date diff --git a/generated/1.26/apis/concierge/authentication/v1alpha1/types_jwtauthenticator.go b/generated/1.26/apis/concierge/authentication/v1alpha1/types_jwtauthenticator.go index f75d50776..eddef774a 100644 --- a/generated/1.26/apis/concierge/authentication/v1alpha1/types_jwtauthenticator.go +++ b/generated/1.26/apis/concierge/authentication/v1alpha1/types_jwtauthenticator.go @@ -79,6 +79,7 @@ type JWTTokenClaims struct { // +kubebuilder:resource:categories=pinniped;pinniped-authenticator;pinniped-authenticators,scope=Cluster // +kubebuilder:printcolumn:name="Issuer",type=string,JSONPath=`.spec.issuer` // +kubebuilder:printcolumn:name="Audience",type=string,JSONPath=`.spec.audience` +// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.phase` // +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` // +kubebuilder:subresource:status type JWTAuthenticator struct { diff --git a/generated/1.26/apis/concierge/authentication/v1alpha1/types_webhookauthenticator.go b/generated/1.26/apis/concierge/authentication/v1alpha1/types_webhookauthenticator.go index cbe3eeeb0..f05902cb2 100644 --- a/generated/1.26/apis/concierge/authentication/v1alpha1/types_webhookauthenticator.go +++ b/generated/1.26/apis/concierge/authentication/v1alpha1/types_webhookauthenticator.go @@ -50,6 +50,7 @@ type WebhookAuthenticatorSpec struct { // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // +kubebuilder:resource:categories=pinniped;pinniped-authenticator;pinniped-authenticators,scope=Cluster // +kubebuilder:printcolumn:name="Endpoint",type=string,JSONPath=`.spec.endpoint` +// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.phase` // +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` // +kubebuilder:subresource:status type WebhookAuthenticator struct { diff --git a/generated/1.26/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml b/generated/1.26/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml index 1e503bda2..5b85a8bd3 100644 --- a/generated/1.26/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml +++ b/generated/1.26/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml @@ -25,6 +25,9 @@ spec: - jsonPath: .spec.audience name: Audience type: string + - jsonPath: .status.phase + name: Status + type: string - jsonPath: .metadata.creationTimestamp name: Age type: date diff --git a/generated/1.26/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml b/generated/1.26/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml index b3b024977..c2dce0c76 100644 --- a/generated/1.26/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml +++ b/generated/1.26/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml @@ -22,6 +22,9 @@ spec: - jsonPath: .spec.endpoint name: Endpoint type: string + - jsonPath: .status.phase + name: Status + type: string - jsonPath: .metadata.creationTimestamp name: Age type: date diff --git a/generated/1.27/apis/concierge/authentication/v1alpha1/types_jwtauthenticator.go b/generated/1.27/apis/concierge/authentication/v1alpha1/types_jwtauthenticator.go index f75d50776..eddef774a 100644 --- a/generated/1.27/apis/concierge/authentication/v1alpha1/types_jwtauthenticator.go +++ b/generated/1.27/apis/concierge/authentication/v1alpha1/types_jwtauthenticator.go @@ -79,6 +79,7 @@ type JWTTokenClaims struct { // +kubebuilder:resource:categories=pinniped;pinniped-authenticator;pinniped-authenticators,scope=Cluster // +kubebuilder:printcolumn:name="Issuer",type=string,JSONPath=`.spec.issuer` // +kubebuilder:printcolumn:name="Audience",type=string,JSONPath=`.spec.audience` +// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.phase` // +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` // +kubebuilder:subresource:status type JWTAuthenticator struct { diff --git a/generated/1.27/apis/concierge/authentication/v1alpha1/types_webhookauthenticator.go b/generated/1.27/apis/concierge/authentication/v1alpha1/types_webhookauthenticator.go index cbe3eeeb0..f05902cb2 100644 --- a/generated/1.27/apis/concierge/authentication/v1alpha1/types_webhookauthenticator.go +++ b/generated/1.27/apis/concierge/authentication/v1alpha1/types_webhookauthenticator.go @@ -50,6 +50,7 @@ type WebhookAuthenticatorSpec struct { // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // +kubebuilder:resource:categories=pinniped;pinniped-authenticator;pinniped-authenticators,scope=Cluster // +kubebuilder:printcolumn:name="Endpoint",type=string,JSONPath=`.spec.endpoint` +// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.phase` // +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` // +kubebuilder:subresource:status type WebhookAuthenticator struct { diff --git a/generated/1.27/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml b/generated/1.27/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml index 1e503bda2..5b85a8bd3 100644 --- a/generated/1.27/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml +++ b/generated/1.27/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml @@ -25,6 +25,9 @@ spec: - jsonPath: .spec.audience name: Audience type: string + - jsonPath: .status.phase + name: Status + type: string - jsonPath: .metadata.creationTimestamp name: Age type: date diff --git a/generated/1.27/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml b/generated/1.27/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml index b3b024977..c2dce0c76 100644 --- a/generated/1.27/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml +++ b/generated/1.27/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml @@ -22,6 +22,9 @@ spec: - jsonPath: .spec.endpoint name: Endpoint type: string + - jsonPath: .status.phase + name: Status + type: string - jsonPath: .metadata.creationTimestamp name: Age type: date diff --git a/generated/1.28/apis/concierge/authentication/v1alpha1/types_jwtauthenticator.go b/generated/1.28/apis/concierge/authentication/v1alpha1/types_jwtauthenticator.go index f75d50776..eddef774a 100644 --- a/generated/1.28/apis/concierge/authentication/v1alpha1/types_jwtauthenticator.go +++ b/generated/1.28/apis/concierge/authentication/v1alpha1/types_jwtauthenticator.go @@ -79,6 +79,7 @@ type JWTTokenClaims struct { // +kubebuilder:resource:categories=pinniped;pinniped-authenticator;pinniped-authenticators,scope=Cluster // +kubebuilder:printcolumn:name="Issuer",type=string,JSONPath=`.spec.issuer` // +kubebuilder:printcolumn:name="Audience",type=string,JSONPath=`.spec.audience` +// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.phase` // +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` // +kubebuilder:subresource:status type JWTAuthenticator struct { diff --git a/generated/1.28/apis/concierge/authentication/v1alpha1/types_webhookauthenticator.go b/generated/1.28/apis/concierge/authentication/v1alpha1/types_webhookauthenticator.go index cbe3eeeb0..f05902cb2 100644 --- a/generated/1.28/apis/concierge/authentication/v1alpha1/types_webhookauthenticator.go +++ b/generated/1.28/apis/concierge/authentication/v1alpha1/types_webhookauthenticator.go @@ -50,6 +50,7 @@ type WebhookAuthenticatorSpec struct { // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // +kubebuilder:resource:categories=pinniped;pinniped-authenticator;pinniped-authenticators,scope=Cluster // +kubebuilder:printcolumn:name="Endpoint",type=string,JSONPath=`.spec.endpoint` +// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.phase` // +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` // +kubebuilder:subresource:status type WebhookAuthenticator struct { diff --git a/generated/1.28/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml b/generated/1.28/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml index 1e503bda2..5b85a8bd3 100644 --- a/generated/1.28/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml +++ b/generated/1.28/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml @@ -25,6 +25,9 @@ spec: - jsonPath: .spec.audience name: Audience type: string + - jsonPath: .status.phase + name: Status + type: string - jsonPath: .metadata.creationTimestamp name: Age type: date diff --git a/generated/1.28/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml b/generated/1.28/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml index b3b024977..c2dce0c76 100644 --- a/generated/1.28/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml +++ b/generated/1.28/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml @@ -22,6 +22,9 @@ spec: - jsonPath: .spec.endpoint name: Endpoint type: string + - jsonPath: .status.phase + name: Status + type: string - jsonPath: .metadata.creationTimestamp name: Age type: date diff --git a/generated/1.29/apis/concierge/authentication/v1alpha1/types_jwtauthenticator.go b/generated/1.29/apis/concierge/authentication/v1alpha1/types_jwtauthenticator.go index f75d50776..eddef774a 100644 --- a/generated/1.29/apis/concierge/authentication/v1alpha1/types_jwtauthenticator.go +++ b/generated/1.29/apis/concierge/authentication/v1alpha1/types_jwtauthenticator.go @@ -79,6 +79,7 @@ type JWTTokenClaims struct { // +kubebuilder:resource:categories=pinniped;pinniped-authenticator;pinniped-authenticators,scope=Cluster // +kubebuilder:printcolumn:name="Issuer",type=string,JSONPath=`.spec.issuer` // +kubebuilder:printcolumn:name="Audience",type=string,JSONPath=`.spec.audience` +// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.phase` // +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` // +kubebuilder:subresource:status type JWTAuthenticator struct { diff --git a/generated/1.29/apis/concierge/authentication/v1alpha1/types_webhookauthenticator.go b/generated/1.29/apis/concierge/authentication/v1alpha1/types_webhookauthenticator.go index cbe3eeeb0..f05902cb2 100644 --- a/generated/1.29/apis/concierge/authentication/v1alpha1/types_webhookauthenticator.go +++ b/generated/1.29/apis/concierge/authentication/v1alpha1/types_webhookauthenticator.go @@ -50,6 +50,7 @@ type WebhookAuthenticatorSpec struct { // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // +kubebuilder:resource:categories=pinniped;pinniped-authenticator;pinniped-authenticators,scope=Cluster // +kubebuilder:printcolumn:name="Endpoint",type=string,JSONPath=`.spec.endpoint` +// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.phase` // +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` // +kubebuilder:subresource:status type WebhookAuthenticator struct { diff --git a/generated/1.29/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml b/generated/1.29/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml index 1e503bda2..5b85a8bd3 100644 --- a/generated/1.29/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml +++ b/generated/1.29/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml @@ -25,6 +25,9 @@ spec: - jsonPath: .spec.audience name: Audience type: string + - jsonPath: .status.phase + name: Status + type: string - jsonPath: .metadata.creationTimestamp name: Age type: date diff --git a/generated/1.29/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml b/generated/1.29/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml index b3b024977..c2dce0c76 100644 --- a/generated/1.29/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml +++ b/generated/1.29/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml @@ -22,6 +22,9 @@ spec: - jsonPath: .spec.endpoint name: Endpoint type: string + - jsonPath: .status.phase + name: Status + type: string - jsonPath: .metadata.creationTimestamp name: Age type: date diff --git a/generated/1.30/apis/concierge/authentication/v1alpha1/types_jwtauthenticator.go b/generated/1.30/apis/concierge/authentication/v1alpha1/types_jwtauthenticator.go index f75d50776..eddef774a 100644 --- a/generated/1.30/apis/concierge/authentication/v1alpha1/types_jwtauthenticator.go +++ b/generated/1.30/apis/concierge/authentication/v1alpha1/types_jwtauthenticator.go @@ -79,6 +79,7 @@ type JWTTokenClaims struct { // +kubebuilder:resource:categories=pinniped;pinniped-authenticator;pinniped-authenticators,scope=Cluster // +kubebuilder:printcolumn:name="Issuer",type=string,JSONPath=`.spec.issuer` // +kubebuilder:printcolumn:name="Audience",type=string,JSONPath=`.spec.audience` +// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.phase` // +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` // +kubebuilder:subresource:status type JWTAuthenticator struct { diff --git a/generated/1.30/apis/concierge/authentication/v1alpha1/types_webhookauthenticator.go b/generated/1.30/apis/concierge/authentication/v1alpha1/types_webhookauthenticator.go index cbe3eeeb0..f05902cb2 100644 --- a/generated/1.30/apis/concierge/authentication/v1alpha1/types_webhookauthenticator.go +++ b/generated/1.30/apis/concierge/authentication/v1alpha1/types_webhookauthenticator.go @@ -50,6 +50,7 @@ type WebhookAuthenticatorSpec struct { // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // +kubebuilder:resource:categories=pinniped;pinniped-authenticator;pinniped-authenticators,scope=Cluster // +kubebuilder:printcolumn:name="Endpoint",type=string,JSONPath=`.spec.endpoint` +// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.phase` // +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` // +kubebuilder:subresource:status type WebhookAuthenticator struct { diff --git a/generated/1.30/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml b/generated/1.30/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml index 1e503bda2..5b85a8bd3 100644 --- a/generated/1.30/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml +++ b/generated/1.30/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml @@ -25,6 +25,9 @@ spec: - jsonPath: .spec.audience name: Audience type: string + - jsonPath: .status.phase + name: Status + type: string - jsonPath: .metadata.creationTimestamp name: Age type: date diff --git a/generated/1.30/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml b/generated/1.30/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml index b3b024977..c2dce0c76 100644 --- a/generated/1.30/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml +++ b/generated/1.30/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml @@ -22,6 +22,9 @@ spec: - jsonPath: .spec.endpoint name: Endpoint type: string + - jsonPath: .status.phase + name: Status + type: string - jsonPath: .metadata.creationTimestamp name: Age type: date diff --git a/generated/latest/apis/concierge/authentication/v1alpha1/types_jwtauthenticator.go b/generated/latest/apis/concierge/authentication/v1alpha1/types_jwtauthenticator.go index f75d50776..eddef774a 100644 --- a/generated/latest/apis/concierge/authentication/v1alpha1/types_jwtauthenticator.go +++ b/generated/latest/apis/concierge/authentication/v1alpha1/types_jwtauthenticator.go @@ -79,6 +79,7 @@ type JWTTokenClaims struct { // +kubebuilder:resource:categories=pinniped;pinniped-authenticator;pinniped-authenticators,scope=Cluster // +kubebuilder:printcolumn:name="Issuer",type=string,JSONPath=`.spec.issuer` // +kubebuilder:printcolumn:name="Audience",type=string,JSONPath=`.spec.audience` +// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.phase` // +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` // +kubebuilder:subresource:status type JWTAuthenticator struct { diff --git a/generated/latest/apis/concierge/authentication/v1alpha1/types_webhookauthenticator.go b/generated/latest/apis/concierge/authentication/v1alpha1/types_webhookauthenticator.go index cbe3eeeb0..f05902cb2 100644 --- a/generated/latest/apis/concierge/authentication/v1alpha1/types_webhookauthenticator.go +++ b/generated/latest/apis/concierge/authentication/v1alpha1/types_webhookauthenticator.go @@ -50,6 +50,7 @@ type WebhookAuthenticatorSpec struct { // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // +kubebuilder:resource:categories=pinniped;pinniped-authenticator;pinniped-authenticators,scope=Cluster // +kubebuilder:printcolumn:name="Endpoint",type=string,JSONPath=`.spec.endpoint` +// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.phase` // +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` // +kubebuilder:subresource:status type WebhookAuthenticator struct { diff --git a/test/integration/kube_api_discovery_test.go b/test/integration/kube_api_discovery_test.go index e01d0a63d..a166e552d 100644 --- a/test/integration/kube_api_discovery_test.go +++ b/test/integration/kube_api_discovery_test.go @@ -552,7 +552,7 @@ func TestCRDAdditionalPrinterColumns_Parallel(t *testing.T) { addSuffix("webhookauthenticators.authentication.concierge"): { "v1alpha1": []apiextensionsv1.CustomResourceColumnDefinition{ {Name: "Endpoint", Type: "string", JSONPath: ".spec.endpoint"}, - // Note that WebhookAuthenticators have a status type, but no controller currently sets the status, so we don't show it. + {Name: "Status", Type: "string", JSONPath: ".status.phase"}, {Name: "Age", Type: "date", JSONPath: ".metadata.creationTimestamp"}, }, }, @@ -560,7 +560,7 @@ func TestCRDAdditionalPrinterColumns_Parallel(t *testing.T) { "v1alpha1": []apiextensionsv1.CustomResourceColumnDefinition{ {Name: "Issuer", Type: "string", JSONPath: ".spec.issuer"}, {Name: "Audience", Type: "string", JSONPath: ".spec.audience"}, - // Note that JWTAuthenticators have a status type, but no controller currently sets the status, so we don't show it. + {Name: "Status", Type: "string", JSONPath: ".status.phase"}, {Name: "Age", Type: "date", JSONPath: ".metadata.creationTimestamp"}, }, }, From 30c0fd479e1be1212d7ab27d4615024c1c192bd1 Mon Sep 17 00:00:00 2001 From: Ryan Richard Date: Tue, 23 Jul 2024 09:51:11 -0700 Subject: [PATCH 28/99] Fix e2e_test.go Co-authored-by: Joshua Casey --- test/integration/e2e_test.go | 37 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/test/integration/e2e_test.go b/test/integration/e2e_test.go index 1a51c3a3e..862a1665b 100644 --- a/test/integration/e2e_test.go +++ b/test/integration/e2e_test.go @@ -146,7 +146,7 @@ func TestE2EFullIntegration_Browser(t *testing.T) { Resource: "namespaces", }) - authenticator := testlib.CreateTestJWTAuthenticator(topSetupCtx, t, defaultJWTAuthenticatorSpec, authenticationv1alpha1.JWTAuthenticatorPhaseError) + authenticator := testlib.CreateTestJWTAuthenticator(testCtx, t, defaultJWTAuthenticatorSpec, authenticationv1alpha1.JWTAuthenticatorPhaseError) // Create upstream OIDC provider and wait for it to become ready. createdProvider := testlib.CreateTestOIDCIdentityProvider(t, idpv1alpha1.OIDCIdentityProviderSpec{ Issuer: env.SupervisorUpstreamOIDC.Issuer, @@ -247,7 +247,7 @@ func TestE2EFullIntegration_Browser(t *testing.T) { Name: caSecret.Name, Key: "ca.crt", } - authenticator := testlib.CreateTestJWTAuthenticator(topSetupCtx, t, *jwtAuthnSpec, authenticationv1alpha1.JWTAuthenticatorPhaseError) + authenticator := testlib.CreateTestJWTAuthenticator(testCtx, t, *jwtAuthnSpec, authenticationv1alpha1.JWTAuthenticatorPhaseError) // Create upstream OIDC provider and wait for it to become ready. createdProvider := testlib.CreateTestOIDCIdentityProvider(t, idpv1alpha1.OIDCIdentityProviderSpec{ Issuer: env.SupervisorUpstreamOIDC.Issuer, @@ -350,7 +350,7 @@ func TestE2EFullIntegration_Browser(t *testing.T) { Key: "ca.crt", } - authenticator := testlib.CreateTestJWTAuthenticator(topSetupCtx, t, *jwtAuthnSpec, authenticationv1alpha1.JWTAuthenticatorPhaseError) + authenticator := testlib.CreateTestJWTAuthenticator(testCtx, t, *jwtAuthnSpec, authenticationv1alpha1.JWTAuthenticatorPhaseError) t.Logf("authenticator: %s/%s; concierge ns: %s", authenticator.Namespace, authenticator.Name, env.ConciergeNamespace) // Create upstream OIDC provider and wait for it to become ready. createdProvider := testlib.CreateTestOIDCIdentityProvider(t, idpv1alpha1.OIDCIdentityProviderSpec{ @@ -489,7 +489,7 @@ func TestE2EFullIntegration_Browser(t *testing.T) { Key: "ca.crt", } - authenticator := testlib.CreateTestJWTAuthenticator(topSetupCtx, t, defaultJWTAuthenticatorSpec, authenticationv1alpha1.JWTAuthenticatorPhaseError) + authenticator := testlib.CreateTestJWTAuthenticator(testCtx, t, defaultJWTAuthenticatorSpec, authenticationv1alpha1.JWTAuthenticatorPhaseError) // Create upstream OIDC provider and wait for it to become ready. createdProvider := testlib.CreateTestOIDCIdentityProvider(t, idpv1alpha1.OIDCIdentityProviderSpec{ Issuer: env.SupervisorUpstreamOIDC.Issuer, @@ -620,7 +620,7 @@ func TestE2EFullIntegration_Browser(t *testing.T) { Resource: "namespaces", }) - authenticator := testlib.CreateTestJWTAuthenticator(topSetupCtx, t, defaultJWTAuthenticatorSpec, authenticationv1alpha1.JWTAuthenticatorPhaseError) + authenticator := testlib.CreateTestJWTAuthenticator(testCtx, t, defaultJWTAuthenticatorSpec, authenticationv1alpha1.JWTAuthenticatorPhaseError) // Create upstream OIDC provider and wait for it to become ready. createdProvider := testlib.CreateTestOIDCIdentityProvider(t, idpv1alpha1.OIDCIdentityProviderSpec{ Issuer: env.SupervisorUpstreamOIDC.Issuer, @@ -694,7 +694,7 @@ func TestE2EFullIntegration_Browser(t *testing.T) { tempDir := t.TempDir() // per-test tmp dir to avoid sharing files between tests - authenticator := testlib.CreateTestJWTAuthenticator(topSetupCtx, t, defaultJWTAuthenticatorSpec, authenticationv1alpha1.JWTAuthenticatorPhaseError) + authenticator := testlib.CreateTestJWTAuthenticator(testCtx, t, defaultJWTAuthenticatorSpec, authenticationv1alpha1.JWTAuthenticatorPhaseError) // Create upstream OIDC provider and wait for it to become ready. oidcIdentityProvider := testlib.CreateTestOIDCIdentityProvider(t, idpv1alpha1.OIDCIdentityProviderSpec{ Issuer: env.SupervisorUpstreamOIDC.Issuer, @@ -776,7 +776,7 @@ func TestE2EFullIntegration_Browser(t *testing.T) { expectedUsername := env.SupervisorUpstreamLDAP.TestUserMailAttributeValue expectedGroups := env.SupervisorUpstreamLDAP.TestUserDirectGroupsDNs - authenticator := testlib.CreateTestJWTAuthenticator(topSetupCtx, t, defaultJWTAuthenticatorSpec, authenticationv1alpha1.JWTAuthenticatorPhaseError) + authenticator := testlib.CreateTestJWTAuthenticator(testCtx, t, defaultJWTAuthenticatorSpec, authenticationv1alpha1.JWTAuthenticatorPhaseError) createdProvider := setupClusterForEndToEndLDAPTest(t, expectedUsername, env) testlib.WaitForFederationDomainStatusPhase(testCtx, t, federationDomain.Name, supervisorconfigv1alpha1.FederationDomainPhaseReady) testlib.WaitForJWTAuthenticatorStatusPhase(testCtx, t, authenticator.Name, authenticationv1alpha1.JWTAuthenticatorPhaseReady) @@ -836,7 +836,7 @@ func TestE2EFullIntegration_Browser(t *testing.T) { expectedUsername := env.SupervisorUpstreamLDAP.TestUserMailAttributeValue expectedGroups := env.SupervisorUpstreamLDAP.TestUserDirectGroupsDNs - authenticator := testlib.CreateTestJWTAuthenticator(topSetupCtx, t, defaultJWTAuthenticatorSpec, authenticationv1alpha1.JWTAuthenticatorPhaseError) + authenticator := testlib.CreateTestJWTAuthenticator(testCtx, t, defaultJWTAuthenticatorSpec, authenticationv1alpha1.JWTAuthenticatorPhaseError) createdProvider := setupClusterForEndToEndLDAPTest(t, expectedUsername, env) testlib.WaitForFederationDomainStatusPhase(testCtx, t, federationDomain.Name, supervisorconfigv1alpha1.FederationDomainPhaseReady) testlib.WaitForJWTAuthenticatorStatusPhase(testCtx, t, authenticator.Name, authenticationv1alpha1.JWTAuthenticatorPhaseReady) @@ -900,7 +900,7 @@ func TestE2EFullIntegration_Browser(t *testing.T) { expectedUsername := env.SupervisorUpstreamLDAP.TestUserMailAttributeValue expectedGroups := env.SupervisorUpstreamLDAP.TestUserDirectGroupsDNs - authenticator := testlib.CreateTestJWTAuthenticator(topSetupCtx, t, defaultJWTAuthenticatorSpec, authenticationv1alpha1.JWTAuthenticatorPhaseError) + authenticator := testlib.CreateTestJWTAuthenticator(testCtx, t, defaultJWTAuthenticatorSpec, authenticationv1alpha1.JWTAuthenticatorPhaseError) createdProvider := setupClusterForEndToEndLDAPTest(t, expectedUsername, env) testlib.WaitForFederationDomainStatusPhase(testCtx, t, federationDomain.Name, supervisorconfigv1alpha1.FederationDomainPhaseReady) testlib.WaitForJWTAuthenticatorStatusPhase(testCtx, t, authenticator.Name, authenticationv1alpha1.JWTAuthenticatorPhaseReady) @@ -972,7 +972,7 @@ func TestE2EFullIntegration_Browser(t *testing.T) { expectedUsername := env.SupervisorUpstreamActiveDirectory.TestUserPrincipalNameValue expectedGroups := env.SupervisorUpstreamActiveDirectory.TestUserIndirectGroupsSAMAccountPlusDomainNames - authenticator := testlib.CreateTestJWTAuthenticator(topSetupCtx, t, defaultJWTAuthenticatorSpec, authenticationv1alpha1.JWTAuthenticatorPhaseError) + authenticator := testlib.CreateTestJWTAuthenticator(testCtx, t, defaultJWTAuthenticatorSpec, authenticationv1alpha1.JWTAuthenticatorPhaseError) createdProvider := setupClusterForEndToEndActiveDirectoryTest(t, expectedUsername, env) testlib.WaitForFederationDomainStatusPhase(testCtx, t, federationDomain.Name, supervisorconfigv1alpha1.FederationDomainPhaseReady) testlib.WaitForJWTAuthenticatorStatusPhase(testCtx, t, authenticator.Name, authenticationv1alpha1.JWTAuthenticatorPhaseReady) @@ -1032,7 +1032,7 @@ func TestE2EFullIntegration_Browser(t *testing.T) { expectedUsername := env.SupervisorUpstreamActiveDirectory.TestUserPrincipalNameValue expectedGroups := env.SupervisorUpstreamActiveDirectory.TestUserIndirectGroupsSAMAccountPlusDomainNames - authenticator := testlib.CreateTestJWTAuthenticator(topSetupCtx, t, defaultJWTAuthenticatorSpec, authenticationv1alpha1.JWTAuthenticatorPhaseError) + authenticator := testlib.CreateTestJWTAuthenticator(testCtx, t, defaultJWTAuthenticatorSpec, authenticationv1alpha1.JWTAuthenticatorPhaseError) createdProvider := setupClusterForEndToEndActiveDirectoryTest(t, expectedUsername, env) testlib.WaitForFederationDomainStatusPhase(testCtx, t, federationDomain.Name, supervisorconfigv1alpha1.FederationDomainPhaseReady) testlib.WaitForJWTAuthenticatorStatusPhase(testCtx, t, authenticator.Name, authenticationv1alpha1.JWTAuthenticatorPhaseReady) @@ -1106,7 +1106,7 @@ func TestE2EFullIntegration_Browser(t *testing.T) { expectedUsername := env.SupervisorUpstreamLDAP.TestUserMailAttributeValue expectedGroups := env.SupervisorUpstreamLDAP.TestUserDirectGroupsDNs - authenticator := testlib.CreateTestJWTAuthenticator(topSetupCtx, t, defaultJWTAuthenticatorSpec, authenticationv1alpha1.JWTAuthenticatorPhaseError) + authenticator := testlib.CreateTestJWTAuthenticator(testCtx, t, defaultJWTAuthenticatorSpec, authenticationv1alpha1.JWTAuthenticatorPhaseError) createdProvider := setupClusterForEndToEndLDAPTest(t, expectedUsername, env) testlib.WaitForFederationDomainStatusPhase(testCtx, t, federationDomain.Name, supervisorconfigv1alpha1.FederationDomainPhaseReady) testlib.WaitForJWTAuthenticatorStatusPhase(testCtx, t, authenticator.Name, authenticationv1alpha1.JWTAuthenticatorPhaseReady) @@ -1162,7 +1162,7 @@ func TestE2EFullIntegration_Browser(t *testing.T) { expectedUsername := env.SupervisorUpstreamActiveDirectory.TestUserPrincipalNameValue expectedGroups := env.SupervisorUpstreamActiveDirectory.TestUserIndirectGroupsSAMAccountPlusDomainNames - authenticator := testlib.CreateTestJWTAuthenticator(topSetupCtx, t, defaultJWTAuthenticatorSpec, authenticationv1alpha1.JWTAuthenticatorPhaseError) + authenticator := testlib.CreateTestJWTAuthenticator(testCtx, t, defaultJWTAuthenticatorSpec, authenticationv1alpha1.JWTAuthenticatorPhaseError) createdProvider := setupClusterForEndToEndActiveDirectoryTest(t, expectedUsername, env) testlib.WaitForFederationDomainStatusPhase(testCtx, t, federationDomain.Name, supervisorconfigv1alpha1.FederationDomainPhaseReady) testlib.WaitForJWTAuthenticatorStatusPhase(testCtx, t, authenticator.Name, authenticationv1alpha1.JWTAuthenticatorPhaseReady) @@ -1218,7 +1218,7 @@ func TestE2EFullIntegration_Browser(t *testing.T) { expectedUsername := env.SupervisorUpstreamLDAP.TestUserMailAttributeValue expectedGroups := env.SupervisorUpstreamLDAP.TestUserDirectGroupsDNs - authenticator := testlib.CreateTestJWTAuthenticator(topSetupCtx, t, defaultJWTAuthenticatorSpec, authenticationv1alpha1.JWTAuthenticatorPhaseError) + authenticator := testlib.CreateTestJWTAuthenticator(testCtx, t, defaultJWTAuthenticatorSpec, authenticationv1alpha1.JWTAuthenticatorPhaseError) createdProvider := setupClusterForEndToEndLDAPTest(t, expectedUsername, env) testlib.WaitForFederationDomainStatusPhase(testCtx, t, federationDomain.Name, supervisorconfigv1alpha1.FederationDomainPhaseReady) testlib.WaitForJWTAuthenticatorStatusPhase(testCtx, t, authenticator.Name, authenticationv1alpha1.JWTAuthenticatorPhaseReady) @@ -1309,9 +1309,8 @@ func TestE2EFullIntegration_Browser(t *testing.T) { ).Name, }, }, idpv1alpha1.GitHubPhaseReady) - authenticator := testlib.CreateTestJWTAuthenticator(topSetupCtx, t, defaultJWTAuthenticatorSpec, authenticationv1alpha1.JWTAuthenticatorPhaseError) testlib.WaitForFederationDomainStatusPhase(testCtx, t, federationDomain.Name, supervisorconfigv1alpha1.FederationDomainPhaseReady) - testlib.WaitForJWTAuthenticatorStatusPhase(testCtx, t, authenticator.Name, authenticationv1alpha1.JWTAuthenticatorPhaseReady) + authenticator := testlib.CreateTestJWTAuthenticator(testCtx, t, defaultJWTAuthenticatorSpec, authenticationv1alpha1.JWTAuthenticatorPhaseReady) // Use a specific session cache for this test. sessionCachePath := tempDir + "/test-sessions.yaml" @@ -1382,9 +1381,8 @@ func TestE2EFullIntegration_Browser(t *testing.T) { expectedDownstreamOIDCGroups = append(expectedDownstreamOIDCGroups, downstreamPrefix+g) } + authenticator := testlib.CreateTestJWTAuthenticator(testCtx, t, defaultJWTAuthenticatorSpec, authenticationv1alpha1.JWTAuthenticatorPhaseError) createdLDAPProvider := setupClusterForEndToEndLDAPTest(t, expectedDownstreamLDAPUsername, env) - - authenticator := testlib.CreateTestJWTAuthenticator(topSetupCtx, t, defaultJWTAuthenticatorSpec, authenticationv1alpha1.JWTAuthenticatorPhaseError) // Having one IDP should put the FederationDomain into a ready state. testlib.WaitForFederationDomainStatusPhase(testCtx, t, federationDomain.Name, supervisorconfigv1alpha1.FederationDomainPhaseReady) testlib.WaitForJWTAuthenticatorStatusPhase(testCtx, t, authenticator.Name, authenticationv1alpha1.JWTAuthenticatorPhaseReady) @@ -1703,9 +1701,8 @@ func TestE2EFullIntegration_Browser(t *testing.T) { expectedDownstreamLDAPGroups := env.SupervisorUpstreamLDAP.TestUserDirectGroupsDNs expectedDownstreamOIDCGroups := env.SupervisorUpstreamOIDC.ExpectedGroups + authenticator := testlib.CreateTestJWTAuthenticator(testCtx, t, defaultJWTAuthenticatorSpec, authenticationv1alpha1.JWTAuthenticatorPhaseError) createdLDAPProvider := setupClusterForEndToEndLDAPTest(t, expectedDownstreamLDAPUsername, env) - - authenticator := testlib.CreateTestJWTAuthenticator(topSetupCtx, t, defaultJWTAuthenticatorSpec, authenticationv1alpha1.JWTAuthenticatorPhaseError) // Having one IDP should put the FederationDomain into a ready state. testlib.WaitForFederationDomainStatusPhase(testCtx, t, federationDomain.Name, supervisorconfigv1alpha1.FederationDomainPhaseReady) testlib.WaitForJWTAuthenticatorStatusPhase(testCtx, t, authenticator.Name, authenticationv1alpha1.JWTAuthenticatorPhaseReady) From a4ad5d68a99fe3ff358d944e3b705f2e938ec100 Mon Sep 17 00:00:00 2001 From: Ryan Richard Date: Tue, 23 Jul 2024 10:10:04 -0700 Subject: [PATCH 29/99] Fix *_tls_spec_test.go for old versions of Kubernetes Co-authored-by: Joshua Casey --- test/integration/concierge_tls_spec_test.go | 13 ++++++++++--- test/integration/supervisor_tls_spec_test.go | 13 ++++++++++--- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/test/integration/concierge_tls_spec_test.go b/test/integration/concierge_tls_spec_test.go index 5cdfcb4af..cb59f4339 100644 --- a/test/integration/concierge_tls_spec_test.go +++ b/test/integration/concierge_tls_spec_test.go @@ -12,6 +12,7 @@ import ( "strings" "testing" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.pinniped.dev/internal/here" @@ -223,21 +224,27 @@ func TestTLSSpecKubeBuilderValidationConcierge_Parallel(t *testing.T) { yamlBytes := []byte(fmt.Sprintf(tc.customResourceYaml, env.APIGroupSuffix, resourceName)) require.NoError(t, os.WriteFile(yamlFilepath, yamlBytes, 0600)) + + // Use --validate=false to disable old client-side validations to avoid getting different error messages in Kube 1.24 and older. + // Note that this also disables validations of unknown and duplicate fields, but that's not what this test is about. //nolint:gosec // this is test code. - cmd := exec.CommandContext(context.Background(), "kubectl", []string{"apply", "-f", yamlFilepath}...) + cmd := exec.CommandContext(context.Background(), "kubectl", []string{"apply", "--validate=false", "-f", yamlFilepath}...) + var stdOut, stdErr bytes.Buffer cmd.Stdout = &stdOut cmd.Stderr = &stdErr err := cmd.Run() + t.Cleanup(func() { t.Helper() //nolint:gosec // this is test code. require.NoError(t, exec.Command("kubectl", []string{"delete", "--ignore-not-found", "-f", yamlFilepath}...).Run()) }) + if tc.expectedError == "" { + assert.Empty(t, stdErr.String()) + assert.Equal(t, fmt.Sprintf("webhookauthenticator.authentication.concierge.pinniped.dev/%s created\n", resourceName), stdOut.String()) require.NoError(t, err) - require.Equal(t, fmt.Sprintf("webhookauthenticator.authentication.concierge.pinniped.dev/%s created\n", resourceName), stdOut.String()) - require.Empty(t, stdErr.String()) } else { require.Equal(t, fmt.Sprintf(tc.expectedError, resourceName), strings.TrimSuffix(stdErr.String(), "\n")) } diff --git a/test/integration/supervisor_tls_spec_test.go b/test/integration/supervisor_tls_spec_test.go index d3724fbdd..877a85e02 100644 --- a/test/integration/supervisor_tls_spec_test.go +++ b/test/integration/supervisor_tls_spec_test.go @@ -12,6 +12,7 @@ import ( "strings" "testing" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.pinniped.dev/internal/here" @@ -273,21 +274,27 @@ func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { yamlBytes := []byte(fmt.Sprintf(tc.customResourceYaml, env.APIGroupSuffix, resourceName)) require.NoError(t, os.WriteFile(yamlFilepath, yamlBytes, 0600)) + + // Use --validate=false to disable old client-side validations to avoid getting different error messages in Kube 1.24 and older. + // Note that this also disables validations of unknown and duplicate fields, but that's not what this test is about. //nolint:gosec // this is test code. - cmd := exec.CommandContext(context.Background(), "kubectl", []string{"apply", "-f", yamlFilepath}...) + cmd := exec.CommandContext(context.Background(), "kubectl", []string{"apply", "--validate=false", "-f", yamlFilepath}...) + var stdOut, stdErr bytes.Buffer cmd.Stdout = &stdOut cmd.Stderr = &stdErr err := cmd.Run() + t.Cleanup(func() { t.Helper() //nolint:gosec // this is test code. require.NoError(t, exec.Command("kubectl", []string{"delete", "--ignore-not-found", "-f", yamlFilepath}...).Run()) }) + if tc.expectedError == "" { + assert.Empty(t, stdErr.String()) + assert.Equal(t, fmt.Sprintf("oidcidentityprovider.idp.supervisor.pinniped.dev/%s created\n", resourceName), stdOut.String()) require.NoError(t, err) - require.Equal(t, fmt.Sprintf("oidcidentityprovider.idp.supervisor.pinniped.dev/%s created\n", resourceName), stdOut.String()) - require.Empty(t, stdErr.String()) } else { require.Equal(t, fmt.Sprintf(tc.expectedError, resourceName), strings.TrimSuffix(stdErr.String(), "\n")) } From d62d6a1f2798d379a261c203803dfe582c8b78fc Mon Sep 17 00:00:00 2001 From: Joshua Casey Date: Tue, 23 Jul 2024 13:40:12 -0500 Subject: [PATCH 30/99] Refactor github_controller_watcher to simplify the tls Dial --- .../github_upstream_watcher.go | 60 +++++++++---------- 1 file changed, 29 insertions(+), 31 deletions(-) diff --git a/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher.go b/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher.go index 7003a4cbf..d54006cfd 100644 --- a/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher.go +++ b/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher.go @@ -89,8 +89,9 @@ func (g *GitHubValidatedAPICache) MarkAsValidated(address string, caBundlePEM [] address: address, caBundlePEMSHA256: sha256.Sum256(caBundlePEM), } - // Existence in the cache means it has been validated - g.cache.Set(key, nil, 24*365*time.Hour) + // Existence in the cache means it has been validated. + // The TTL in the cache is not important, it's just a "really long time". + g.cache.Set(key, nil, 365*24*time.Hour) } func (g *GitHubValidatedAPICache) IsValid(address string, caBundlePEM []byte) bool { @@ -329,30 +330,17 @@ func (c *gitHubWatcherController) validateUpstreamAndUpdateConditions(ctx contro c.configMapInformer) conditions = append(conditions, tlsConfigCondition) - var hostURL string - var httpClient *http.Client - if hostCondition.Status != metav1.ConditionTrue || tlsConfigCondition.Status != metav1.ConditionTrue { - conditions = append(conditions, &metav1.Condition{ - Type: GitHubConnectionValid, - Status: metav1.ConditionUnknown, - Reason: "UnableToValidate", - Message: "unable to validate; see other conditions for details", - }) - } else { - address := hostPort.Endpoint() - var githubConnectionCondition *metav1.Condition - var githubConnectionErr error - - githubConnectionCondition, hostURL, httpClient, githubConnectionErr = c.validateGitHubConnection( - address, - caBundlePEM, - certPool, - ) - if githubConnectionErr != nil { - applicationErrors = append(applicationErrors, githubConnectionErr) - } - conditions = append(conditions, githubConnectionCondition) + githubConnectionCondition, hostURL, httpClient, githubConnectionErr := c.validateGitHubConnection( + hostPort, + caBundlePEM, + certPool, + hostCondition.Status == metav1.ConditionTrue, + tlsConfigCondition.Status == metav1.ConditionTrue, + ) + if githubConnectionErr != nil { + applicationErrors = append(applicationErrors, githubConnectionErr) } + conditions = append(conditions, githubConnectionCondition) // The critical pattern to maintain is that every run of the sync loop will populate the exact number of the exact // same set of conditions. Conditions depending on other conditions should get Status: metav1.ConditionUnknown, or @@ -435,15 +423,24 @@ func validateHost(gitHubAPIConfig idpv1alpha1.GitHubAPIConfig) (*metav1.Conditio } func (c *gitHubWatcherController) validateGitHubConnection( - address string, + hostPort *endpointaddr.HostPort, caBundlePEM []byte, certPool *x509.CertPool, + hostConditionOk, tlsConfigConditionOk bool, ) (*metav1.Condition, string, *http.Client, error) { - var conn *tls.Conn - var tlsDialErr error + if !hostConditionOk || !tlsConfigConditionOk { + return &metav1.Condition{ + Type: GitHubConnectionValid, + Status: metav1.ConditionUnknown, + Reason: "UnableToValidate", + Message: "unable to validate; see other conditions for details", + }, "", nil, nil + } + + address := hostPort.Endpoint() if !c.validatedCache.IsValid(address, caBundlePEM) { - conn, tlsDialErr = c.dialFunc("tcp", address, ptls.Default(certPool)) + conn, tlsDialErr := c.dialFunc("tcp", address, ptls.Default(certPool)) if tlsDialErr != nil { return &metav1.Condition{ Type: GitHubConnectionValid, @@ -452,7 +449,8 @@ func (c *gitHubWatcherController) validateGitHubConnection( Message: fmt.Sprintf("cannot dial server spec.githubAPI.host (%q): %s", address, buildDialErrorMessage(tlsDialErr)), }, "", nil, tlsDialErr } - tlsDialErr = conn.Close() + // Any error should be ignored. We have performed a successful Dial, so no need to requeue this Sync. + _ = conn.Close() } c.validatedCache.MarkAsValidated(address, caBundlePEM) @@ -462,7 +460,7 @@ func (c *gitHubWatcherController) validateGitHubConnection( Status: metav1.ConditionTrue, Reason: conditionsutil.ReasonSuccess, Message: fmt.Sprintf("spec.githubAPI.host (%q) is reachable and TLS verification succeeds", address), - }, fmt.Sprintf("https://%s", address), phttp.Default(certPool), tlsDialErr + }, fmt.Sprintf("https://%s", address), phttp.Default(certPool), nil } // buildDialErrorMessage standardizes DNS error messages that appear differently on different platforms, so that tests and log grepping is uniform. From 0f103ed2a4de9a18e54017b67f64c576ea9259a8 Mon Sep 17 00:00:00 2001 From: Ryan Richard Date: Tue, 23 Jul 2024 11:51:32 -0700 Subject: [PATCH 31/99] Add unit tests for external CA bundle in oidc_upstream_watcher_test.go --- .../oidc_upstream_watcher_test.go | 807 ++++++++---------- 1 file changed, 343 insertions(+), 464 deletions(-) diff --git a/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher_test.go b/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher_test.go index cc5d33525..c3ea5fda2 100644 --- a/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher_test.go +++ b/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher_test.go @@ -236,7 +236,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { tests := []struct { name string inputUpstreams []runtime.Object - inputSecrets []runtime.Object + inputResources []runtime.Object wantErr string wantLogs []string wantResultingCache []*oidctestutil.TestUpstreamOIDCIdentityProvider @@ -246,7 +246,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { name: "no upstreams", }, { - name: "missing secret", + name: "missing client Secret", inputUpstreams: []runtime.Object{&idpv1alpha1.OIDCIdentityProvider{ ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testName}, Spec: idpv1alpha1.OIDCIdentityProviderSpec{ @@ -255,8 +255,8 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { Client: idpv1alpha1.OIDCClient{SecretName: testSecretName}, }, }}, - inputSecrets: []runtime.Object{}, - wantErr: controllerlib.ErrSyntheticRequeue.Error(), + inputResources: []runtime.Object{}, + wantErr: controllerlib.ErrSyntheticRequeue.Error(), wantLogs: []string{ `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"ClientCredentialsSecretValid","status":"False","reason":"SecretNotFound","message":"secret \"test-client-secret\" not found"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","status":"True","reason":"Success","message":"discovered issuer configuration"}`, @@ -271,33 +271,18 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { Phase: "Error", Conditions: []metav1.Condition{ happyAdditionalAuthorizeParametersValidCondition, - { - Type: "ClientCredentialsSecretValid", - Status: "False", - LastTransitionTime: now, - Reason: "SecretNotFound", - Message: `secret "test-client-secret" not found`, - }, - { - Type: "OIDCDiscoverySucceeded", - Status: "True", - LastTransitionTime: now, - Reason: "Success", - Message: "discovered issuer configuration", - }, - { - Type: "TLSConfigurationValid", - Status: "True", - LastTransitionTime: now, - Reason: "Success", - Message: "spec.tls is valid: loaded TLS configuration", - }, + {Type: "ClientCredentialsSecretValid", Status: "False", LastTransitionTime: now, Reason: "SecretNotFound", + Message: `secret "test-client-secret" not found`}, + {Type: "OIDCDiscoverySucceeded", Status: "True", LastTransitionTime: now, Reason: "Success", + Message: "discovered issuer configuration"}, + {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", + Message: "spec.tls is valid: loaded TLS configuration"}, }, }, }}, }, { - name: "secret has wrong type", + name: "client Secret has wrong type", inputUpstreams: []runtime.Object{&idpv1alpha1.OIDCIdentityProvider{ ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testName}, Spec: idpv1alpha1.OIDCIdentityProviderSpec{ @@ -306,7 +291,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { Client: idpv1alpha1.OIDCClient{SecretName: testSecretName}, }, }}, - inputSecrets: []runtime.Object{&corev1.Secret{ + inputResources: []runtime.Object{&corev1.Secret{ ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testSecretName}, Type: "some-other-type", Data: testValidSecretData, @@ -326,27 +311,12 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { Phase: "Error", Conditions: []metav1.Condition{ happyAdditionalAuthorizeParametersValidCondition, - { - Type: "ClientCredentialsSecretValid", - Status: "False", - LastTransitionTime: now, - Reason: "SecretWrongType", - Message: `referenced Secret "test-client-secret" has wrong type "some-other-type" (should be "secrets.pinniped.dev/oidc-client")`, - }, - { - Type: "OIDCDiscoverySucceeded", - Status: "True", - LastTransitionTime: now, - Reason: "Success", - Message: "discovered issuer configuration", - }, - { - Type: "TLSConfigurationValid", - Status: "True", - LastTransitionTime: now, - Reason: "Success", - Message: "spec.tls is valid: loaded TLS configuration", - }, + {Type: "ClientCredentialsSecretValid", Status: "False", LastTransitionTime: now, Reason: "SecretWrongType", + Message: `referenced Secret "test-client-secret" has wrong type "some-other-type" (should be "secrets.pinniped.dev/oidc-client")`}, + {Type: "OIDCDiscoverySucceeded", Status: "True", LastTransitionTime: now, Reason: "Success", + Message: "discovered issuer configuration"}, + {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", + Message: "spec.tls is valid: loaded TLS configuration"}, }, }, }}, @@ -361,7 +331,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { Client: idpv1alpha1.OIDCClient{SecretName: testSecretName}, }, }}, - inputSecrets: []runtime.Object{&corev1.Secret{ + inputResources: []runtime.Object{&corev1.Secret{ ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testSecretName}, Type: "secrets.pinniped.dev/oidc-client", }}, @@ -380,27 +350,12 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { Phase: "Error", Conditions: []metav1.Condition{ happyAdditionalAuthorizeParametersValidCondition, - { - Type: "ClientCredentialsSecretValid", - Status: "False", - LastTransitionTime: now, - Reason: "SecretMissingKeys", - Message: `referenced Secret "test-client-secret" is missing required keys ["clientID" "clientSecret"]`, - }, - { - Type: "OIDCDiscoverySucceeded", - Status: "True", - LastTransitionTime: now, - Reason: "Success", - Message: "discovered issuer configuration", - }, - { - Type: "TLSConfigurationValid", - Status: "True", - LastTransitionTime: now, - Reason: "Success", - Message: "spec.tls is valid: loaded TLS configuration", - }, + {Type: "ClientCredentialsSecretValid", Status: "False", LastTransitionTime: now, Reason: "SecretMissingKeys", + Message: `referenced Secret "test-client-secret" is missing required keys ["clientID" "clientSecret"]`}, + {Type: "OIDCDiscoverySucceeded", Status: "True", LastTransitionTime: now, Reason: "Success", + Message: "discovered issuer configuration"}, + {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", + Message: "spec.tls is valid: loaded TLS configuration"}, }, }, }}, @@ -417,7 +372,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { Client: idpv1alpha1.OIDCClient{SecretName: testSecretName}, }, }}, - inputSecrets: []runtime.Object{&corev1.Secret{ + inputResources: []runtime.Object{&corev1.Secret{ ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testSecretName}, Type: "secrets.pinniped.dev/oidc-client", Data: testValidSecretData, @@ -438,27 +393,12 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { Phase: "Error", Conditions: []metav1.Condition{ happyAdditionalAuthorizeParametersValidCondition, - { - Type: "ClientCredentialsSecretValid", - Status: "True", - LastTransitionTime: now, - Reason: "Success", - Message: "loaded client credentials", - }, - { - Type: "OIDCDiscoverySucceeded", - Status: "False", - LastTransitionTime: now, - Reason: "InvalidTLSConfig", - Message: `spec.tls.certificateAuthorityData is invalid: illegal base64 data at input byte 7`, - }, - { - Type: "TLSConfigurationValid", - Status: "False", - LastTransitionTime: now, - Reason: "InvalidTLSConfig", - Message: "spec.tls.certificateAuthorityData is invalid: illegal base64 data at input byte 7", - }, + {Type: "ClientCredentialsSecretValid", Status: "True", LastTransitionTime: now, Reason: "Success", + Message: "loaded client credentials"}, + {Type: "OIDCDiscoverySucceeded", Status: "False", LastTransitionTime: now, Reason: "InvalidTLSConfig", + Message: `spec.tls.certificateAuthorityData is invalid: illegal base64 data at input byte 7`}, + {Type: "TLSConfigurationValid", Status: "False", LastTransitionTime: now, Reason: "InvalidTLSConfig", + Message: "spec.tls.certificateAuthorityData is invalid: illegal base64 data at input byte 7"}, }, }, }}, @@ -475,7 +415,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { Client: idpv1alpha1.OIDCClient{SecretName: testSecretName}, }, }}, - inputSecrets: []runtime.Object{&corev1.Secret{ + inputResources: []runtime.Object{&corev1.Secret{ ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testSecretName}, Type: "secrets.pinniped.dev/oidc-client", Data: testValidSecretData, @@ -496,27 +436,12 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { Phase: "Error", Conditions: []metav1.Condition{ happyAdditionalAuthorizeParametersValidCondition, - { - Type: "ClientCredentialsSecretValid", - Status: "True", - LastTransitionTime: now, - Reason: "Success", - Message: "loaded client credentials", - }, - { - Type: "OIDCDiscoverySucceeded", - Status: "False", - LastTransitionTime: now, - Reason: "InvalidTLSConfig", - Message: `spec.tls.certificateAuthorityData is invalid: no certificates found`, - }, - { - Type: "TLSConfigurationValid", - Status: "False", - LastTransitionTime: now, - Reason: "InvalidTLSConfig", - Message: "spec.tls.certificateAuthorityData is invalid: no certificates found", - }, + {Type: "ClientCredentialsSecretValid", Status: "True", LastTransitionTime: now, Reason: "Success", + Message: "loaded client credentials"}, + {Type: "OIDCDiscoverySucceeded", Status: "False", LastTransitionTime: now, Reason: "InvalidTLSConfig", + Message: `spec.tls.certificateAuthorityData is invalid: no certificates found`}, + {Type: "TLSConfigurationValid", Status: "False", LastTransitionTime: now, Reason: "InvalidTLSConfig", + Message: "spec.tls.certificateAuthorityData is invalid: no certificates found"}, }, }, }}, @@ -530,7 +455,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { Client: idpv1alpha1.OIDCClient{SecretName: testSecretName}, }, }}, - inputSecrets: []runtime.Object{&corev1.Secret{ + inputResources: []runtime.Object{&corev1.Secret{ ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testSecretName}, Type: "secrets.pinniped.dev/oidc-client", Data: testValidSecretData, @@ -550,27 +475,12 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { Phase: "Error", Conditions: []metav1.Condition{ happyAdditionalAuthorizeParametersValidCondition, - { - Type: "ClientCredentialsSecretValid", - Status: "True", - LastTransitionTime: now, - Reason: "Success", - Message: "loaded client credentials", - }, - { - Type: "OIDCDiscoverySucceeded", - Status: "False", - LastTransitionTime: now, - Reason: "Unreachable", - Message: `failed to parse issuer URL: parse "%invalid-url-that-is-really-really-long-nanananananananannanananan-batman-nanananananananananananananana-batman-lalalalalalalalalal-batman-weeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee": invalid URL escape "%in"`, - }, - { - Type: "TLSConfigurationValid", - Status: "True", - LastTransitionTime: now, - Reason: "Success", - Message: "spec.tls is valid: no TLS configuration provided", - }, + {Type: "ClientCredentialsSecretValid", Status: "True", LastTransitionTime: now, Reason: "Success", + Message: "loaded client credentials"}, + {Type: "OIDCDiscoverySucceeded", Status: "False", LastTransitionTime: now, Reason: "Unreachable", + Message: `failed to parse issuer URL: parse "%invalid-url-that-is-really-really-long-nanananananananannanananan-batman-nanananananananananananananana-batman-lalalalalalalalalal-batman-weeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee": invalid URL escape "%in"`}, + {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", + Message: "spec.tls is valid: no TLS configuration provided"}, }, }, }}, @@ -584,7 +494,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { Client: idpv1alpha1.OIDCClient{SecretName: testSecretName}, }, }}, - inputSecrets: []runtime.Object{&corev1.Secret{ + inputResources: []runtime.Object{&corev1.Secret{ ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testSecretName}, Type: "secrets.pinniped.dev/oidc-client", Data: testValidSecretData, @@ -604,27 +514,12 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { Phase: "Error", Conditions: []metav1.Condition{ happyAdditionalAuthorizeParametersValidCondition, - { - Type: "ClientCredentialsSecretValid", - Status: "True", - LastTransitionTime: now, - Reason: "Success", - Message: "loaded client credentials", - }, - { - Type: "OIDCDiscoverySucceeded", - Status: "False", - LastTransitionTime: now, - Reason: "Unreachable", - Message: `issuer URL '` + strings.Replace(testIssuerURL, "https", "http", 1) + `' must have "https" scheme, not "http"`, - }, - { - Type: "TLSConfigurationValid", - Status: "True", - LastTransitionTime: now, - Reason: "Success", - Message: "spec.tls is valid: no TLS configuration provided", - }, + {Type: "ClientCredentialsSecretValid", Status: "True", LastTransitionTime: now, Reason: "Success", + Message: "loaded client credentials"}, + {Type: "OIDCDiscoverySucceeded", Status: "False", LastTransitionTime: now, Reason: "Unreachable", + Message: `issuer URL '` + strings.Replace(testIssuerURL, "https", "http", 1) + `' must have "https" scheme, not "http"`}, + {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", + Message: "spec.tls is valid: no TLS configuration provided"}, }, }, }}, @@ -638,7 +533,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { Client: idpv1alpha1.OIDCClient{SecretName: testSecretName}, }, }}, - inputSecrets: []runtime.Object{&corev1.Secret{ + inputResources: []runtime.Object{&corev1.Secret{ ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testSecretName}, Type: "secrets.pinniped.dev/oidc-client", Data: testValidSecretData, @@ -658,27 +553,12 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { Phase: "Error", Conditions: []metav1.Condition{ happyAdditionalAuthorizeParametersValidCondition, - { - Type: "ClientCredentialsSecretValid", - Status: "True", - LastTransitionTime: now, - Reason: "Success", - Message: "loaded client credentials", - }, - { - Type: "OIDCDiscoverySucceeded", - Status: "False", - LastTransitionTime: now, - Reason: "Unreachable", - Message: `issuer URL '` + testIssuerURL + "?sub=foo" + `' cannot contain query or fragment component`, - }, - { - Type: "TLSConfigurationValid", - Status: "True", - LastTransitionTime: now, - Reason: "Success", - Message: "spec.tls is valid: no TLS configuration provided", - }, + {Type: "ClientCredentialsSecretValid", Status: "True", LastTransitionTime: now, Reason: "Success", + Message: "loaded client credentials"}, + {Type: "OIDCDiscoverySucceeded", Status: "False", LastTransitionTime: now, Reason: "Unreachable", + Message: `issuer URL '` + testIssuerURL + "?sub=foo" + `' cannot contain query or fragment component`}, + {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", + Message: "spec.tls is valid: no TLS configuration provided"}, }, }, }}, @@ -692,7 +572,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { Client: idpv1alpha1.OIDCClient{SecretName: testSecretName}, }, }}, - inputSecrets: []runtime.Object{&corev1.Secret{ + inputResources: []runtime.Object{&corev1.Secret{ ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testSecretName}, Type: "secrets.pinniped.dev/oidc-client", Data: testValidSecretData, @@ -712,27 +592,12 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { Phase: "Error", Conditions: []metav1.Condition{ happyAdditionalAuthorizeParametersValidCondition, - { - Type: "ClientCredentialsSecretValid", - Status: "True", - LastTransitionTime: now, - Reason: "Success", - Message: "loaded client credentials", - }, - { - Type: "OIDCDiscoverySucceeded", - Status: "False", - LastTransitionTime: now, - Reason: "Unreachable", - Message: `issuer URL '` + testIssuerURL + "#fragment" + `' cannot contain query or fragment component`, - }, - { - Type: "TLSConfigurationValid", - Status: "True", - LastTransitionTime: now, - Reason: "Success", - Message: "spec.tls is valid: no TLS configuration provided", - }, + {Type: "ClientCredentialsSecretValid", Status: "True", LastTransitionTime: now, Reason: "Success", + Message: "loaded client credentials"}, + {Type: "OIDCDiscoverySucceeded", Status: "False", LastTransitionTime: now, Reason: "Unreachable", + Message: `issuer URL '` + testIssuerURL + "#fragment" + `' cannot contain query or fragment component`}, + {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", + Message: "spec.tls is valid: no TLS configuration provided"}, }, }, }}, @@ -747,7 +612,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { Client: idpv1alpha1.OIDCClient{SecretName: testSecretName}, }, }}, - inputSecrets: []runtime.Object{&corev1.Secret{ + inputResources: []runtime.Object{&corev1.Secret{ ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testSecretName}, Type: "secrets.pinniped.dev/oidc-client", Data: testValidSecretData, @@ -768,28 +633,13 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { Phase: "Error", Conditions: []metav1.Condition{ happyAdditionalAuthorizeParametersValidCondition, - { - Type: "ClientCredentialsSecretValid", - Status: "True", - LastTransitionTime: now, - Reason: "Success", - Message: "loaded client credentials", - }, - { - Type: "OIDCDiscoverySucceeded", - Status: "False", - LastTransitionTime: now, - Reason: "Unreachable", - Message: `failed to perform OIDC discovery against "` + testIssuerURL + `/valid-url-that-is-really-really-long-nanananananananannanananan-batman-nanananananananananananananana-batman-lalalalalalalalalal-batman-weeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee": -Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nanananananananannanananan-batman-nanananananananananananananana-batman-lalalalalalalalalal-batman-weeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee/.well-known/openid-configuration": tls: failed to verify certificate: x509: certificate signed by unknown authority`, - }, - { - Type: "TLSConfigurationValid", - Status: "True", - LastTransitionTime: now, - Reason: "Success", - Message: "spec.tls is valid: loaded TLS configuration", - }, + {Type: "ClientCredentialsSecretValid", Status: "True", LastTransitionTime: now, Reason: "Success", + Message: "loaded client credentials"}, + {Type: "OIDCDiscoverySucceeded", Status: "False", LastTransitionTime: now, Reason: "Unreachable", + Message: `failed to perform OIDC discovery against "` + testIssuerURL + `/valid-url-that-is-really-really-long-nanananananananannanananan-batman-nanananananananananananananana-batman-lalalalalalalalalal-batman-weeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee":` + "\n" + + `Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nanananananananannanananan-batman-nanananananananananananananana-batman-lalalalalalalalalal-batman-weeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee/.well-known/openid-configuration": tls: failed to verify certificate: x509: certificate signed by unknown authority`}, + {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", + Message: "spec.tls is valid: loaded TLS configuration"}, }, }, }}, @@ -804,7 +654,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana Client: idpv1alpha1.OIDCClient{SecretName: testSecretName}, }, }}, - inputSecrets: []runtime.Object{&corev1.Secret{ + inputResources: []runtime.Object{&corev1.Secret{ ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testSecretName}, Type: "secrets.pinniped.dev/oidc-client", Data: testValidSecretData, @@ -824,27 +674,12 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana Phase: "Error", Conditions: []metav1.Condition{ happyAdditionalAuthorizeParametersValidCondition, - { - Type: "ClientCredentialsSecretValid", - Status: "True", - LastTransitionTime: now, - Reason: "Success", - Message: "loaded client credentials", - }, - { - Type: "OIDCDiscoverySucceeded", - Status: "False", - LastTransitionTime: now, - Reason: "InvalidResponse", - Message: `failed to parse authorization endpoint URL: parse "%": invalid URL escape "%"`, - }, - { - Type: "TLSConfigurationValid", - Status: "True", - LastTransitionTime: now, - Reason: "Success", - Message: "spec.tls is valid: loaded TLS configuration", - }, + {Type: "ClientCredentialsSecretValid", Status: "True", LastTransitionTime: now, Reason: "Success", + Message: "loaded client credentials"}, + {Type: "OIDCDiscoverySucceeded", Status: "False", LastTransitionTime: now, Reason: "InvalidResponse", + Message: `failed to parse authorization endpoint URL: parse "%": invalid URL escape "%"`}, + {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", + Message: "spec.tls is valid: loaded TLS configuration"}, }, }, }}, @@ -859,7 +694,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana Client: idpv1alpha1.OIDCClient{SecretName: testSecretName}, }, }}, - inputSecrets: []runtime.Object{&corev1.Secret{ + inputResources: []runtime.Object{&corev1.Secret{ ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testSecretName}, Type: "secrets.pinniped.dev/oidc-client", Data: testValidSecretData, @@ -879,27 +714,12 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana Phase: "Error", Conditions: []metav1.Condition{ happyAdditionalAuthorizeParametersValidCondition, - { - Type: "ClientCredentialsSecretValid", - Status: "True", - LastTransitionTime: now, - Reason: "Success", - Message: "loaded client credentials", - }, - { - Type: "OIDCDiscoverySucceeded", - Status: "False", - LastTransitionTime: now, - Reason: "InvalidResponse", - Message: `failed to parse revocation endpoint URL: parse "%": invalid URL escape "%"`, - }, - { - Type: "TLSConfigurationValid", - Status: "True", - LastTransitionTime: now, - Reason: "Success", - Message: "spec.tls is valid: loaded TLS configuration", - }, + {Type: "ClientCredentialsSecretValid", Status: "True", LastTransitionTime: now, Reason: "Success", + Message: "loaded client credentials"}, + {Type: "OIDCDiscoverySucceeded", Status: "False", LastTransitionTime: now, Reason: "InvalidResponse", + Message: `failed to parse revocation endpoint URL: parse "%": invalid URL escape "%"`}, + {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", + Message: "spec.tls is valid: loaded TLS configuration"}, }, }, }}, @@ -914,7 +734,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana Client: idpv1alpha1.OIDCClient{SecretName: testSecretName}, }, }}, - inputSecrets: []runtime.Object{&corev1.Secret{ + inputResources: []runtime.Object{&corev1.Secret{ ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testSecretName}, Type: "secrets.pinniped.dev/oidc-client", Data: testValidSecretData, @@ -934,27 +754,12 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana Phase: "Error", Conditions: []metav1.Condition{ happyAdditionalAuthorizeParametersValidCondition, - { - Type: "ClientCredentialsSecretValid", - Status: "True", - LastTransitionTime: now, - Reason: "Success", - Message: "loaded client credentials", - }, - { - Type: "OIDCDiscoverySucceeded", - Status: "False", - LastTransitionTime: now, - Reason: "InvalidResponse", - Message: `authorization endpoint URL 'http://example.com/authorize' must have "https" scheme, not "http"`, - }, - { - Type: "TLSConfigurationValid", - Status: "True", - LastTransitionTime: now, - Reason: "Success", - Message: "spec.tls is valid: loaded TLS configuration", - }, + {Type: "ClientCredentialsSecretValid", Status: "True", LastTransitionTime: now, Reason: "Success", + Message: "loaded client credentials"}, + {Type: "OIDCDiscoverySucceeded", Status: "False", LastTransitionTime: now, Reason: "InvalidResponse", + Message: `authorization endpoint URL 'http://example.com/authorize' must have "https" scheme, not "http"`}, + {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", + Message: "spec.tls is valid: loaded TLS configuration"}, }, }, }}, @@ -969,7 +774,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana Client: idpv1alpha1.OIDCClient{SecretName: testSecretName}, }, }}, - inputSecrets: []runtime.Object{&corev1.Secret{ + inputResources: []runtime.Object{&corev1.Secret{ ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testSecretName}, Type: "secrets.pinniped.dev/oidc-client", Data: testValidSecretData, @@ -989,27 +794,12 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana Phase: "Error", Conditions: []metav1.Condition{ happyAdditionalAuthorizeParametersValidCondition, - { - Type: "ClientCredentialsSecretValid", - Status: "True", - LastTransitionTime: now, - Reason: "Success", - Message: "loaded client credentials", - }, - { - Type: "OIDCDiscoverySucceeded", - Status: "False", - LastTransitionTime: now, - Reason: "InvalidResponse", - Message: `revocation endpoint URL 'http://example.com/revoke' must have "https" scheme, not "http"`, - }, - { - Type: "TLSConfigurationValid", - Status: "True", - LastTransitionTime: now, - Reason: "Success", - Message: "spec.tls is valid: loaded TLS configuration", - }, + {Type: "ClientCredentialsSecretValid", Status: "True", LastTransitionTime: now, Reason: "Success", + Message: "loaded client credentials"}, + {Type: "OIDCDiscoverySucceeded", Status: "False", LastTransitionTime: now, Reason: "InvalidResponse", + Message: `revocation endpoint URL 'http://example.com/revoke' must have "https" scheme, not "http"`}, + {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", + Message: "spec.tls is valid: loaded TLS configuration"}, }, }, }}, @@ -1024,7 +814,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana Client: idpv1alpha1.OIDCClient{SecretName: testSecretName}, }, }}, - inputSecrets: []runtime.Object{&corev1.Secret{ + inputResources: []runtime.Object{&corev1.Secret{ ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testSecretName}, Type: "secrets.pinniped.dev/oidc-client", Data: testValidSecretData, @@ -1044,27 +834,12 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana Phase: "Error", Conditions: []metav1.Condition{ happyAdditionalAuthorizeParametersValidCondition, - { - Type: "ClientCredentialsSecretValid", - Status: "True", - LastTransitionTime: now, - Reason: "Success", - Message: "loaded client credentials", - }, - { - Type: "OIDCDiscoverySucceeded", - Status: "False", - LastTransitionTime: now, - Reason: "InvalidResponse", - Message: `token endpoint URL 'http://example.com/token' must have "https" scheme, not "http"`, - }, - { - Type: "TLSConfigurationValid", - Status: "True", - LastTransitionTime: now, - Reason: "Success", - Message: "spec.tls is valid: loaded TLS configuration", - }, + {Type: "ClientCredentialsSecretValid", Status: "True", LastTransitionTime: now, Reason: "Success", + Message: "loaded client credentials"}, + {Type: "OIDCDiscoverySucceeded", Status: "False", LastTransitionTime: now, Reason: "InvalidResponse", + Message: `token endpoint URL 'http://example.com/token' must have "https" scheme, not "http"`}, + {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", + Message: "spec.tls is valid: loaded TLS configuration"}, }, }, }}, @@ -1079,7 +854,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana Client: idpv1alpha1.OIDCClient{SecretName: testSecretName}, }, }}, - inputSecrets: []runtime.Object{&corev1.Secret{ + inputResources: []runtime.Object{&corev1.Secret{ ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testSecretName}, Type: "secrets.pinniped.dev/oidc-client", Data: testValidSecretData, @@ -1099,27 +874,12 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana Phase: "Error", Conditions: []metav1.Condition{ happyAdditionalAuthorizeParametersValidCondition, - { - Type: "ClientCredentialsSecretValid", - Status: "True", - LastTransitionTime: now, - Reason: "Success", - Message: "loaded client credentials", - }, - { - Type: "OIDCDiscoverySucceeded", - Status: "False", - LastTransitionTime: now, - Reason: "InvalidResponse", - Message: `token endpoint URL '' must have "https" scheme, not ""`, - }, - { - Type: "TLSConfigurationValid", - Status: "True", - LastTransitionTime: now, - Reason: "Success", - Message: "spec.tls is valid: loaded TLS configuration", - }, + {Type: "ClientCredentialsSecretValid", Status: "True", LastTransitionTime: now, Reason: "Success", + Message: "loaded client credentials"}, + {Type: "OIDCDiscoverySucceeded", Status: "False", LastTransitionTime: now, Reason: "InvalidResponse", + Message: `token endpoint URL '' must have "https" scheme, not ""`}, + {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", + Message: "spec.tls is valid: loaded TLS configuration"}, }, }, }}, @@ -1134,7 +894,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana Client: idpv1alpha1.OIDCClient{SecretName: testSecretName}, }, }}, - inputSecrets: []runtime.Object{&corev1.Secret{ + inputResources: []runtime.Object{&corev1.Secret{ ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testSecretName}, Type: "secrets.pinniped.dev/oidc-client", Data: testValidSecretData, @@ -1154,27 +914,12 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana Phase: "Error", Conditions: []metav1.Condition{ happyAdditionalAuthorizeParametersValidCondition, - { - Type: "ClientCredentialsSecretValid", - Status: "True", - LastTransitionTime: now, - Reason: "Success", - Message: "loaded client credentials", - }, - { - Type: "OIDCDiscoverySucceeded", - Status: "False", - LastTransitionTime: now, - Reason: "InvalidResponse", - Message: `authorization endpoint URL '' must have "https" scheme, not ""`, - }, - { - Type: "TLSConfigurationValid", - Status: "True", - LastTransitionTime: now, - Reason: "Success", - Message: "spec.tls is valid: loaded TLS configuration", - }, + {Type: "ClientCredentialsSecretValid", Status: "True", LastTransitionTime: now, Reason: "Success", + Message: "loaded client credentials"}, + {Type: "OIDCDiscoverySucceeded", Status: "False", LastTransitionTime: now, Reason: "InvalidResponse", + Message: `authorization endpoint URL '' must have "https" scheme, not ""`}, + {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", + Message: "spec.tls is valid: loaded TLS configuration"}, }, }, }}, @@ -1196,12 +941,14 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana Status: idpv1alpha1.OIDCIdentityProviderStatus{ Phase: "Error", Conditions: []metav1.Condition{ - {Type: "ClientCredentialsSecretValid", Status: "False", LastTransitionTime: earlier, Reason: "SomeError1", Message: "some previous error 1"}, - {Type: "OIDCDiscoverySucceeded", Status: "False", LastTransitionTime: earlier, Reason: "SomeError2", Message: "some previous error 2"}, + {Type: "ClientCredentialsSecretValid", Status: "False", LastTransitionTime: earlier, Reason: "SomeError1", + Message: "some previous error 1"}, + {Type: "OIDCDiscoverySucceeded", Status: "False", LastTransitionTime: earlier, Reason: "SomeError2", + Message: "some previous error 2"}, }, }, }}, - inputSecrets: []runtime.Object{&corev1.Secret{ + inputResources: []runtime.Object{&corev1.Secret{ ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testSecretName}, Type: "secrets.pinniped.dev/oidc-client", Data: testValidSecretData, @@ -1233,9 +980,12 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana Phase: "Ready", Conditions: []metav1.Condition{ happyAdditionalAuthorizeParametersValidCondition, - {Type: "ClientCredentialsSecretValid", Status: "True", LastTransitionTime: now, Reason: "Success", Message: "loaded client credentials"}, - {Type: "OIDCDiscoverySucceeded", Status: "True", LastTransitionTime: now, Reason: "Success", Message: "discovered issuer configuration"}, - {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", Message: "spec.tls is valid: loaded TLS configuration"}, + {Type: "ClientCredentialsSecretValid", Status: "True", LastTransitionTime: now, Reason: "Success", + Message: "loaded client credentials"}, + {Type: "OIDCDiscoverySucceeded", Status: "True", LastTransitionTime: now, Reason: "Success", + Message: "discovered issuer configuration"}, + {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", + Message: "spec.tls is valid: loaded TLS configuration"}, }, }, }}, @@ -1254,12 +1004,14 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana Phase: "Ready", Conditions: []metav1.Condition{ happyAdditionalAuthorizeParametersValidConditionEarlier, - {Type: "ClientCredentialsSecretValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "loaded client credentials"}, - {Type: "OIDCDiscoverySucceeded", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "discovered issuer configuration"}, + {Type: "ClientCredentialsSecretValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", + Message: "loaded client credentials"}, + {Type: "OIDCDiscoverySucceeded", Status: "True", LastTransitionTime: earlier, Reason: "Success", + Message: "discovered issuer configuration"}, }, }, }}, - inputSecrets: []runtime.Object{&corev1.Secret{ + inputResources: []runtime.Object{&corev1.Secret{ ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testSecretName}, Type: "secrets.pinniped.dev/oidc-client", Data: testValidSecretData, @@ -1290,10 +1042,147 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana Status: idpv1alpha1.OIDCIdentityProviderStatus{ Phase: "Ready", Conditions: []metav1.Condition{ - {Type: "AdditionalAuthorizeParametersValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "additionalAuthorizeParameters parameter names are allowed", ObservedGeneration: 1234}, - {Type: "ClientCredentialsSecretValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "loaded client credentials", ObservedGeneration: 1234}, - {Type: "OIDCDiscoverySucceeded", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "discovered issuer configuration", ObservedGeneration: 1234}, - {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", Message: "spec.tls is valid: loaded TLS configuration", ObservedGeneration: 1234}, + {Type: "AdditionalAuthorizeParametersValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", + Message: "additionalAuthorizeParameters parameter names are allowed", ObservedGeneration: 1234}, + {Type: "ClientCredentialsSecretValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", + Message: "loaded client credentials", ObservedGeneration: 1234}, + {Type: "OIDCDiscoverySucceeded", Status: "True", LastTransitionTime: earlier, Reason: "Success", + Message: "discovered issuer configuration", ObservedGeneration: 1234}, + {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", + Message: "spec.tls is valid: loaded TLS configuration", ObservedGeneration: 1234}, + }, + }, + }}, + }, + { + name: "valid upstream with CA bundle read from a Secret", + inputUpstreams: []runtime.Object{&idpv1alpha1.OIDCIdentityProvider{ + ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testName, Generation: 1234, UID: testUID}, + Spec: idpv1alpha1.OIDCIdentityProviderSpec{ + Issuer: testIssuerURL, + TLS: &idpv1alpha1.TLSSpec{ + CertificateAuthorityDataSource: &idpv1alpha1.CABundleSource{ + Kind: "Secret", + Name: "ca-bundle-secret", + Key: "ca.crt", + }, + }, + Client: idpv1alpha1.OIDCClient{SecretName: testSecretName}, + Claims: idpv1alpha1.OIDCClaims{Groups: testGroupsClaim, Username: testUsernameClaim}, + }, + }}, + inputResources: []runtime.Object{ + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testSecretName}, + Type: "secrets.pinniped.dev/oidc-client", + Data: testValidSecretData, + }, + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: "ca-bundle-secret"}, + Type: corev1.SecretTypeOpaque, + Data: map[string][]byte{"ca.crt": []byte(testIssuerCA)}, + }, + }, + wantLogs: []string{ + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"ClientCredentialsSecretValid","status":"True","reason":"Success","message":"loaded client credentials"}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","status":"True","reason":"Success","message":"discovered issuer configuration"}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: loaded TLS configuration"}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"AdditionalAuthorizeParametersValid","status":"True","reason":"Success","message":"additionalAuthorizeParameters parameter names are allowed"}`, + }, + wantResultingCache: []*oidctestutil.TestUpstreamOIDCIdentityProvider{ + { + Name: testName, + ClientID: testClientID, + AuthorizationURL: *testIssuerAuthorizeURL, + RevocationURL: testIssuerRevocationURL, + Scopes: testDefaultExpectedScopes, + UsernameClaim: testUsernameClaim, + GroupsClaim: testGroupsClaim, + AllowPasswordGrant: false, + AdditionalAuthcodeParams: map[string]string{}, + AdditionalClaimMappings: nil, // Does not default to empty map + ResourceUID: testUID, + }, + }, + wantResultingUpstreams: []idpv1alpha1.OIDCIdentityProvider{{ + ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testName, Generation: 1234, UID: testUID}, + Status: idpv1alpha1.OIDCIdentityProviderStatus{ + Phase: "Ready", + Conditions: []metav1.Condition{ + {Type: "AdditionalAuthorizeParametersValid", Status: "True", LastTransitionTime: now, Reason: "Success", + Message: "additionalAuthorizeParameters parameter names are allowed", ObservedGeneration: 1234}, + {Type: "ClientCredentialsSecretValid", Status: "True", LastTransitionTime: now, Reason: "Success", + Message: "loaded client credentials", ObservedGeneration: 1234}, + {Type: "OIDCDiscoverySucceeded", Status: "True", LastTransitionTime: now, Reason: "Success", + Message: "discovered issuer configuration", ObservedGeneration: 1234}, + {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", + Message: "spec.tls is valid: loaded TLS configuration", ObservedGeneration: 1234}, + }, + }, + }}, + }, + { + name: "valid upstream with CA bundle read from a ConfigMap", + inputUpstreams: []runtime.Object{&idpv1alpha1.OIDCIdentityProvider{ + ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testName, Generation: 1234, UID: testUID}, + Spec: idpv1alpha1.OIDCIdentityProviderSpec{ + Issuer: testIssuerURL, + TLS: &idpv1alpha1.TLSSpec{ + CertificateAuthorityDataSource: &idpv1alpha1.CABundleSource{ + Kind: "ConfigMap", + Name: "ca-bundle-configmap", + Key: "ca.crt", + }, + }, + Client: idpv1alpha1.OIDCClient{SecretName: testSecretName}, + Claims: idpv1alpha1.OIDCClaims{Groups: testGroupsClaim, Username: testUsernameClaim}, + }, + }}, + inputResources: []runtime.Object{ + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testSecretName}, + Type: "secrets.pinniped.dev/oidc-client", + Data: testValidSecretData, + }, + &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: "ca-bundle-configmap"}, + Data: map[string]string{"ca.crt": testIssuerCA}, + }, + }, + wantLogs: []string{ + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"ClientCredentialsSecretValid","status":"True","reason":"Success","message":"loaded client credentials"}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","status":"True","reason":"Success","message":"discovered issuer configuration"}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: loaded TLS configuration"}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"AdditionalAuthorizeParametersValid","status":"True","reason":"Success","message":"additionalAuthorizeParameters parameter names are allowed"}`, + }, + wantResultingCache: []*oidctestutil.TestUpstreamOIDCIdentityProvider{ + { + Name: testName, + ClientID: testClientID, + AuthorizationURL: *testIssuerAuthorizeURL, + RevocationURL: testIssuerRevocationURL, + Scopes: testDefaultExpectedScopes, + UsernameClaim: testUsernameClaim, + GroupsClaim: testGroupsClaim, + AllowPasswordGrant: false, + AdditionalAuthcodeParams: map[string]string{}, + AdditionalClaimMappings: nil, // Does not default to empty map + ResourceUID: testUID, + }, + }, + wantResultingUpstreams: []idpv1alpha1.OIDCIdentityProvider{{ + ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testName, Generation: 1234, UID: testUID}, + Status: idpv1alpha1.OIDCIdentityProviderStatus{ + Phase: "Ready", + Conditions: []metav1.Condition{ + {Type: "AdditionalAuthorizeParametersValid", Status: "True", LastTransitionTime: now, Reason: "Success", + Message: "additionalAuthorizeParameters parameter names are allowed", ObservedGeneration: 1234}, + {Type: "ClientCredentialsSecretValid", Status: "True", LastTransitionTime: now, Reason: "Success", + Message: "loaded client credentials", ObservedGeneration: 1234}, + {Type: "OIDCDiscoverySucceeded", Status: "True", LastTransitionTime: now, Reason: "Success", + Message: "discovered issuer configuration", ObservedGeneration: 1234}, + {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", + Message: "spec.tls is valid: loaded TLS configuration", ObservedGeneration: 1234}, }, }, }}, @@ -1312,12 +1201,14 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana Phase: "Ready", Conditions: []metav1.Condition{ happyAdditionalAuthorizeParametersValidConditionEarlier, - {Type: "ClientCredentialsSecretValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "loaded client credentials"}, - {Type: "OIDCDiscoverySucceeded", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "discovered issuer configuration"}, + {Type: "ClientCredentialsSecretValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", + Message: "loaded client credentials"}, + {Type: "OIDCDiscoverySucceeded", Status: "True", LastTransitionTime: earlier, Reason: "Success", + Message: "discovered issuer configuration"}, }, }, }}, - inputSecrets: []runtime.Object{&corev1.Secret{ + inputResources: []runtime.Object{&corev1.Secret{ ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testSecretName}, Type: "secrets.pinniped.dev/oidc-client", Data: testValidSecretData, @@ -1348,10 +1239,14 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana Status: idpv1alpha1.OIDCIdentityProviderStatus{ Phase: "Ready", Conditions: []metav1.Condition{ - {Type: "AdditionalAuthorizeParametersValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "additionalAuthorizeParameters parameter names are allowed", ObservedGeneration: 1234}, - {Type: "ClientCredentialsSecretValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "loaded client credentials", ObservedGeneration: 1234}, - {Type: "OIDCDiscoverySucceeded", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "discovered issuer configuration", ObservedGeneration: 1234}, - {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", Message: "spec.tls is valid: loaded TLS configuration", ObservedGeneration: 1234}, + {Type: "AdditionalAuthorizeParametersValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", + Message: "additionalAuthorizeParameters parameter names are allowed", ObservedGeneration: 1234}, + {Type: "ClientCredentialsSecretValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", + Message: "loaded client credentials", ObservedGeneration: 1234}, + {Type: "OIDCDiscoverySucceeded", Status: "True", LastTransitionTime: earlier, Reason: "Success", + Message: "discovered issuer configuration", ObservedGeneration: 1234}, + {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", + Message: "spec.tls is valid: loaded TLS configuration", ObservedGeneration: 1234}, }, }, }}, @@ -1373,12 +1268,14 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana Phase: "Ready", Conditions: []metav1.Condition{ happyAdditionalAuthorizeParametersValidConditionEarlier, - {Type: "ClientCredentialsSecretValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "loaded client credentials"}, - {Type: "OIDCDiscoverySucceeded", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "discovered issuer configuration"}, + {Type: "ClientCredentialsSecretValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", + Message: "loaded client credentials"}, + {Type: "OIDCDiscoverySucceeded", Status: "True", LastTransitionTime: earlier, Reason: "Success", + Message: "discovered issuer configuration"}, }, }, }}, - inputSecrets: []runtime.Object{&corev1.Secret{ + inputResources: []runtime.Object{&corev1.Secret{ ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testSecretName}, Type: "secrets.pinniped.dev/oidc-client", Data: testValidSecretData, @@ -1409,10 +1306,14 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana Status: idpv1alpha1.OIDCIdentityProviderStatus{ Phase: "Ready", Conditions: []metav1.Condition{ - {Type: "AdditionalAuthorizeParametersValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "additionalAuthorizeParameters parameter names are allowed", ObservedGeneration: 1234}, - {Type: "ClientCredentialsSecretValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "loaded client credentials", ObservedGeneration: 1234}, - {Type: "OIDCDiscoverySucceeded", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "discovered issuer configuration", ObservedGeneration: 1234}, - {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", Message: "spec.tls is valid: loaded TLS configuration", ObservedGeneration: 1234}, + {Type: "AdditionalAuthorizeParametersValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", + Message: "additionalAuthorizeParameters parameter names are allowed", ObservedGeneration: 1234}, + {Type: "ClientCredentialsSecretValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", + Message: "loaded client credentials", ObservedGeneration: 1234}, + {Type: "OIDCDiscoverySucceeded", Status: "True", LastTransitionTime: earlier, Reason: "Success", + Message: "discovered issuer configuration", ObservedGeneration: 1234}, + {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", + Message: "spec.tls is valid: loaded TLS configuration", ObservedGeneration: 1234}, }, }, }}, @@ -1442,12 +1343,14 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana Phase: "Ready", Conditions: []metav1.Condition{ happyAdditionalAuthorizeParametersValidConditionEarlier, - {Type: "ClientCredentialsSecretValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "loaded client credentials"}, - {Type: "OIDCDiscoverySucceeded", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "discovered issuer configuration"}, + {Type: "ClientCredentialsSecretValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", + Message: "loaded client credentials"}, + {Type: "OIDCDiscoverySucceeded", Status: "True", LastTransitionTime: earlier, Reason: "Success", + Message: "discovered issuer configuration"}, }, }, }}, - inputSecrets: []runtime.Object{&corev1.Secret{ + inputResources: []runtime.Object{&corev1.Secret{ ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testSecretName}, Type: "secrets.pinniped.dev/oidc-client", Data: testValidSecretData, @@ -1480,10 +1383,14 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana Status: idpv1alpha1.OIDCIdentityProviderStatus{ Phase: "Ready", Conditions: []metav1.Condition{ - {Type: "AdditionalAuthorizeParametersValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "additionalAuthorizeParameters parameter names are allowed", ObservedGeneration: 1234}, - {Type: "ClientCredentialsSecretValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "loaded client credentials", ObservedGeneration: 1234}, - {Type: "OIDCDiscoverySucceeded", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "discovered issuer configuration", ObservedGeneration: 1234}, - {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", Message: "spec.tls is valid: loaded TLS configuration", ObservedGeneration: 1234}, + {Type: "AdditionalAuthorizeParametersValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", + Message: "additionalAuthorizeParameters parameter names are allowed", ObservedGeneration: 1234}, + {Type: "ClientCredentialsSecretValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", + Message: "loaded client credentials", ObservedGeneration: 1234}, + {Type: "OIDCDiscoverySucceeded", Status: "True", LastTransitionTime: earlier, Reason: "Success", + Message: "discovered issuer configuration", ObservedGeneration: 1234}, + {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", + Message: "spec.tls is valid: loaded TLS configuration", ObservedGeneration: 1234}, }, }, }}, @@ -1512,7 +1419,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana }, }, }}, - inputSecrets: []runtime.Object{&corev1.Secret{ + inputResources: []runtime.Object{&corev1.Secret{ ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testSecretName}, Type: "secrets.pinniped.dev/oidc-client", Data: testValidSecretData, @@ -1534,9 +1441,12 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana {Type: "AdditionalAuthorizeParametersValid", Status: "False", LastTransitionTime: now, Reason: "DisallowedParameterName", Message: "the following additionalAuthorizeParameters are not allowed: " + "response_type,scope,client_id,state,nonce,code_challenge,code_challenge_method,redirect_uri,hd", ObservedGeneration: 1234}, - {Type: "ClientCredentialsSecretValid", Status: "True", LastTransitionTime: now, Reason: "Success", Message: "loaded client credentials", ObservedGeneration: 1234}, - {Type: "OIDCDiscoverySucceeded", Status: "True", LastTransitionTime: now, Reason: "Success", Message: "discovered issuer configuration", ObservedGeneration: 1234}, - {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", Message: "spec.tls is valid: loaded TLS configuration", ObservedGeneration: 1234}, + {Type: "ClientCredentialsSecretValid", Status: "True", LastTransitionTime: now, Reason: "Success", + Message: "loaded client credentials", ObservedGeneration: 1234}, + {Type: "OIDCDiscoverySucceeded", Status: "True", LastTransitionTime: now, Reason: "Success", + Message: "discovered issuer configuration", ObservedGeneration: 1234}, + {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", + Message: "spec.tls is valid: loaded TLS configuration", ObservedGeneration: 1234}, }, }, }}, @@ -1551,7 +1461,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana Client: idpv1alpha1.OIDCClient{SecretName: testSecretName}, }, }}, - inputSecrets: []runtime.Object{&corev1.Secret{ + inputResources: []runtime.Object{&corev1.Secret{ ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testSecretName}, Type: "secrets.pinniped.dev/oidc-client", Data: testValidSecretData, @@ -1572,28 +1482,12 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana Phase: "Error", Conditions: []metav1.Condition{ happyAdditionalAuthorizeParametersValidCondition, - { - Type: "ClientCredentialsSecretValid", - Status: "True", - LastTransitionTime: now, - Reason: "Success", - Message: "loaded client credentials", - }, - { - Type: "OIDCDiscoverySucceeded", - Status: "False", - LastTransitionTime: now, - Reason: "Unreachable", - Message: `failed to perform OIDC discovery against "` + testIssuerURL + `/ends-with-slash": -oidc: issuer did not match the issuer returned by provider, expected "` + testIssuerURL + `/ends-with-slash" got "` + testIssuerURL + `/ends-with-slash/"`, - }, - { - Type: "TLSConfigurationValid", - Status: "True", - LastTransitionTime: now, - Reason: "Success", - Message: "spec.tls is valid: loaded TLS configuration", - }, + {Type: "ClientCredentialsSecretValid", Status: "True", LastTransitionTime: now, Reason: "Success", + Message: "loaded client credentials"}, + {Type: "OIDCDiscoverySucceeded", Status: "False", LastTransitionTime: now, Reason: "Unreachable", + Message: `failed to perform OIDC discovery against "` + testIssuerURL + `/ends-with-slash":` + "\n" + `oidc: issuer did not match the issuer returned by provider, expected "` + testIssuerURL + `/ends-with-slash" got "` + testIssuerURL + `/ends-with-slash/"`}, + {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", + Message: "spec.tls is valid: loaded TLS configuration"}, }, }, }}, @@ -1608,7 +1502,7 @@ oidc: issuer did not match the issuer returned by provider, expected "` + testIs Client: idpv1alpha1.OIDCClient{SecretName: testSecretName}, }, }}, - inputSecrets: []runtime.Object{&corev1.Secret{ + inputResources: []runtime.Object{&corev1.Secret{ ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testSecretName}, Type: "secrets.pinniped.dev/oidc-client", Data: testValidSecretData, @@ -1629,28 +1523,13 @@ oidc: issuer did not match the issuer returned by provider, expected "` + testIs Phase: "Error", Conditions: []metav1.Condition{ happyAdditionalAuthorizeParametersValidCondition, - { - Type: "ClientCredentialsSecretValid", - Status: "True", - LastTransitionTime: now, - Reason: "Success", - Message: "loaded client credentials", - }, - { - Type: "OIDCDiscoverySucceeded", - Status: "False", - LastTransitionTime: now, - Reason: "Unreachable", - Message: `failed to perform OIDC discovery against "` + testIssuerURL + `/": -oidc: issuer did not match the issuer returned by provider, expected "` + testIssuerURL + `/" got "` + testIssuerURL + `"`, - }, - { - Type: "TLSConfigurationValid", - Status: "True", - LastTransitionTime: now, - Reason: "Success", - Message: "spec.tls is valid: loaded TLS configuration", - }, + {Type: "ClientCredentialsSecretValid", Status: "True", LastTransitionTime: now, Reason: "Success", + Message: "loaded client credentials"}, + {Type: "OIDCDiscoverySucceeded", Status: "False", LastTransitionTime: now, Reason: "Unreachable", + Message: `failed to perform OIDC discovery against "` + testIssuerURL + `/":` + "\n" + + `oidc: issuer did not match the issuer returned by provider, expected "` + testIssuerURL + `/" got "` + testIssuerURL + `"`}, + {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", + Message: "spec.tls is valid: loaded TLS configuration"}, }, }, }}, @@ -1661,7 +1540,7 @@ oidc: issuer did not match the issuer returned by provider, expected "` + testIs t.Parallel() fakePinnipedClient := supervisorfake.NewSimpleClientset(tt.inputUpstreams...) pinnipedInformers := supervisorinformers.NewSharedInformerFactory(fakePinnipedClient, 0) - fakeKubeClient := fake.NewSimpleClientset(tt.inputSecrets...) + fakeKubeClient := fake.NewSimpleClientset(tt.inputResources...) kubeInformers := informers.NewSharedInformerFactory(fakeKubeClient, 0) cache := dynamicupstreamprovider.NewDynamicUpstreamIDPProvider() cache.SetOIDCIdentityProviders([]upstreamprovider.UpstreamOIDCIdentityProviderI{ From d5e3ad9da0be00be7d80d57162d65d6bb187540a Mon Sep 17 00:00:00 2001 From: Joshua Casey Date: Tue, 23 Jul 2024 13:55:10 -0500 Subject: [PATCH 32/99] Concierge external TLS static integration tests use the real URL of the deployed local-user-authenticator --- test/integration/concierge_tls_spec_test.go | 26 +++++++++++---------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/test/integration/concierge_tls_spec_test.go b/test/integration/concierge_tls_spec_test.go index cb59f4339..8ecca6416 100644 --- a/test/integration/concierge_tls_spec_test.go +++ b/test/integration/concierge_tls_spec_test.go @@ -23,13 +23,15 @@ import ( // in Pinniped concierge CRDs using WebhookAuthenticator as an example. func TestTLSSpecKubeBuilderValidationConcierge_Parallel(t *testing.T) { env := testlib.IntegrationEnv(t) + + localUserAuthenticatorEndpoint := env.TestWebhook.Endpoint + testCases := []struct { name string customResourceYaml string customResourceName string expectedError string }{ - // TODO: these "spec.endpoint" could use the real URL of the local-user-authenticator // TODO: should we repeat these tests using the JWTAuthenticator too? { name: "should disallow certificate authority data source with missing name", @@ -40,7 +42,7 @@ func TestTLSSpecKubeBuilderValidationConcierge_Parallel(t *testing.T) { metadata: name: %s spec: - endpoint: "https://web-auth/token" + endpoint: %s tls: certificateAuthorityDataSource: kind: Secret @@ -58,7 +60,7 @@ func TestTLSSpecKubeBuilderValidationConcierge_Parallel(t *testing.T) { metadata: name: %s spec: - endpoint: "https://web-auth/token" + endpoint: %s tls: certificateAuthorityDataSource: kind: Secret @@ -77,7 +79,7 @@ func TestTLSSpecKubeBuilderValidationConcierge_Parallel(t *testing.T) { metadata: name: %s spec: - endpoint: "https://web-auth/token" + endpoint: %s tls: certificateAuthorityDataSource: kind: Secret @@ -95,7 +97,7 @@ func TestTLSSpecKubeBuilderValidationConcierge_Parallel(t *testing.T) { metadata: name: %s spec: - endpoint: "https://web-auth/token" + endpoint: %s tls: certificateAuthorityDataSource: kind: Secret @@ -114,7 +116,7 @@ func TestTLSSpecKubeBuilderValidationConcierge_Parallel(t *testing.T) { metadata: name: %s spec: - endpoint: "https://web-auth/token" + endpoint: %s tls: certificateAuthorityDataSource: name: foo @@ -132,7 +134,7 @@ func TestTLSSpecKubeBuilderValidationConcierge_Parallel(t *testing.T) { metadata: name: %s spec: - endpoint: "https://web-auth/token" + endpoint: %s tls: certificateAuthorityDataSource: kind: "" @@ -151,7 +153,7 @@ func TestTLSSpecKubeBuilderValidationConcierge_Parallel(t *testing.T) { metadata: name: %s spec: - endpoint: "https://web-auth/token" + endpoint: %s tls: certificateAuthorityDataSource: kind: sorcery @@ -170,7 +172,7 @@ func TestTLSSpecKubeBuilderValidationConcierge_Parallel(t *testing.T) { metadata: name: %s spec: - endpoint: "https://web-auth/token" + endpoint: %s tls: certificateAuthorityDataSource: kind: Secret @@ -189,7 +191,7 @@ func TestTLSSpecKubeBuilderValidationConcierge_Parallel(t *testing.T) { metadata: name: %s spec: - endpoint: "https://web-auth/token" + endpoint: %s tls: certificateAuthorityDataSource: kind: ConfigMap @@ -208,7 +210,7 @@ func TestTLSSpecKubeBuilderValidationConcierge_Parallel(t *testing.T) { metadata: name: %s spec: - endpoint: "https://web-auth/token" + endpoint: %s `), customResourceName: "no-tls-spec", expectedError: "", @@ -221,7 +223,7 @@ func TestTLSSpecKubeBuilderValidationConcierge_Parallel(t *testing.T) { yamlFilepath := filepath.Join(t.TempDir(), fmt.Sprintf("tls-spec-validation-%s.yaml", tc.customResourceName)) resourceName := tc.customResourceName + "-" + testlib.RandHex(t, 7) - yamlBytes := []byte(fmt.Sprintf(tc.customResourceYaml, env.APIGroupSuffix, resourceName)) + yamlBytes := []byte(fmt.Sprintf(tc.customResourceYaml, env.APIGroupSuffix, resourceName, localUserAuthenticatorEndpoint)) require.NoError(t, os.WriteFile(yamlFilepath, yamlBytes, 0600)) From afec420ce6f97a14b175f0348388312fb4c05563 Mon Sep 17 00:00:00 2001 From: Joshua Casey Date: Tue, 23 Jul 2024 14:32:21 -0500 Subject: [PATCH 33/99] Add JWTAuthenticators to the static validation checks for concierge TLS spec --- test/integration/concierge_tls_spec_test.go | 292 +++++++++++++++----- 1 file changed, 230 insertions(+), 62 deletions(-) diff --git a/test/integration/concierge_tls_spec_test.go b/test/integration/concierge_tls_spec_test.go index 8ecca6416..00de622b0 100644 --- a/test/integration/concierge_tls_spec_test.go +++ b/test/integration/concierge_tls_spec_test.go @@ -6,13 +6,14 @@ import ( "bytes" "context" "fmt" + "net/url" "os" "os/exec" "path/filepath" + "regexp" "strings" "testing" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.pinniped.dev/internal/here" @@ -20,22 +21,20 @@ import ( ) // TestTLSSpecKubeBuilderValidationConcierge_Parallel tests kubebuilder validation on the TLSSpec -// in Pinniped concierge CRDs using WebhookAuthenticator as an example. +// in Pinniped concierge CRDs for both WebhookAuthenticators and JWTAuthenticators. func TestTLSSpecKubeBuilderValidationConcierge_Parallel(t *testing.T) { env := testlib.IntegrationEnv(t) - localUserAuthenticatorEndpoint := env.TestWebhook.Endpoint - testCases := []struct { - name string - customResourceYaml string - customResourceName string - expectedError string + name string + customWebhookAuthenticatorYaml string + customJWTAuthenticatorYaml string + resourceNamePrefix string + expectedError string }{ - // TODO: should we repeat these tests using the JWTAuthenticator too? { name: "should disallow certificate authority data source with missing name", - customResourceYaml: here.Doc(` + customWebhookAuthenticatorYaml: here.Doc(` --- apiVersion: authentication.concierge.%s/v1alpha1 kind: WebhookAuthenticator @@ -48,12 +47,26 @@ func TestTLSSpecKubeBuilderValidationConcierge_Parallel(t *testing.T) { kind: Secret key: bar `), - customResourceName: "invalid-webhook-auth-missing-name", - expectedError: `The WebhookAuthenticator "%s" is invalid: spec.tls.certificateAuthorityDataSource.name: Required value`, + customJWTAuthenticatorYaml: here.Doc(` + --- + apiVersion: authentication.concierge.%s/v1alpha1 + kind: JWTAuthenticator + metadata: + name: %s + spec: + issuer: %s + audience: some-audience + tls: + certificateAuthorityDataSource: + kind: Secret + key: bar + `), + resourceNamePrefix: "invalid-tls-spec-missing-name", + expectedError: `The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.name: Required value`, }, { name: "should disallow certificate authority data source with empty value for name", - customResourceYaml: here.Doc(` + customWebhookAuthenticatorYaml: here.Doc(` --- apiVersion: authentication.concierge.%s/v1alpha1 kind: WebhookAuthenticator @@ -67,12 +80,27 @@ func TestTLSSpecKubeBuilderValidationConcierge_Parallel(t *testing.T) { name: "" key: bar `), - customResourceName: "invalid-webhook-auth-empty-name", - expectedError: `The WebhookAuthenticator "%s" is invalid: spec.tls.certificateAuthorityDataSource.name: Invalid value: "": spec.tls.certificateAuthorityDataSource.name in body should be at least 1 chars long`, + customJWTAuthenticatorYaml: here.Doc(` + --- + apiVersion: authentication.concierge.%s/v1alpha1 + kind: JWTAuthenticator + metadata: + name: %s + spec: + issuer: %s + audience: some-audience + tls: + certificateAuthorityDataSource: + kind: Secret + name: "" + key: bar + `), + resourceNamePrefix: "invalid-tls-spec-empty-name", + expectedError: `The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.name: Invalid value: "": spec.tls.certificateAuthorityDataSource.name in body should be at least 1 chars long`, }, { name: "should disallow certificate authority data source with missing key", - customResourceYaml: here.Doc(` + customWebhookAuthenticatorYaml: here.Doc(` --- apiVersion: authentication.concierge.%s/v1alpha1 kind: WebhookAuthenticator @@ -85,12 +113,26 @@ func TestTLSSpecKubeBuilderValidationConcierge_Parallel(t *testing.T) { kind: Secret name: foo `), - customResourceName: "invalid-webhook-auth-missing-key", - expectedError: `The WebhookAuthenticator "%s" is invalid: spec.tls.certificateAuthorityDataSource.key: Required value`, + customJWTAuthenticatorYaml: here.Doc(` + --- + apiVersion: authentication.concierge.%s/v1alpha1 + kind: JWTAuthenticator + metadata: + name: %s + spec: + issuer: %s + audience: some-audience + tls: + certificateAuthorityDataSource: + kind: Secret + name: foo + `), + resourceNamePrefix: "invalid-tls-spec-missing-key", + expectedError: `The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.key: Required value`, }, { name: "should disallow certificate authority data source with empty value for key", - customResourceYaml: here.Doc(` + customWebhookAuthenticatorYaml: here.Doc(` --- apiVersion: authentication.concierge.%s/v1alpha1 kind: WebhookAuthenticator @@ -104,12 +146,27 @@ func TestTLSSpecKubeBuilderValidationConcierge_Parallel(t *testing.T) { name: foo key: "" `), - customResourceName: "invalid-webhook-auth-empty-kind", - expectedError: `The WebhookAuthenticator "%s" is invalid: spec.tls.certificateAuthorityDataSource.key: Invalid value: "": spec.tls.certificateAuthorityDataSource.key in body should be at least 1 chars long`, + customJWTAuthenticatorYaml: here.Doc(` + --- + apiVersion: authentication.concierge.%s/v1alpha1 + kind: JWTAuthenticator + metadata: + name: %s + spec: + issuer: %s + audience: some-audience + tls: + certificateAuthorityDataSource: + kind: Secret + name: foo + key: "" + `), + resourceNamePrefix: "invalid-tls-spec-empty-kind", + expectedError: `The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.key: Invalid value: "": spec.tls.certificateAuthorityDataSource.key in body should be at least 1 chars long`, }, { name: "should disallow certificate authority data source with missing kind", - customResourceYaml: here.Doc(` + customWebhookAuthenticatorYaml: here.Doc(` --- apiVersion: authentication.concierge.%s/v1alpha1 kind: WebhookAuthenticator @@ -122,12 +179,26 @@ func TestTLSSpecKubeBuilderValidationConcierge_Parallel(t *testing.T) { name: foo key: bar `), - customResourceName: "invalid-webhook-auth-missing-kind", - expectedError: `The WebhookAuthenticator "%s" is invalid: spec.tls.certificateAuthorityDataSource.kind: Required value`, + customJWTAuthenticatorYaml: here.Doc(` + --- + apiVersion: authentication.concierge.%s/v1alpha1 + kind: JWTAuthenticator + metadata: + name: %s + spec: + issuer: %s + audience: some-audience + tls: + certificateAuthorityDataSource: + name: foo + key: bar + `), + resourceNamePrefix: "invalid-tls-spec-missing-kind", + expectedError: `The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.kind: Required value`, }, { name: "should disallow certificate authority data source with empty value for kind", - customResourceYaml: here.Doc(` + customWebhookAuthenticatorYaml: here.Doc(` --- apiVersion: authentication.concierge.%s/v1alpha1 kind: WebhookAuthenticator @@ -141,12 +212,27 @@ func TestTLSSpecKubeBuilderValidationConcierge_Parallel(t *testing.T) { name: foo key: bar `), - customResourceName: "invalid-webhook-auth-invalid-kind", - expectedError: `The WebhookAuthenticator "%s" is invalid: spec.tls.certificateAuthorityDataSource.kind: Unsupported value: "": supported values: "Secret", "ConfigMap"`, + customJWTAuthenticatorYaml: here.Doc(` + --- + apiVersion: authentication.concierge.%s/v1alpha1 + kind: JWTAuthenticator + metadata: + name: %s + spec: + issuer: %s + audience: some-audience + tls: + certificateAuthorityDataSource: + kind: "" + name: foo + key: bar + `), + resourceNamePrefix: "invalid-tls-spec-invalid-kind", + expectedError: `The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.kind: Unsupported value: "": supported values: "Secret", "ConfigMap"`, }, { name: "should disallow certificate authority data source with invalid kind", - customResourceYaml: here.Doc(` + customWebhookAuthenticatorYaml: here.Doc(` --- apiVersion: authentication.concierge.%s/v1alpha1 kind: WebhookAuthenticator @@ -160,12 +246,27 @@ func TestTLSSpecKubeBuilderValidationConcierge_Parallel(t *testing.T) { name: foo key: bar `), - customResourceName: "invalid-webhook-auth-invalid-kind", - expectedError: `The WebhookAuthenticator "%s" is invalid: spec.tls.certificateAuthorityDataSource.kind: Unsupported value: "sorcery": supported values: "Secret", "ConfigMap"`, + customJWTAuthenticatorYaml: here.Doc(` + --- + apiVersion: authentication.concierge.%s/v1alpha1 + kind: JWTAuthenticator + metadata: + name: %s + spec: + issuer: %s + audience: some-audience + tls: + certificateAuthorityDataSource: + kind: sorcery + name: foo + key: bar + `), + resourceNamePrefix: "invalid-tls-spec-invalid-kind", + expectedError: `The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.kind: Unsupported value: "sorcery": supported values: "Secret", "ConfigMap"`, }, { name: "should create a custom resource passing all validations using a Secret source", - customResourceYaml: here.Doc(` + customWebhookAuthenticatorYaml: here.Doc(` --- apiVersion: authentication.concierge.%s/v1alpha1 kind: WebhookAuthenticator @@ -178,13 +279,28 @@ func TestTLSSpecKubeBuilderValidationConcierge_Parallel(t *testing.T) { kind: Secret name: foo key: bar - `), - customResourceName: "valid-webhook-auth-secret-kind", + `), + customJWTAuthenticatorYaml: here.Doc(` + --- + apiVersion: authentication.concierge.%s/v1alpha1 + kind: JWTAuthenticator + metadata: + name: %s + spec: + issuer: %s + audience: some-audience + tls: + certificateAuthorityDataSource: + kind: Secret + name: foo + key: bar + `), + resourceNamePrefix: "valid-webhook-auth-secret-kind", expectedError: "", }, { name: "should create a custom resource passing all validations using a ConfigMap source", - customResourceYaml: here.Doc(` + customWebhookAuthenticatorYaml: here.Doc(` --- apiVersion: authentication.concierge.%s/v1alpha1 kind: WebhookAuthenticator @@ -198,12 +314,27 @@ func TestTLSSpecKubeBuilderValidationConcierge_Parallel(t *testing.T) { name: foo key: bar `), - customResourceName: "valid-webhook-auth-cm-kind", + customJWTAuthenticatorYaml: here.Doc(` + --- + apiVersion: authentication.concierge.%s/v1alpha1 + kind: JWTAuthenticator + metadata: + name: %s + spec: + issuer: %s + audience: some-audience + tls: + certificateAuthorityDataSource: + kind: ConfigMap + name: foo + key: bar + `), + resourceNamePrefix: "valid-webhook-auth-cm-kind", expectedError: "", }, { name: "should create a custom resource without any tls spec", - customResourceYaml: here.Doc(` + customWebhookAuthenticatorYaml: here.Doc(` --- apiVersion: authentication.concierge.%s/v1alpha1 kind: WebhookAuthenticator @@ -212,7 +343,17 @@ func TestTLSSpecKubeBuilderValidationConcierge_Parallel(t *testing.T) { spec: endpoint: %s `), - customResourceName: "no-tls-spec", + customJWTAuthenticatorYaml: here.Doc(` + --- + apiVersion: authentication.concierge.%s/v1alpha1 + kind: JWTAuthenticator + metadata: + name: %s + spec: + issuer: %s + audience: some-audience + `), + resourceNamePrefix: "no-tls-spec", expectedError: "", }, } @@ -220,36 +361,63 @@ func TestTLSSpecKubeBuilderValidationConcierge_Parallel(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { t.Parallel() - yamlFilepath := filepath.Join(t.TempDir(), fmt.Sprintf("tls-spec-validation-%s.yaml", tc.customResourceName)) - resourceName := tc.customResourceName + "-" + testlib.RandHex(t, 7) - yamlBytes := []byte(fmt.Sprintf(tc.customResourceYaml, env.APIGroupSuffix, resourceName, localUserAuthenticatorEndpoint)) + t.Run("apply webhook authenticator", func(t *testing.T) { + webhookResourceName := tc.resourceNamePrefix + "-" + testlib.RandHex(t, 7) + webhookYamlBytes := []byte(fmt.Sprintf(tc.customWebhookAuthenticatorYaml, env.APIGroupSuffix, webhookResourceName, env.TestWebhook.Endpoint)) - require.NoError(t, os.WriteFile(yamlFilepath, yamlBytes, 0600)) - - // Use --validate=false to disable old client-side validations to avoid getting different error messages in Kube 1.24 and older. - // Note that this also disables validations of unknown and duplicate fields, but that's not what this test is about. - //nolint:gosec // this is test code. - cmd := exec.CommandContext(context.Background(), "kubectl", []string{"apply", "--validate=false", "-f", yamlFilepath}...) - - var stdOut, stdErr bytes.Buffer - cmd.Stdout = &stdOut - cmd.Stderr = &stdErr - err := cmd.Run() - - t.Cleanup(func() { - t.Helper() - //nolint:gosec // this is test code. - require.NoError(t, exec.Command("kubectl", []string{"delete", "--ignore-not-found", "-f", yamlFilepath}...).Run()) + performKubectlApply(t, webhookYamlBytes, tc.expectedError, "WebhookAuthenticator", webhookResourceName) }) - if tc.expectedError == "" { - assert.Empty(t, stdErr.String()) - assert.Equal(t, fmt.Sprintf("webhookauthenticator.authentication.concierge.pinniped.dev/%s created\n", resourceName), stdOut.String()) + t.Run("apply jwt authenticator", func(t *testing.T) { + issuerURL, err := url.Parse(env.SupervisorUpstreamOIDC.CallbackURL) require.NoError(t, err) - } else { - require.Equal(t, fmt.Sprintf(tc.expectedError, resourceName), strings.TrimSuffix(stdErr.String(), "\n")) - } + require.True(t, strings.HasSuffix(issuerURL.Path, "/callback")) + issuerURL.Path = strings.TrimSuffix(issuerURL.Path, "/callback") + + jwtAuthenticatorResourceName := tc.resourceNamePrefix + "-" + testlib.RandHex(t, 7) + jwtAuthenticatorYamlBytes := []byte(fmt.Sprintf(tc.customJWTAuthenticatorYaml, env.APIGroupSuffix, jwtAuthenticatorResourceName, issuerURL.String())) + + performKubectlApply(t, jwtAuthenticatorYamlBytes, tc.expectedError, "JWTAuthenticator", jwtAuthenticatorResourceName) + }) }) } } + +func performKubectlApply( + t *testing.T, + yamlBytes []byte, + expectedError string, + resourceType string, + resourceName string, +) { + t.Helper() + + yamlFilepath := filepath.Join(t.TempDir(), fmt.Sprintf("tls-spec-validation-%s.yaml", resourceName)) + + require.NoError(t, os.WriteFile(yamlFilepath, yamlBytes, 0600)) + + // Use --validate=false to disable old client-side validations to avoid getting different error messages in Kube 1.24 and older. + // Note that this also disables validations of unknown and duplicate fields, but that's not what this test is about. + //nolint:gosec // this is test code. + cmd := exec.CommandContext(context.Background(), "kubectl", []string{"apply", "--validate=false", "-f", yamlFilepath}...) + + var stdOut, stdErr bytes.Buffer + cmd.Stdout = &stdOut + cmd.Stderr = &stdErr + err := cmd.Run() + + t.Cleanup(func() { + t.Helper() + //nolint:gosec // this is test code. + require.NoError(t, exec.Command("kubectl", []string{"delete", "--ignore-not-found", "-f", yamlFilepath}...).Run()) + }) + + if expectedError == "" { + require.Empty(t, stdErr.String()) + require.Regexp(t, "^(webhookauthenticator|jwtauthenticator)"+regexp.QuoteMeta(fmt.Sprintf(".authentication.concierge.pinniped.dev/%s created\n", resourceName)), stdOut.String()) + require.NoError(t, err) + } else { + require.Equal(t, fmt.Sprintf(expectedError, resourceType, resourceName), strings.TrimSuffix(stdErr.String(), "\n")) + } +} From 0f9352db3b45f446be262aee52b777dfd2f11727 Mon Sep 17 00:00:00 2001 From: Joshua Casey Date: Tue, 23 Jul 2024 14:43:38 -0500 Subject: [PATCH 34/99] Integration tests should use a helper func to infer Supervisor's downstream issuer URL --- test/integration/concierge_tls_spec_test.go | 8 ++------ test/integration/e2e_test.go | 7 +------ test/integration/supervisor_login_test.go | 7 +------ test/integration/supervisor_tls_spec_test.go | 1 + test/integration/supervisor_warnings_test.go | 8 +------- test/testlib/env.go | 15 +++++++++++++++ 6 files changed, 21 insertions(+), 25 deletions(-) diff --git a/test/integration/concierge_tls_spec_test.go b/test/integration/concierge_tls_spec_test.go index 00de622b0..c9ec84849 100644 --- a/test/integration/concierge_tls_spec_test.go +++ b/test/integration/concierge_tls_spec_test.go @@ -6,7 +6,6 @@ import ( "bytes" "context" "fmt" - "net/url" "os" "os/exec" "path/filepath" @@ -370,13 +369,10 @@ func TestTLSSpecKubeBuilderValidationConcierge_Parallel(t *testing.T) { }) t.Run("apply jwt authenticator", func(t *testing.T) { - issuerURL, err := url.Parse(env.SupervisorUpstreamOIDC.CallbackURL) - require.NoError(t, err) - require.True(t, strings.HasSuffix(issuerURL.Path, "/callback")) - issuerURL.Path = strings.TrimSuffix(issuerURL.Path, "/callback") + _, supervisorIssuer := env.SupervisorUpstreamOIDC.InferTheIssuerURL(t) jwtAuthenticatorResourceName := tc.resourceNamePrefix + "-" + testlib.RandHex(t, 7) - jwtAuthenticatorYamlBytes := []byte(fmt.Sprintf(tc.customJWTAuthenticatorYaml, env.APIGroupSuffix, jwtAuthenticatorResourceName, issuerURL.String())) + jwtAuthenticatorYamlBytes := []byte(fmt.Sprintf(tc.customJWTAuthenticatorYaml, env.APIGroupSuffix, jwtAuthenticatorResourceName, supervisorIssuer)) performKubectlApply(t, jwtAuthenticatorYamlBytes, tc.expectedError, "JWTAuthenticator", jwtAuthenticatorResourceName) }) diff --git a/test/integration/e2e_test.go b/test/integration/e2e_test.go index 862a1665b..935b6d5ea 100644 --- a/test/integration/e2e_test.go +++ b/test/integration/e2e_test.go @@ -70,12 +70,7 @@ func TestE2EFullIntegration_Browser(t *testing.T) { // Build pinniped CLI. pinnipedExe := testlib.PinnipedCLIPath(t) - // Infer the downstream issuer URL from the callback associated with the upstream test client registration. - issuerURL, err := url.Parse(env.SupervisorUpstreamOIDC.CallbackURL) - require.NoError(t, err) - require.True(t, strings.HasSuffix(issuerURL.Path, "/callback")) - issuerURL.Path = strings.TrimSuffix(issuerURL.Path, "/callback") - t.Logf("testing with downstream issuer URL %s", issuerURL.String()) + issuerURL, _ := env.SupervisorUpstreamOIDC.InferTheIssuerURL(t) // Generate a CA bundle with which to serve this provider. t.Logf("generating test CA") diff --git a/test/integration/supervisor_login_test.go b/test/integration/supervisor_login_test.go index 089a3bbe3..6555143fa 100644 --- a/test/integration/supervisor_login_test.go +++ b/test/integration/supervisor_login_test.go @@ -2948,12 +2948,7 @@ func testSupervisorLogin( ctx, cancel := context.WithTimeout(context.Background(), 7*time.Minute) defer cancel() - // Infer the downstream issuer URL from the callback associated with the upstream test client registration. - issuerURL, err := url.Parse(env.SupervisorUpstreamOIDC.CallbackURL) - require.NoError(t, err) - require.True(t, strings.HasSuffix(issuerURL.Path, "/callback")) - issuerURL.Path = strings.TrimSuffix(issuerURL.Path, "/callback") - t.Logf("testing with downstream issuer URL %s", issuerURL.String()) + issuerURL, _ := env.SupervisorUpstreamOIDC.InferTheIssuerURL(t) // Generate a CA bundle with which to serve this provider. t.Logf("generating test CA") diff --git a/test/integration/supervisor_tls_spec_test.go b/test/integration/supervisor_tls_spec_test.go index 877a85e02..5ac34b4be 100644 --- a/test/integration/supervisor_tls_spec_test.go +++ b/test/integration/supervisor_tls_spec_test.go @@ -23,6 +23,7 @@ import ( // on the TLSSpec in Pinniped supervisor CRDs using OIDCIdentityProvider as an example. func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { env := testlib.IntegrationEnv(t) + env.SupervisorUpstreamOIDC.Issuer testCases := []struct { name string customResourceYaml string diff --git a/test/integration/supervisor_warnings_test.go b/test/integration/supervisor_warnings_test.go index 17da03367..a2a6d4536 100644 --- a/test/integration/supervisor_warnings_test.go +++ b/test/integration/supervisor_warnings_test.go @@ -7,7 +7,6 @@ import ( "encoding/base64" "fmt" "io" - "net/url" "os" "os/exec" "path/filepath" @@ -49,12 +48,7 @@ func TestSupervisorWarnings_Browser(t *testing.T) { pinnipedExe := testlib.PinnipedCLIPath(t) tempDir := t.TempDir() - // Infer the downstream issuer URL from the callback associated with the upstream test client registration. - issuerURL, err := url.Parse(env.SupervisorUpstreamOIDC.CallbackURL) - require.NoError(t, err) - require.True(t, strings.HasSuffix(issuerURL.Path, "/callback")) - issuerURL.Path = strings.TrimSuffix(issuerURL.Path, "/callback") - t.Logf("testing with downstream issuer URL %s", issuerURL.String()) + issuerURL, _ := env.SupervisorUpstreamOIDC.InferTheIssuerURL(t) // Generate a CA bundle with which to serve this provider. t.Logf("generating test CA") diff --git a/test/testlib/env.go b/test/testlib/env.go index 5e83caa43..2e4e57611 100644 --- a/test/testlib/env.go +++ b/test/testlib/env.go @@ -5,6 +5,7 @@ package testlib import ( "encoding/base64" + "net/url" "os" "sort" "strings" @@ -83,6 +84,20 @@ type TestOIDCUpstream struct { ExpectedGroups []string `json:"expectedGroups"` } +// InferTheIssuerURL infers the downstream issuer URL from the callback associated with the upstream test client registration. +func (upstream *TestOIDCUpstream) InferTheIssuerURL(t *testing.T) (*url.URL, string) { + t.Helper() + issuerURL, err := url.Parse(upstream.CallbackURL) + require.NoError(t, err) + require.True(t, strings.HasSuffix(issuerURL.Path, "/callback")) + issuerURL.Path = strings.TrimSuffix(issuerURL.Path, "/callback") + + issuerAsString := issuerURL.String() + t.Logf("testing with downstream issuer URL %s", issuerAsString) + + return issuerURL, issuerAsString +} + type TestLDAPUpstream struct { Host string `json:"host"` Domain string `json:"domain"` From d74c2a6e3fe512a759896b67b050b4070bc6be16 Mon Sep 17 00:00:00 2001 From: Joshua Casey Date: Tue, 23 Jul 2024 15:12:26 -0500 Subject: [PATCH 35/99] Supervisor TLS spec integration tests should use an OIDC issuer url from the test environment --- test/integration/supervisor_tls_spec_test.go | 26 +++++++++----------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/test/integration/supervisor_tls_spec_test.go b/test/integration/supervisor_tls_spec_test.go index 5ac34b4be..288b53777 100644 --- a/test/integration/supervisor_tls_spec_test.go +++ b/test/integration/supervisor_tls_spec_test.go @@ -23,15 +23,13 @@ import ( // on the TLSSpec in Pinniped supervisor CRDs using OIDCIdentityProvider as an example. func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { env := testlib.IntegrationEnv(t) - env.SupervisorUpstreamOIDC.Issuer testCases := []struct { name string customResourceYaml string customResourceName string expectedError string }{ - // TODO: use the OIDC provider from env instead of bar.com - // TODO: make ths a loop to also run the same tests on LDAP, AD, GitHub?? + // TODO: make this a loop to also run the same tests on LDAP, AD, GitHub?? { name: "should disallow certificate authority data source with missing name", customResourceYaml: here.Doc(` @@ -45,7 +43,7 @@ func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { certificateAuthorityDataSource: kind: Secret key: bar - issuer: https://foo.bar.com/oauth2/default + issuer: %s authorizationConfig: additionalScopes: [offline_access, email] allowPasswordGrant: true @@ -69,7 +67,7 @@ func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { kind: Secret name: "" key: bar - issuer: https://foo.bar.com/oauth2/default + issuer: %s authorizationConfig: additionalScopes: [offline_access, email] allowPasswordGrant: true @@ -92,7 +90,7 @@ func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { certificateAuthorityDataSource: kind: Secret name: foo - issuer: https://foo.bar.com/oauth2/default + issuer: %s authorizationConfig: additionalScopes: [offline_access, email] allowPasswordGrant: true @@ -116,7 +114,7 @@ func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { kind: Secret name: foo key: "" - issuer: https://foo.bar.com/oauth2/default + issuer: %s authorizationConfig: additionalScopes: [offline_access, email] allowPasswordGrant: true @@ -139,7 +137,7 @@ func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { certificateAuthorityDataSource: name: foo key: bar - issuer: https://foo.bar.com/oauth2/default + issuer: %s authorizationConfig: additionalScopes: [offline_access, email] allowPasswordGrant: true @@ -163,7 +161,7 @@ func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { kind: "" name: foo key: bar - issuer: https://foo.bar.com/oauth2/default + issuer: %s authorizationConfig: additionalScopes: [offline_access, email] allowPasswordGrant: true @@ -187,7 +185,7 @@ func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { kind: sorcery name: foo key: bar - issuer: https://foo.bar.com/oauth2/default + issuer: %s authorizationConfig: additionalScopes: [offline_access, email] allowPasswordGrant: true @@ -211,7 +209,7 @@ func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { kind: Secret name: foo key: bar - issuer: https://foo.bar.com/oauth2/default + issuer: %s authorizationConfig: additionalScopes: [offline_access, email] allowPasswordGrant: true @@ -235,7 +233,7 @@ func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { kind: ConfigMap name: foo key: bar - issuer: https://foo.bar.com/oauth2/default + issuer: %s authorizationConfig: additionalScopes: [offline_access, email] allowPasswordGrant: true @@ -254,7 +252,7 @@ func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { metadata: name: %s spec: - issuer: https://foo.bar.com/oauth2/default + issuer: %s authorizationConfig: additionalScopes: [offline_access, email] allowPasswordGrant: true @@ -272,7 +270,7 @@ func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { yamlFilepath := filepath.Join(t.TempDir(), fmt.Sprintf("tls-spec-validation-%s.yaml", tc.customResourceName)) resourceName := tc.customResourceName + "-" + testlib.RandHex(t, 7) - yamlBytes := []byte(fmt.Sprintf(tc.customResourceYaml, env.APIGroupSuffix, resourceName)) + yamlBytes := []byte(fmt.Sprintf(tc.customResourceYaml, env.APIGroupSuffix, resourceName, env.SupervisorUpstreamOIDC.Issuer)) require.NoError(t, os.WriteFile(yamlFilepath, yamlBytes, 0600)) From 09724cfa712bd7aced2854540a0d0ebe39cd5529 Mon Sep 17 00:00:00 2001 From: Ryan Richard Date: Tue, 23 Jul 2024 13:40:13 -0700 Subject: [PATCH 36/99] Add unit test: when discovery is already cached for OIDCIdentityProvider --- .../oidc_upstream_watcher.go | 13 +- .../oidc_upstream_watcher_test.go | 111 ++++++++++++++++++ internal/supervisor/server/server.go | 1 + 3 files changed, 119 insertions(+), 6 deletions(-) diff --git a/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher.go b/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher.go index 9a73aea91..a2a85f21c 100644 --- a/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher.go +++ b/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher.go @@ -109,8 +109,8 @@ type oidcDiscoveryCacheValue struct { // oidcDiscoveryCache caches the discovered provider along with the http Client to use for making calls to that provider, // for a particular combination OIDC issuer and CA bundle for that issuer. type oidcDiscoveryCache interface { - getProvider(*oidcDiscoveryCacheKey) *oidcDiscoveryCacheValue - putProvider(*oidcDiscoveryCacheKey, *oidcDiscoveryCacheValue) + getProvider(oidcDiscoveryCacheKey) *oidcDiscoveryCacheValue + putProvider(oidcDiscoveryCacheKey, *oidcDiscoveryCacheValue) } // ttlProviderCache caches the *coreosoidc.Provider associated with a particular issuer/TLS configuration, @@ -121,7 +121,7 @@ type ttlProviderCache struct{ cache *cache.Expiring } var _ oidcDiscoveryCache = (*ttlProviderCache)(nil) // getProvider gets an entry from the ttlProviderCache. -func (c *ttlProviderCache) getProvider(key *oidcDiscoveryCacheKey) *oidcDiscoveryCacheValue { +func (c *ttlProviderCache) getProvider(key oidcDiscoveryCacheKey) *oidcDiscoveryCacheValue { if result, ok := c.cache.Get(key); ok { entry := result.(*oidcDiscoveryCacheValue) return entry @@ -130,7 +130,7 @@ func (c *ttlProviderCache) getProvider(key *oidcDiscoveryCacheKey) *oidcDiscover } // putProvider adds to the ttlProviderCache for a limited period of time. -func (c *ttlProviderCache) putProvider(key *oidcDiscoveryCacheKey, value *oidcDiscoveryCacheValue) { +func (c *ttlProviderCache) putProvider(key oidcDiscoveryCacheKey, value *oidcDiscoveryCacheValue) { c.cache.Set(key, value, oidcValidatorCacheTTL) } @@ -153,6 +153,7 @@ func New( configMapInformer corev1informers.ConfigMapInformer, log plog.Logger, withInformer pinnipedcontroller.WithInformerOptionFunc, + validatorCache *cache.Expiring, ) controllerlib.Controller { c := oidcWatcherController{ cache: idpCache, @@ -161,7 +162,7 @@ func New( oidcIdentityProviderInformer: oidcIdentityProviderInformer, secretInformer: secretInformer, configMapInformer: configMapInformer, - validatorCache: &ttlProviderCache{cache: cache.NewExpiring()}, + validatorCache: &ttlProviderCache{cache: validatorCache}, } return controllerlib.New( controllerlib.Config{Name: oidcControllerName, Syncer: &c}, @@ -357,7 +358,7 @@ func (c *oidcWatcherController) validateIssuer(ctx context.Context, upstream *id var httpClient *http.Client // Get the discovered provider and HTTP client from cache, if they are found in the cache. - cacheKey := &oidcDiscoveryCacheKey{ + cacheKey := oidcDiscoveryCacheKey{ issuer: upstream.Spec.Issuer, caBundleHash: sha256.Sum256(caBundlePEM), // note that this will always return the same hash for nil input } diff --git a/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher_test.go b/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher_test.go index c3ea5fda2..ed5968185 100644 --- a/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher_test.go +++ b/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher_test.go @@ -6,6 +6,8 @@ package oidcupstreamwatcher import ( "bytes" "context" + "crypto/sha256" + "crypto/x509" "encoding/base64" "encoding/json" "net/http" @@ -15,11 +17,13 @@ import ( "testing" "time" + coreosoidc "github.com/coreos/go-oidc/v3/oidc" "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" + expiringcache "k8s.io/apimachinery/pkg/util/cache" "k8s.io/apimachinery/pkg/util/net" "k8s.io/client-go/informers" "k8s.io/client-go/kubernetes/fake" @@ -31,6 +35,7 @@ import ( "go.pinniped.dev/internal/controllerlib" "go.pinniped.dev/internal/federationdomain/dynamicupstreamprovider" "go.pinniped.dev/internal/federationdomain/upstreamprovider" + "go.pinniped.dev/internal/net/phttp" "go.pinniped.dev/internal/plog" "go.pinniped.dev/internal/testutil" "go.pinniped.dev/internal/testutil/oidctestutil" @@ -119,6 +124,7 @@ func TestOIDCUpstreamWatcherControllerFilterSecret(t *testing.T) { configMapInformer, logger, withInformer.WithInformer, + expiringcache.NewExpiring(), ) unrelated := corev1.Secret{} @@ -178,6 +184,7 @@ func TestOIDCUpstreamWatcherControllerFilterConfigMaps(t *testing.T) { configMapInformer, logger, withInformer.WithInformer, + expiringcache.NewExpiring(), ) unrelated := corev1.ConfigMap{} @@ -237,6 +244,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { name string inputUpstreams []runtime.Object inputResources []runtime.Object + inputValidatorCache func(*testing.T) map[oidcDiscoveryCacheKey]*oidcDiscoveryCacheValue wantErr string wantLogs []string wantResultingCache []*oidctestutil.TestUpstreamOIDCIdentityProvider @@ -1054,6 +1062,99 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { }, }}, }, + { + name: "valid upstream which already exists in the OIDC discovery validation cache, should skip performing OIDC discovery again and just use cached discovery results", + inputUpstreams: []runtime.Object{&idpv1alpha1.OIDCIdentityProvider{ + ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testName, Generation: 1234, UID: testUID}, + Spec: idpv1alpha1.OIDCIdentityProviderSpec{ + Issuer: testIssuerURL + "/this-path-does-not-exist", + TLS: &idpv1alpha1.TLSSpec{CertificateAuthorityData: testIssuerCABase64}, + Client: idpv1alpha1.OIDCClient{SecretName: testSecretName}, + Claims: idpv1alpha1.OIDCClaims{Groups: testGroupsClaim, Username: testUsernameClaim}, + }, + Status: idpv1alpha1.OIDCIdentityProviderStatus{ + Phase: "Ready", + // Was previously validated, so already has conditions. + Conditions: []metav1.Condition{ + {Type: "AdditionalAuthorizeParametersValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", + Message: "additionalAuthorizeParameters parameter names are allowed", ObservedGeneration: 1234}, + {Type: "ClientCredentialsSecretValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", + Message: "loaded client credentials", ObservedGeneration: 1234}, + {Type: "OIDCDiscoverySucceeded", Status: "True", LastTransitionTime: earlier, Reason: "Success", + Message: "discovered issuer configuration", ObservedGeneration: 1234}, + {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", + Message: "spec.tls is valid: loaded TLS configuration", ObservedGeneration: 1234}, + }, + }, + }}, + inputResources: []runtime.Object{&corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testSecretName}, + Type: "secrets.pinniped.dev/oidc-client", + Data: testValidSecretData, + }}, + inputValidatorCache: func(t *testing.T) map[oidcDiscoveryCacheKey]*oidcDiscoveryCacheValue { + // Create a working OIDC discovery validator cache entry for the working issuer and CA bundle. + certPool := x509.NewCertPool() + require.True(t, certPool.AppendCertsFromPEM([]byte(testIssuerCA))) + httpClient := phttp.Default(certPool) + httpClient.Timeout = time.Minute // same timeout as in the production code + testCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + t.Cleanup(cancel) + // Really do OIDC discovery, so we can put the real result into the cache. + discoveredProvider, err := coreosoidc.NewProvider(coreosoidc.ClientContext(testCtx, httpClient), testIssuerURL) + require.NoError(t, err) + cacheValue := &oidcDiscoveryCacheValue{ + provider: discoveredProvider, + client: httpClient, + } + // Create the cache key to use with the above entry, and cache it at the issuer value that was + // configured in the OIDCIdentityProvider. If the production code tries to perform OIDC discovery + // on that URL, it will fail with a 404. But if the production code correctly reads the pre-cached + // discovery result from this cache, then it should skip discovery and use the value from this cache + // without encountering any errors. + cacheKey := oidcDiscoveryCacheKey{ + issuer: testIssuerURL + "/this-path-does-not-exist", + caBundleHash: sha256.Sum256([]byte(testIssuerCA)), + } + // Put it into the initial cache for this test. + return map[oidcDiscoveryCacheKey]*oidcDiscoveryCacheValue{ + cacheKey: cacheValue, + } + }, + wantLogs: []string{}, + wantResultingCache: []*oidctestutil.TestUpstreamOIDCIdentityProvider{ + { + Name: testName, + ClientID: testClientID, + AuthorizationURL: *testIssuerAuthorizeURL, + RevocationURL: testIssuerRevocationURL, + Scopes: testDefaultExpectedScopes, + UsernameClaim: testUsernameClaim, + GroupsClaim: testGroupsClaim, + AllowPasswordGrant: false, + AdditionalAuthcodeParams: map[string]string{}, + AdditionalClaimMappings: nil, // Does not default to empty map + ResourceUID: testUID, + }, + }, + wantResultingUpstreams: []idpv1alpha1.OIDCIdentityProvider{{ + ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testName, Generation: 1234, UID: testUID}, + Status: idpv1alpha1.OIDCIdentityProviderStatus{ + Phase: "Ready", + // Conditions are unchanged. + Conditions: []metav1.Condition{ + {Type: "AdditionalAuthorizeParametersValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", + Message: "additionalAuthorizeParameters parameter names are allowed", ObservedGeneration: 1234}, + {Type: "ClientCredentialsSecretValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", + Message: "loaded client credentials", ObservedGeneration: 1234}, + {Type: "OIDCDiscoverySucceeded", Status: "True", LastTransitionTime: earlier, Reason: "Success", + Message: "discovered issuer configuration", ObservedGeneration: 1234}, + {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", + Message: "spec.tls is valid: loaded TLS configuration", ObservedGeneration: 1234}, + }, + }, + }}, + }, { name: "valid upstream with CA bundle read from a Secret", inputUpstreams: []runtime.Object{&idpv1alpha1.OIDCIdentityProvider{ @@ -1550,6 +1651,15 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { var log bytes.Buffer logger := plog.TestLogger(t, &log) + validatorCache := expiringcache.NewExpiring() + if tt.inputValidatorCache != nil { + oidcValidatorCache := &ttlProviderCache{cache: validatorCache} + // add to the underlying validatorCache using oidcValidatorCache which wraps it + for key, value := range tt.inputValidatorCache(t) { + oidcValidatorCache.putProvider(key, value) + } + } + controller := New( cache, fakePinnipedClient, @@ -1558,6 +1668,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { kubeInformers.Core().V1().ConfigMaps(), logger, controllerlib.WithInformer, + validatorCache, ) ctx, cancel := context.WithCancel(context.Background()) diff --git a/internal/supervisor/server/server.go b/internal/supervisor/server/server.go index 15c75e378..c2fdceb4b 100644 --- a/internal/supervisor/server/server.go +++ b/internal/supervisor/server/server.go @@ -308,6 +308,7 @@ func prepareControllers( configMapInformer, plog.New(), controllerlib.WithInformer, + cache.NewExpiring(), ), singletonWorker). WithController( From 3a303cc8fbc1f0c3c5c3a41ab07987066b498d3d Mon Sep 17 00:00:00 2001 From: Joshua Casey Date: Tue, 23 Jul 2024 15:41:46 -0500 Subject: [PATCH 37/99] Supervisor TLS Spec validation integration tests should use helper method --- test/integration/concierge_tls_spec_test.go | 21 ++++++-- test/integration/supervisor_tls_spec_test.go | 50 ++++---------------- 2 files changed, 26 insertions(+), 45 deletions(-) diff --git a/test/integration/concierge_tls_spec_test.go b/test/integration/concierge_tls_spec_test.go index c9ec84849..c4aab15ea 100644 --- a/test/integration/concierge_tls_spec_test.go +++ b/test/integration/concierge_tls_spec_test.go @@ -365,7 +365,14 @@ func TestTLSSpecKubeBuilderValidationConcierge_Parallel(t *testing.T) { webhookResourceName := tc.resourceNamePrefix + "-" + testlib.RandHex(t, 7) webhookYamlBytes := []byte(fmt.Sprintf(tc.customWebhookAuthenticatorYaml, env.APIGroupSuffix, webhookResourceName, env.TestWebhook.Endpoint)) - performKubectlApply(t, webhookYamlBytes, tc.expectedError, "WebhookAuthenticator", webhookResourceName) + performKubectlApply( + t, + webhookYamlBytes, + `webhookauthenticator.authentication.concierge.pinniped.dev`, + tc.expectedError, + "WebhookAuthenticator", + webhookResourceName, + ) }) t.Run("apply jwt authenticator", func(t *testing.T) { @@ -374,7 +381,14 @@ func TestTLSSpecKubeBuilderValidationConcierge_Parallel(t *testing.T) { jwtAuthenticatorResourceName := tc.resourceNamePrefix + "-" + testlib.RandHex(t, 7) jwtAuthenticatorYamlBytes := []byte(fmt.Sprintf(tc.customJWTAuthenticatorYaml, env.APIGroupSuffix, jwtAuthenticatorResourceName, supervisorIssuer)) - performKubectlApply(t, jwtAuthenticatorYamlBytes, tc.expectedError, "JWTAuthenticator", jwtAuthenticatorResourceName) + performKubectlApply( + t, + jwtAuthenticatorYamlBytes, + `jwtauthenticator.authentication.concierge.pinniped.dev`, + tc.expectedError, + "JWTAuthenticator", + jwtAuthenticatorResourceName, + ) }) }) } @@ -383,6 +397,7 @@ func TestTLSSpecKubeBuilderValidationConcierge_Parallel(t *testing.T) { func performKubectlApply( t *testing.T, yamlBytes []byte, + expectedSuccessPrefix string, expectedError string, resourceType string, resourceName string, @@ -411,7 +426,7 @@ func performKubectlApply( if expectedError == "" { require.Empty(t, stdErr.String()) - require.Regexp(t, "^(webhookauthenticator|jwtauthenticator)"+regexp.QuoteMeta(fmt.Sprintf(".authentication.concierge.pinniped.dev/%s created\n", resourceName)), stdOut.String()) + require.Regexp(t, regexp.QuoteMeta(expectedSuccessPrefix)+regexp.QuoteMeta(fmt.Sprintf("/%s created\n", resourceName)), stdOut.String()) require.NoError(t, err) } else { require.Equal(t, fmt.Sprintf(expectedError, resourceType, resourceName), strings.TrimSuffix(stdErr.String(), "\n")) diff --git a/test/integration/supervisor_tls_spec_test.go b/test/integration/supervisor_tls_spec_test.go index 288b53777..bb1edc937 100644 --- a/test/integration/supervisor_tls_spec_test.go +++ b/test/integration/supervisor_tls_spec_test.go @@ -3,18 +3,9 @@ package integration import ( - "bytes" - "context" "fmt" - "os" - "os/exec" - "path/filepath" - "strings" "testing" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "go.pinniped.dev/internal/here" "go.pinniped.dev/test/testlib" ) @@ -51,7 +42,7 @@ func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { secretName: foo-bar-client-credentials `), customResourceName: "invalid-oidc-idp-missing-name", - expectedError: `The OIDCIdentityProvider "%s" is invalid: spec.tls.certificateAuthorityDataSource.name: Required value`, + expectedError: `The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.name: Required value`, }, { name: "should disallow certificate authority data source with empty value for name", @@ -75,7 +66,7 @@ func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { secretName: foo-bar-client-credentials `), customResourceName: "invalid-oidc-idp-empty-name", - expectedError: `The OIDCIdentityProvider "%s" is invalid: spec.tls.certificateAuthorityDataSource.name: Invalid value: "": spec.tls.certificateAuthorityDataSource.name in body should be at least 1 chars long`, + expectedError: `The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.name: Invalid value: "": spec.tls.certificateAuthorityDataSource.name in body should be at least 1 chars long`, }, { name: "should disallow certificate authority data source with missing key", @@ -98,7 +89,7 @@ func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { secretName: foo-bar-client-credentials `), customResourceName: "invalid-oidc-idp-missing-key", - expectedError: `The OIDCIdentityProvider "%s" is invalid: spec.tls.certificateAuthorityDataSource.key: Required value`, + expectedError: `The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.key: Required value`, }, { name: "should disallow certificate authority data source with empty value for key", @@ -122,7 +113,7 @@ func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { secretName: foo-bar-client-credentials `), customResourceName: "invalid-oidc-idp-empty-key", - expectedError: `The OIDCIdentityProvider "%s" is invalid: spec.tls.certificateAuthorityDataSource.key: Invalid value: "": spec.tls.certificateAuthorityDataSource.key in body should be at least 1 chars long`, + expectedError: `The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.key: Invalid value: "": spec.tls.certificateAuthorityDataSource.key in body should be at least 1 chars long`, }, { name: "should disallow certificate authority data source with missing kind", @@ -145,7 +136,7 @@ func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { secretName: foo-bar-client-credentials `), customResourceName: "invalid-oidc-idp-missing-kind", - expectedError: `The OIDCIdentityProvider "%s" is invalid: spec.tls.certificateAuthorityDataSource.kind: Required value`, + expectedError: `The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.kind: Required value`, }, { name: "should disallow certificate authority data source with empty value kind", @@ -169,7 +160,7 @@ func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { secretName: foo-bar-client-credentials `), customResourceName: "invalid-oidc-idp-invalid-kind", - expectedError: `The OIDCIdentityProvider "%s" is invalid: spec.tls.certificateAuthorityDataSource.kind: Unsupported value: "": supported values: "Secret", "ConfigMap"`, + expectedError: `The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.kind: Unsupported value: "": supported values: "Secret", "ConfigMap"`, }, { name: "should disallow certificate authority data source with invalid kind", @@ -193,7 +184,7 @@ func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { secretName: foo-bar-client-credentials `), customResourceName: "invalid-oidc-idp-invalid-kind", - expectedError: `The OIDCIdentityProvider "%s" is invalid: spec.tls.certificateAuthorityDataSource.kind: Unsupported value: "sorcery": supported values: "Secret", "ConfigMap"`, + expectedError: `The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.kind: Unsupported value: "sorcery": supported values: "Secret", "ConfigMap"`, }, { name: "should create a custom resource passing all validations using a Secret source", @@ -267,36 +258,11 @@ func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { t.Parallel() - yamlFilepath := filepath.Join(t.TempDir(), fmt.Sprintf("tls-spec-validation-%s.yaml", tc.customResourceName)) resourceName := tc.customResourceName + "-" + testlib.RandHex(t, 7) yamlBytes := []byte(fmt.Sprintf(tc.customResourceYaml, env.APIGroupSuffix, resourceName, env.SupervisorUpstreamOIDC.Issuer)) - require.NoError(t, os.WriteFile(yamlFilepath, yamlBytes, 0600)) - - // Use --validate=false to disable old client-side validations to avoid getting different error messages in Kube 1.24 and older. - // Note that this also disables validations of unknown and duplicate fields, but that's not what this test is about. - //nolint:gosec // this is test code. - cmd := exec.CommandContext(context.Background(), "kubectl", []string{"apply", "--validate=false", "-f", yamlFilepath}...) - - var stdOut, stdErr bytes.Buffer - cmd.Stdout = &stdOut - cmd.Stderr = &stdErr - err := cmd.Run() - - t.Cleanup(func() { - t.Helper() - //nolint:gosec // this is test code. - require.NoError(t, exec.Command("kubectl", []string{"delete", "--ignore-not-found", "-f", yamlFilepath}...).Run()) - }) - - if tc.expectedError == "" { - assert.Empty(t, stdErr.String()) - assert.Equal(t, fmt.Sprintf("oidcidentityprovider.idp.supervisor.pinniped.dev/%s created\n", resourceName), stdOut.String()) - require.NoError(t, err) - } else { - require.Equal(t, fmt.Sprintf(tc.expectedError, resourceName), strings.TrimSuffix(stdErr.String(), "\n")) - } + performKubectlApply(t, yamlBytes, `oidcidentityprovider.idp.supervisor.pinniped.dev`, tc.expectedError, "OIDCIdentityProvider", resourceName) }) } } From f381c92f0bee402676d22e74f6dbf5c510ee212a Mon Sep 17 00:00:00 2001 From: Ryan Richard Date: Tue, 23 Jul 2024 14:25:44 -0700 Subject: [PATCH 38/99] Use templates to reduce duplication in concierge_tls_spec_test.go Co-authored-by: Joshua Casey --- test/integration/concierge_tls_spec_test.go | 309 ++++---------------- 1 file changed, 56 insertions(+), 253 deletions(-) diff --git a/test/integration/concierge_tls_spec_test.go b/test/integration/concierge_tls_spec_test.go index c4aab15ea..314cc7495 100644 --- a/test/integration/concierge_tls_spec_test.go +++ b/test/integration/concierge_tls_spec_test.go @@ -24,336 +24,132 @@ import ( func TestTLSSpecKubeBuilderValidationConcierge_Parallel(t *testing.T) { env := testlib.IntegrationEnv(t) + webhookAuthenticatorYamlTemplate := here.Doc(` + apiVersion: authentication.concierge.%s/v1alpha1 + kind: WebhookAuthenticator + metadata: + name: %s + spec: + endpoint: %s + %s + `) + + jwtAuthenticatorYamlTemplate := here.Doc(` + apiVersion: authentication.concierge.%s/v1alpha1 + kind: JWTAuthenticator + metadata: + name: %s + spec: + issuer: %s + audience: some-audience + %s + `) + testCases := []struct { - name string - customWebhookAuthenticatorYaml string - customJWTAuthenticatorYaml string - resourceNamePrefix string - expectedError string + name string + tlsYAML string + expectedError string }{ { name: "should disallow certificate authority data source with missing name", - customWebhookAuthenticatorYaml: here.Doc(` - --- - apiVersion: authentication.concierge.%s/v1alpha1 - kind: WebhookAuthenticator - metadata: - name: %s - spec: - endpoint: %s + tlsYAML: here.Doc(` tls: certificateAuthorityDataSource: kind: Secret key: bar `), - customJWTAuthenticatorYaml: here.Doc(` - --- - apiVersion: authentication.concierge.%s/v1alpha1 - kind: JWTAuthenticator - metadata: - name: %s - spec: - issuer: %s - audience: some-audience - tls: - certificateAuthorityDataSource: - kind: Secret - key: bar - `), - resourceNamePrefix: "invalid-tls-spec-missing-name", - expectedError: `The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.name: Required value`, + expectedError: `The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.name: Required value`, }, { name: "should disallow certificate authority data source with empty value for name", - customWebhookAuthenticatorYaml: here.Doc(` - --- - apiVersion: authentication.concierge.%s/v1alpha1 - kind: WebhookAuthenticator - metadata: - name: %s - spec: - endpoint: %s + tlsYAML: here.Doc(` tls: certificateAuthorityDataSource: kind: Secret name: "" key: bar `), - customJWTAuthenticatorYaml: here.Doc(` - --- - apiVersion: authentication.concierge.%s/v1alpha1 - kind: JWTAuthenticator - metadata: - name: %s - spec: - issuer: %s - audience: some-audience - tls: - certificateAuthorityDataSource: - kind: Secret - name: "" - key: bar - `), - resourceNamePrefix: "invalid-tls-spec-empty-name", - expectedError: `The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.name: Invalid value: "": spec.tls.certificateAuthorityDataSource.name in body should be at least 1 chars long`, + expectedError: `The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.name: Invalid value: "": spec.tls.certificateAuthorityDataSource.name in body should be at least 1 chars long`, }, { name: "should disallow certificate authority data source with missing key", - customWebhookAuthenticatorYaml: here.Doc(` - --- - apiVersion: authentication.concierge.%s/v1alpha1 - kind: WebhookAuthenticator - metadata: - name: %s - spec: - endpoint: %s + tlsYAML: here.Doc(` tls: certificateAuthorityDataSource: kind: Secret name: foo `), - customJWTAuthenticatorYaml: here.Doc(` - --- - apiVersion: authentication.concierge.%s/v1alpha1 - kind: JWTAuthenticator - metadata: - name: %s - spec: - issuer: %s - audience: some-audience - tls: - certificateAuthorityDataSource: - kind: Secret - name: foo - `), - resourceNamePrefix: "invalid-tls-spec-missing-key", - expectedError: `The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.key: Required value`, + expectedError: `The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.key: Required value`, }, { name: "should disallow certificate authority data source with empty value for key", - customWebhookAuthenticatorYaml: here.Doc(` - --- - apiVersion: authentication.concierge.%s/v1alpha1 - kind: WebhookAuthenticator - metadata: - name: %s - spec: - endpoint: %s + tlsYAML: here.Doc(` tls: certificateAuthorityDataSource: kind: Secret name: foo key: "" `), - customJWTAuthenticatorYaml: here.Doc(` - --- - apiVersion: authentication.concierge.%s/v1alpha1 - kind: JWTAuthenticator - metadata: - name: %s - spec: - issuer: %s - audience: some-audience - tls: - certificateAuthorityDataSource: - kind: Secret - name: foo - key: "" - `), - resourceNamePrefix: "invalid-tls-spec-empty-kind", - expectedError: `The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.key: Invalid value: "": spec.tls.certificateAuthorityDataSource.key in body should be at least 1 chars long`, + expectedError: `The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.key: Invalid value: "": spec.tls.certificateAuthorityDataSource.key in body should be at least 1 chars long`, }, { name: "should disallow certificate authority data source with missing kind", - customWebhookAuthenticatorYaml: here.Doc(` - --- - apiVersion: authentication.concierge.%s/v1alpha1 - kind: WebhookAuthenticator - metadata: - name: %s - spec: - endpoint: %s + tlsYAML: here.Doc(` tls: certificateAuthorityDataSource: name: foo key: bar `), - customJWTAuthenticatorYaml: here.Doc(` - --- - apiVersion: authentication.concierge.%s/v1alpha1 - kind: JWTAuthenticator - metadata: - name: %s - spec: - issuer: %s - audience: some-audience - tls: - certificateAuthorityDataSource: - name: foo - key: bar - `), - resourceNamePrefix: "invalid-tls-spec-missing-kind", - expectedError: `The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.kind: Required value`, + expectedError: `The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.kind: Required value`, }, { name: "should disallow certificate authority data source with empty value for kind", - customWebhookAuthenticatorYaml: here.Doc(` - --- - apiVersion: authentication.concierge.%s/v1alpha1 - kind: WebhookAuthenticator - metadata: - name: %s - spec: - endpoint: %s + tlsYAML: here.Doc(` tls: certificateAuthorityDataSource: kind: "" name: foo key: bar `), - customJWTAuthenticatorYaml: here.Doc(` - --- - apiVersion: authentication.concierge.%s/v1alpha1 - kind: JWTAuthenticator - metadata: - name: %s - spec: - issuer: %s - audience: some-audience - tls: - certificateAuthorityDataSource: - kind: "" - name: foo - key: bar - `), - resourceNamePrefix: "invalid-tls-spec-invalid-kind", - expectedError: `The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.kind: Unsupported value: "": supported values: "Secret", "ConfigMap"`, + expectedError: `The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.kind: Unsupported value: "": supported values: "Secret", "ConfigMap"`, }, { name: "should disallow certificate authority data source with invalid kind", - customWebhookAuthenticatorYaml: here.Doc(` - --- - apiVersion: authentication.concierge.%s/v1alpha1 - kind: WebhookAuthenticator - metadata: - name: %s - spec: - endpoint: %s + tlsYAML: here.Doc(` tls: certificateAuthorityDataSource: kind: sorcery name: foo key: bar `), - customJWTAuthenticatorYaml: here.Doc(` - --- - apiVersion: authentication.concierge.%s/v1alpha1 - kind: JWTAuthenticator - metadata: - name: %s - spec: - issuer: %s - audience: some-audience - tls: - certificateAuthorityDataSource: - kind: sorcery - name: foo - key: bar - `), - resourceNamePrefix: "invalid-tls-spec-invalid-kind", - expectedError: `The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.kind: Unsupported value: "sorcery": supported values: "Secret", "ConfigMap"`, + expectedError: `The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.kind: Unsupported value: "sorcery": supported values: "Secret", "ConfigMap"`, }, { name: "should create a custom resource passing all validations using a Secret source", - customWebhookAuthenticatorYaml: here.Doc(` - --- - apiVersion: authentication.concierge.%s/v1alpha1 - kind: WebhookAuthenticator - metadata: - name: %s - spec: - endpoint: %s + tlsYAML: here.Doc(` tls: certificateAuthorityDataSource: kind: Secret name: foo key: bar `), - customJWTAuthenticatorYaml: here.Doc(` - --- - apiVersion: authentication.concierge.%s/v1alpha1 - kind: JWTAuthenticator - metadata: - name: %s - spec: - issuer: %s - audience: some-audience - tls: - certificateAuthorityDataSource: - kind: Secret - name: foo - key: bar - `), - resourceNamePrefix: "valid-webhook-auth-secret-kind", - expectedError: "", + expectedError: "", }, { name: "should create a custom resource passing all validations using a ConfigMap source", - customWebhookAuthenticatorYaml: here.Doc(` - --- - apiVersion: authentication.concierge.%s/v1alpha1 - kind: WebhookAuthenticator - metadata: - name: %s - spec: - endpoint: %s + tlsYAML: here.Doc(` tls: certificateAuthorityDataSource: kind: ConfigMap name: foo key: bar `), - customJWTAuthenticatorYaml: here.Doc(` - --- - apiVersion: authentication.concierge.%s/v1alpha1 - kind: JWTAuthenticator - metadata: - name: %s - spec: - issuer: %s - audience: some-audience - tls: - certificateAuthorityDataSource: - kind: ConfigMap - name: foo - key: bar - `), - resourceNamePrefix: "valid-webhook-auth-cm-kind", - expectedError: "", + expectedError: "", }, { - name: "should create a custom resource without any tls spec", - customWebhookAuthenticatorYaml: here.Doc(` - --- - apiVersion: authentication.concierge.%s/v1alpha1 - kind: WebhookAuthenticator - metadata: - name: %s - spec: - endpoint: %s - `), - customJWTAuthenticatorYaml: here.Doc(` - --- - apiVersion: authentication.concierge.%s/v1alpha1 - kind: JWTAuthenticator - metadata: - name: %s - spec: - issuer: %s - audience: some-audience - `), - resourceNamePrefix: "no-tls-spec", - expectedError: "", + name: "should create a custom resource without any tls spec", + tlsYAML: "", + expectedError: "", }, } @@ -361,9 +157,15 @@ func TestTLSSpecKubeBuilderValidationConcierge_Parallel(t *testing.T) { t.Run(tc.name, func(t *testing.T) { t.Parallel() + // Further indent every line except for the first line by four spaces. + // Use four spaces because that's what here.Doc uses. + // Do not indent the first line because the template already indents it. + indentedTLSYAML := strings.ReplaceAll(tc.tlsYAML, "\n", "\n ") + t.Run("apply webhook authenticator", func(t *testing.T) { - webhookResourceName := tc.resourceNamePrefix + "-" + testlib.RandHex(t, 7) - webhookYamlBytes := []byte(fmt.Sprintf(tc.customWebhookAuthenticatorYaml, env.APIGroupSuffix, webhookResourceName, env.TestWebhook.Endpoint)) + webhookResourceName := "test-webhook-authenticator-" + testlib.RandHex(t, 7) + webhookYamlBytes := []byte(fmt.Sprintf(webhookAuthenticatorYamlTemplate, + env.APIGroupSuffix, webhookResourceName, env.TestWebhook.Endpoint, indentedTLSYAML)) performKubectlApply( t, @@ -378,8 +180,9 @@ func TestTLSSpecKubeBuilderValidationConcierge_Parallel(t *testing.T) { t.Run("apply jwt authenticator", func(t *testing.T) { _, supervisorIssuer := env.SupervisorUpstreamOIDC.InferTheIssuerURL(t) - jwtAuthenticatorResourceName := tc.resourceNamePrefix + "-" + testlib.RandHex(t, 7) - jwtAuthenticatorYamlBytes := []byte(fmt.Sprintf(tc.customJWTAuthenticatorYaml, env.APIGroupSuffix, jwtAuthenticatorResourceName, supervisorIssuer)) + jwtAuthenticatorResourceName := "test-jwt-authenticator-" + testlib.RandHex(t, 7) + jwtAuthenticatorYamlBytes := []byte(fmt.Sprintf(jwtAuthenticatorYamlTemplate, + env.APIGroupSuffix, jwtAuthenticatorResourceName, supervisorIssuer, indentedTLSYAML)) performKubectlApply( t, From 4b2ed52f44fb410e1b11ea9c12d1dba93432232f Mon Sep 17 00:00:00 2001 From: Joshua Casey Date: Tue, 23 Jul 2024 17:11:37 -0500 Subject: [PATCH 39/99] Add GitHubIdentityProvider to the Supervisor TLS config static validation integration tests Co-authored-by: Ryan Richard --- test/integration/supervisor_tls_spec_test.go | 279 ++++++++----------- 1 file changed, 119 insertions(+), 160 deletions(-) diff --git a/test/integration/supervisor_tls_spec_test.go b/test/integration/supervisor_tls_spec_test.go index bb1edc937..8fa989029 100644 --- a/test/integration/supervisor_tls_spec_test.go +++ b/test/integration/supervisor_tls_spec_test.go @@ -4,6 +4,7 @@ package integration import ( "fmt" + "strings" "testing" "go.pinniped.dev/internal/here" @@ -14,244 +15,166 @@ import ( // on the TLSSpec in Pinniped supervisor CRDs using OIDCIdentityProvider as an example. func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { env := testlib.IntegrationEnv(t) + + oidcIDPTemplate := here.Doc(` + apiVersion: idp.supervisor.%s/v1alpha1 + kind: OIDCIdentityProvider + metadata: + name: %s + spec: + issuer: %s + authorizationConfig: + additionalScopes: [offline_access, email] + allowPasswordGrant: true + client: + secretName: foo-bar-client-credentials + %s + `) + + githubIDPTemplate := here.Doc(` + apiVersion: idp.supervisor.%s/v1alpha1 + kind: GitHubIdentityProvider + metadata: + name: %s + spec: + allowAuthentication: + organizations: + policy: AllGitHubUsers + client: + secretName: does-not-matter + githubAPI: + %s + `) + testCases := []struct { - name string - customResourceYaml string - customResourceName string - expectedError string + name string + tlsYAML string + expectedError string + expectedGitHubError string }{ // TODO: make this a loop to also run the same tests on LDAP, AD, GitHub?? { name: "should disallow certificate authority data source with missing name", - customResourceYaml: here.Doc(` - --- - apiVersion: idp.supervisor.%s/v1alpha1 - kind: OIDCIdentityProvider - metadata: - name: %s - spec: + tlsYAML: here.Doc(` tls: certificateAuthorityDataSource: kind: Secret key: bar - issuer: %s - authorizationConfig: - additionalScopes: [offline_access, email] - allowPasswordGrant: true - client: - secretName: foo-bar-client-credentials `), - customResourceName: "invalid-oidc-idp-missing-name", - expectedError: `The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.name: Required value`, + expectedError: `The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.name: Required value`, + expectedGitHubError: here.Doc(` + The %s "%s" is invalid: + * spec.githubAPI.tls.certificateAuthorityDataSource.name: Required value + * : Invalid value: "null": some validation rules were not checked because the object was invalid; correct the existing errors to complete validation`), }, { name: "should disallow certificate authority data source with empty value for name", - customResourceYaml: here.Doc(` - --- - apiVersion: idp.supervisor.%s/v1alpha1 - kind: OIDCIdentityProvider - metadata: - name: %s - spec: + tlsYAML: here.Doc(` tls: certificateAuthorityDataSource: kind: Secret name: "" key: bar - issuer: %s - authorizationConfig: - additionalScopes: [offline_access, email] - allowPasswordGrant: true - client: - secretName: foo-bar-client-credentials `), - customResourceName: "invalid-oidc-idp-empty-name", - expectedError: `The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.name: Invalid value: "": spec.tls.certificateAuthorityDataSource.name in body should be at least 1 chars long`, + expectedError: `The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.name: Invalid value: "": spec.tls.certificateAuthorityDataSource.name in body should be at least 1 chars long`, + expectedGitHubError: `The %s "%s" is invalid: spec.githubAPI.tls.certificateAuthorityDataSource.name: Invalid value: "": spec.githubAPI.tls.certificateAuthorityDataSource.name in body should be at least 1 chars long`, }, { name: "should disallow certificate authority data source with missing key", - customResourceYaml: here.Doc(` - --- - apiVersion: idp.supervisor.%s/v1alpha1 - kind: OIDCIdentityProvider - metadata: - name: %s - spec: + tlsYAML: here.Doc(` tls: certificateAuthorityDataSource: kind: Secret name: foo - issuer: %s - authorizationConfig: - additionalScopes: [offline_access, email] - allowPasswordGrant: true - client: - secretName: foo-bar-client-credentials `), - customResourceName: "invalid-oidc-idp-missing-key", - expectedError: `The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.key: Required value`, + expectedError: `The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.key: Required value`, + expectedGitHubError: here.Doc(` + The %s "%s" is invalid: + * spec.githubAPI.tls.certificateAuthorityDataSource.key: Required value + * : Invalid value: "null": some validation rules were not checked because the object was invalid; correct the existing errors to complete validation`), }, { name: "should disallow certificate authority data source with empty value for key", - customResourceYaml: here.Doc(` - --- - apiVersion: idp.supervisor.%s/v1alpha1 - kind: OIDCIdentityProvider - metadata: - name: %s - spec: + tlsYAML: here.Doc(` tls: certificateAuthorityDataSource: kind: Secret name: foo key: "" - issuer: %s - authorizationConfig: - additionalScopes: [offline_access, email] - allowPasswordGrant: true - client: - secretName: foo-bar-client-credentials `), - customResourceName: "invalid-oidc-idp-empty-key", - expectedError: `The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.key: Invalid value: "": spec.tls.certificateAuthorityDataSource.key in body should be at least 1 chars long`, + expectedError: `The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.key: Invalid value: "": spec.tls.certificateAuthorityDataSource.key in body should be at least 1 chars long`, + expectedGitHubError: `The %s "%s" is invalid: spec.githubAPI.tls.certificateAuthorityDataSource.key: Invalid value: "": spec.githubAPI.tls.certificateAuthorityDataSource.key in body should be at least 1 chars long`, }, { name: "should disallow certificate authority data source with missing kind", - customResourceYaml: here.Doc(` - --- - apiVersion: idp.supervisor.%s/v1alpha1 - kind: OIDCIdentityProvider - metadata: - name: %s - spec: + tlsYAML: here.Doc(` tls: certificateAuthorityDataSource: name: foo key: bar - issuer: %s - authorizationConfig: - additionalScopes: [offline_access, email] - allowPasswordGrant: true - client: - secretName: foo-bar-client-credentials `), - customResourceName: "invalid-oidc-idp-missing-kind", - expectedError: `The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.kind: Required value`, + expectedError: `The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.kind: Required value`, + expectedGitHubError: here.Doc(` + The %s "%s" is invalid: + * spec.githubAPI.tls.certificateAuthorityDataSource.kind: Required value + * : Invalid value: "null": some validation rules were not checked because the object was invalid; correct the existing errors to complete validation`), }, { - name: "should disallow certificate authority data source with empty value kind", - customResourceYaml: here.Doc(` - --- - apiVersion: idp.supervisor.%s/v1alpha1 - kind: OIDCIdentityProvider - metadata: - name: %s - spec: + name: "should disallow certificate authority data source with empty value for kind", + tlsYAML: here.Doc(` tls: certificateAuthorityDataSource: kind: "" name: foo key: bar - issuer: %s - authorizationConfig: - additionalScopes: [offline_access, email] - allowPasswordGrant: true - client: - secretName: foo-bar-client-credentials `), - customResourceName: "invalid-oidc-idp-invalid-kind", - expectedError: `The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.kind: Unsupported value: "": supported values: "Secret", "ConfigMap"`, + expectedError: `The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.kind: Unsupported value: "": supported values: "Secret", "ConfigMap"`, + expectedGitHubError: here.Doc(` + The %s "%s" is invalid: + * spec.githubAPI.tls.certificateAuthorityDataSource.kind: Unsupported value: "": supported values: "Secret", "ConfigMap" + * : Invalid value: "null": some validation rules were not checked because the object was invalid; correct the existing errors to complete validation`), }, { name: "should disallow certificate authority data source with invalid kind", - customResourceYaml: here.Doc(` - --- - apiVersion: idp.supervisor.%s/v1alpha1 - kind: OIDCIdentityProvider - metadata: - name: %s - spec: + tlsYAML: here.Doc(` tls: certificateAuthorityDataSource: kind: sorcery name: foo key: bar - issuer: %s - authorizationConfig: - additionalScopes: [offline_access, email] - allowPasswordGrant: true - client: - secretName: foo-bar-client-credentials `), - customResourceName: "invalid-oidc-idp-invalid-kind", - expectedError: `The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.kind: Unsupported value: "sorcery": supported values: "Secret", "ConfigMap"`, + expectedError: `The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.kind: Unsupported value: "sorcery": supported values: "Secret", "ConfigMap"`, + expectedGitHubError: here.Doc(` + The %s "%s" is invalid: + * spec.githubAPI.tls.certificateAuthorityDataSource.kind: Unsupported value: "sorcery": supported values: "Secret", "ConfigMap" + * : Invalid value: "null": some validation rules were not checked because the object was invalid; correct the existing errors to complete validation`), }, { name: "should create a custom resource passing all validations using a Secret source", - customResourceYaml: here.Doc(` - --- - apiVersion: idp.supervisor.%s/v1alpha1 - kind: OIDCIdentityProvider - metadata: - name: %s - spec: + tlsYAML: here.Doc(` tls: certificateAuthorityDataSource: kind: Secret name: foo key: bar - issuer: %s - authorizationConfig: - additionalScopes: [offline_access, email] - allowPasswordGrant: true - client: - secretName: foo-bar-client-credentials `), - customResourceName: "valid-oidc-idp-secret-kind", - expectedError: "", + expectedError: "", }, { name: "should create a custom resource passing all validations using a ConfigMap source", - customResourceYaml: here.Doc(` - --- - apiVersion: idp.supervisor.%s/v1alpha1 - kind: OIDCIdentityProvider - metadata: - name: %s - spec: + tlsYAML: here.Doc(` tls: certificateAuthorityDataSource: kind: ConfigMap name: foo key: bar - issuer: %s - authorizationConfig: - additionalScopes: [offline_access, email] - allowPasswordGrant: true - client: - secretName: foo-bar-client-credentials `), - customResourceName: "valid-oidc-idp-cm-kind", - expectedError: "", + expectedError: "", }, { - name: "should create a custom resource without any tls spec", - customResourceYaml: here.Doc(` - --- - apiVersion: idp.supervisor.%s/v1alpha1 - kind: OIDCIdentityProvider - metadata: - name: %s - spec: - issuer: %s - authorizationConfig: - additionalScopes: [offline_access, email] - allowPasswordGrant: true - client: - secretName: foo-bar-client-credentials - `), - customResourceName: "no-tls-spec", - expectedError: "", + name: "should create a custom resource without any tls spec", + tlsYAML: "", + expectedError: "", }, } @@ -259,10 +182,46 @@ func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { t.Run(tc.name, func(t *testing.T) { t.Parallel() - resourceName := tc.customResourceName + "-" + testlib.RandHex(t, 7) - yamlBytes := []byte(fmt.Sprintf(tc.customResourceYaml, env.APIGroupSuffix, resourceName, env.SupervisorUpstreamOIDC.Issuer)) + // Further indent every line except for the first line by four spaces. + // Use four spaces because that's what here.Doc uses. + // Do not indent the first line because the template already indents it. + indentedTLSYAML := strings.ReplaceAll(tc.tlsYAML, "\n", "\n ") - performKubectlApply(t, yamlBytes, `oidcidentityprovider.idp.supervisor.pinniped.dev`, tc.expectedError, "OIDCIdentityProvider", resourceName) + t.Run("apply OIDC IDP", func(t *testing.T) { + resourceName := "test-oidc-idp-" + testlib.RandHex(t, 7) + yamlBytes := []byte(fmt.Sprintf(oidcIDPTemplate, + env.APIGroupSuffix, resourceName, env.SupervisorUpstreamOIDC.Issuer, indentedTLSYAML)) + + performKubectlApply( + t, + yamlBytes, + `oidcidentityprovider.idp.supervisor.pinniped.dev`, + tc.expectedError, + "OIDCIdentityProvider", + resourceName, + ) + }) + + t.Run("apply GitHub IDP", func(t *testing.T) { + // GitHub is nested deeper + indentedTLSYAMLForGitHub := strings.ReplaceAll(indentedTLSYAML, "\n", "\n ") + + // This is how kubectl shows this error + expectedGitHubError := strings.ReplaceAll(tc.expectedGitHubError, "invalid:\n", "invalid: \n") + + resourceName := "test-github-idp-" + testlib.RandHex(t, 7) + yamlBytes := []byte(fmt.Sprintf(githubIDPTemplate, + env.APIGroupSuffix, resourceName, indentedTLSYAMLForGitHub)) + + performKubectlApply( + t, + yamlBytes, + `githubidentityprovider.idp.supervisor.pinniped.dev`, + expectedGitHubError, + "GitHubIdentityProvider", + resourceName, + ) + }) }) } } From b7c26c43ca5527e5f744e82f176625d4d82bcf8f Mon Sep 17 00:00:00 2001 From: Joshua Casey Date: Tue, 23 Jul 2024 17:22:21 -0500 Subject: [PATCH 40/99] Add LDAPIdentityProvider and ActiveDirectoryIdentityProvider to the Supervisor TLS config static validation integration tests Co-authored-by: Ryan Richard --- test/integration/supervisor_tls_spec_test.go | 60 +++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/test/integration/supervisor_tls_spec_test.go b/test/integration/supervisor_tls_spec_test.go index 8fa989029..8d14708a5 100644 --- a/test/integration/supervisor_tls_spec_test.go +++ b/test/integration/supervisor_tls_spec_test.go @@ -31,6 +31,35 @@ func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { %s `) + ldapIDPTemplate := here.Doc(` + apiVersion: idp.supervisor.%s/v1alpha1 + kind: LDAPIdentityProvider + metadata: + name: %s + spec: + host: %s + bind: + secretName: foo-bar-bind-credentials + userSearch: + base: foo + attributes: + username: bar + uid: baz + %s + `) + + activeDirectoryIDPTemplate := here.Doc(` + apiVersion: idp.supervisor.%s/v1alpha1 + kind: ActiveDirectoryIdentityProvider + metadata: + name: %s + spec: + host: %s + bind: + secretName: foo-bar-bind-credentials + %s + `) + githubIDPTemplate := here.Doc(` apiVersion: idp.supervisor.%s/v1alpha1 kind: GitHubIdentityProvider @@ -52,7 +81,6 @@ func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { expectedError string expectedGitHubError string }{ - // TODO: make this a loop to also run the same tests on LDAP, AD, GitHub?? { name: "should disallow certificate authority data source with missing name", tlsYAML: here.Doc(` @@ -202,6 +230,36 @@ func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { ) }) + t.Run("apply LDAP IDP", func(t *testing.T) { + resourceName := "test-ldap-idp-" + testlib.RandHex(t, 7) + yamlBytes := []byte(fmt.Sprintf(ldapIDPTemplate, + env.APIGroupSuffix, resourceName, env.SupervisorUpstreamLDAP.Host, indentedTLSYAML)) + + performKubectlApply( + t, + yamlBytes, + `ldapidentityprovider.idp.supervisor.pinniped.dev`, + tc.expectedError, + "LDAPIdentityProvider", + resourceName, + ) + }) + + t.Run("apply ActiveDirectory IDP", func(t *testing.T) { + resourceName := "test-ad-idp-" + testlib.RandHex(t, 7) + yamlBytes := []byte(fmt.Sprintf(activeDirectoryIDPTemplate, + env.APIGroupSuffix, resourceName, env.SupervisorUpstreamLDAP.Host, indentedTLSYAML)) + + performKubectlApply( + t, + yamlBytes, + `activedirectoryidentityprovider.idp.supervisor.pinniped.dev`, + tc.expectedError, + "ActiveDirectoryIdentityProvider", + resourceName, + ) + }) + t.Run("apply GitHub IDP", func(t *testing.T) { // GitHub is nested deeper indentedTLSYAMLForGitHub := strings.ReplaceAll(indentedTLSYAML, "\n", "\n ") From 4ec5766ea9464a1d396b3a59cd113ee79bacf191 Mon Sep 17 00:00:00 2001 From: Joshua Casey Date: Wed, 24 Jul 2024 10:25:00 -0500 Subject: [PATCH 41/99] Modify Concierge/Superivsor TLS spec integration tests to allow for older K8s versions --- test/integration/concierge_tls_spec_test.go | 54 ++++++------ test/integration/supervisor_tls_spec_test.go | 90 ++++++++++---------- 2 files changed, 75 insertions(+), 69 deletions(-) diff --git a/test/integration/concierge_tls_spec_test.go b/test/integration/concierge_tls_spec_test.go index 314cc7495..b9a42c5e7 100644 --- a/test/integration/concierge_tls_spec_test.go +++ b/test/integration/concierge_tls_spec_test.go @@ -46,9 +46,9 @@ func TestTLSSpecKubeBuilderValidationConcierge_Parallel(t *testing.T) { `) testCases := []struct { - name string - tlsYAML string - expectedError string + name string + tlsYAML string + expectedErrorSnippets []string }{ { name: "should disallow certificate authority data source with missing name", @@ -58,7 +58,7 @@ func TestTLSSpecKubeBuilderValidationConcierge_Parallel(t *testing.T) { kind: Secret key: bar `), - expectedError: `The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.name: Required value`, + expectedErrorSnippets: []string{`The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.name: Required value`}, }, { name: "should disallow certificate authority data source with empty value for name", @@ -69,7 +69,7 @@ func TestTLSSpecKubeBuilderValidationConcierge_Parallel(t *testing.T) { name: "" key: bar `), - expectedError: `The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.name: Invalid value: "": spec.tls.certificateAuthorityDataSource.name in body should be at least 1 chars long`, + expectedErrorSnippets: []string{`The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.name: Invalid value: "": spec.tls.certificateAuthorityDataSource.name in body should be at least 1 chars long`}, }, { name: "should disallow certificate authority data source with missing key", @@ -79,7 +79,7 @@ func TestTLSSpecKubeBuilderValidationConcierge_Parallel(t *testing.T) { kind: Secret name: foo `), - expectedError: `The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.key: Required value`, + expectedErrorSnippets: []string{`The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.key: Required value`}, }, { name: "should disallow certificate authority data source with empty value for key", @@ -90,7 +90,7 @@ func TestTLSSpecKubeBuilderValidationConcierge_Parallel(t *testing.T) { name: foo key: "" `), - expectedError: `The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.key: Invalid value: "": spec.tls.certificateAuthorityDataSource.key in body should be at least 1 chars long`, + expectedErrorSnippets: []string{`The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.key: Invalid value: "": spec.tls.certificateAuthorityDataSource.key in body should be at least 1 chars long`}, }, { name: "should disallow certificate authority data source with missing kind", @@ -100,7 +100,7 @@ func TestTLSSpecKubeBuilderValidationConcierge_Parallel(t *testing.T) { name: foo key: bar `), - expectedError: `The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.kind: Required value`, + expectedErrorSnippets: []string{`The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.kind: Required value`}, }, { name: "should disallow certificate authority data source with empty value for kind", @@ -111,7 +111,7 @@ func TestTLSSpecKubeBuilderValidationConcierge_Parallel(t *testing.T) { name: foo key: bar `), - expectedError: `The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.kind: Unsupported value: "": supported values: "Secret", "ConfigMap"`, + expectedErrorSnippets: []string{`The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.kind: Unsupported value: "": supported values: "Secret", "ConfigMap"`}, }, { name: "should disallow certificate authority data source with invalid kind", @@ -122,7 +122,7 @@ func TestTLSSpecKubeBuilderValidationConcierge_Parallel(t *testing.T) { name: foo key: bar `), - expectedError: `The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.kind: Unsupported value: "sorcery": supported values: "Secret", "ConfigMap"`, + expectedErrorSnippets: []string{`The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.kind: Unsupported value: "sorcery": supported values: "Secret", "ConfigMap"`}, }, { name: "should create a custom resource passing all validations using a Secret source", @@ -133,7 +133,7 @@ func TestTLSSpecKubeBuilderValidationConcierge_Parallel(t *testing.T) { name: foo key: bar `), - expectedError: "", + expectedErrorSnippets: nil, }, { name: "should create a custom resource passing all validations using a ConfigMap source", @@ -144,12 +144,12 @@ func TestTLSSpecKubeBuilderValidationConcierge_Parallel(t *testing.T) { name: foo key: bar `), - expectedError: "", + expectedErrorSnippets: nil, }, { - name: "should create a custom resource without any tls spec", - tlsYAML: "", - expectedError: "", + name: "should create a custom resource without any tls spec", + tlsYAML: "", + expectedErrorSnippets: nil, }, } @@ -171,7 +171,7 @@ func TestTLSSpecKubeBuilderValidationConcierge_Parallel(t *testing.T) { t, webhookYamlBytes, `webhookauthenticator.authentication.concierge.pinniped.dev`, - tc.expectedError, + tc.expectedErrorSnippets, "WebhookAuthenticator", webhookResourceName, ) @@ -188,7 +188,7 @@ func TestTLSSpecKubeBuilderValidationConcierge_Parallel(t *testing.T) { t, jwtAuthenticatorYamlBytes, `jwtauthenticator.authentication.concierge.pinniped.dev`, - tc.expectedError, + tc.expectedErrorSnippets, "JWTAuthenticator", jwtAuthenticatorResourceName, ) @@ -201,7 +201,7 @@ func performKubectlApply( t *testing.T, yamlBytes []byte, expectedSuccessPrefix string, - expectedError string, + expectedErrorSnippets []string, resourceType string, resourceName string, ) { @@ -227,11 +227,17 @@ func performKubectlApply( require.NoError(t, exec.Command("kubectl", []string{"delete", "--ignore-not-found", "-f", yamlFilepath}...).Run()) }) - if expectedError == "" { - require.Empty(t, stdErr.String()) - require.Regexp(t, regexp.QuoteMeta(expectedSuccessPrefix)+regexp.QuoteMeta(fmt.Sprintf("/%s created\n", resourceName)), stdOut.String()) - require.NoError(t, err) - } else { - require.Equal(t, fmt.Sprintf(expectedError, resourceType, resourceName), strings.TrimSuffix(stdErr.String(), "\n")) + if len(expectedErrorSnippets) > 0 { + actualErrorString := strings.TrimSuffix(stdErr.String(), "\n") + for i, snippet := range expectedErrorSnippets { + if i == 0 { + snippet = fmt.Sprintf(snippet, resourceType, resourceName) + } + require.Contains(t, actualErrorString, snippet) + } + return } + require.Empty(t, stdErr.String()) + require.Regexp(t, regexp.QuoteMeta(expectedSuccessPrefix)+regexp.QuoteMeta(fmt.Sprintf("/%s created\n", resourceName)), stdOut.String()) + require.NoError(t, err) } diff --git a/test/integration/supervisor_tls_spec_test.go b/test/integration/supervisor_tls_spec_test.go index 8d14708a5..a8a6ab607 100644 --- a/test/integration/supervisor_tls_spec_test.go +++ b/test/integration/supervisor_tls_spec_test.go @@ -76,10 +76,10 @@ func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { `) testCases := []struct { - name string - tlsYAML string - expectedError string - expectedGitHubError string + name string + tlsYAML string + expectedErrorSnippets []string + expectedGitHubErrorSnippets []string }{ { name: "should disallow certificate authority data source with missing name", @@ -89,11 +89,11 @@ func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { kind: Secret key: bar `), - expectedError: `The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.name: Required value`, - expectedGitHubError: here.Doc(` - The %s "%s" is invalid: - * spec.githubAPI.tls.certificateAuthorityDataSource.name: Required value - * : Invalid value: "null": some validation rules were not checked because the object was invalid; correct the existing errors to complete validation`), + expectedErrorSnippets: []string{`The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.name: Required value`}, + expectedGitHubErrorSnippets: []string{ + `The %s "%s" is invalid:`, + "* spec.githubAPI.tls.certificateAuthorityDataSource.name: Required value", + }, }, { name: "should disallow certificate authority data source with empty value for name", @@ -104,8 +104,8 @@ func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { name: "" key: bar `), - expectedError: `The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.name: Invalid value: "": spec.tls.certificateAuthorityDataSource.name in body should be at least 1 chars long`, - expectedGitHubError: `The %s "%s" is invalid: spec.githubAPI.tls.certificateAuthorityDataSource.name: Invalid value: "": spec.githubAPI.tls.certificateAuthorityDataSource.name in body should be at least 1 chars long`, + expectedErrorSnippets: []string{`The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.name: Invalid value: "": spec.tls.certificateAuthorityDataSource.name in body should be at least 1 chars long`}, + expectedGitHubErrorSnippets: []string{`The %s "%s" is invalid: spec.githubAPI.tls.certificateAuthorityDataSource.name: Invalid value: "": spec.githubAPI.tls.certificateAuthorityDataSource.name in body should be at least 1 chars long`}, }, { name: "should disallow certificate authority data source with missing key", @@ -115,11 +115,11 @@ func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { kind: Secret name: foo `), - expectedError: `The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.key: Required value`, - expectedGitHubError: here.Doc(` - The %s "%s" is invalid: - * spec.githubAPI.tls.certificateAuthorityDataSource.key: Required value - * : Invalid value: "null": some validation rules were not checked because the object was invalid; correct the existing errors to complete validation`), + expectedErrorSnippets: []string{`The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.key: Required value`}, + expectedGitHubErrorSnippets: []string{ + `The %s "%s" is invalid:`, + "* spec.githubAPI.tls.certificateAuthorityDataSource.key: Required value", + }, }, { name: "should disallow certificate authority data source with empty value for key", @@ -130,8 +130,8 @@ func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { name: foo key: "" `), - expectedError: `The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.key: Invalid value: "": spec.tls.certificateAuthorityDataSource.key in body should be at least 1 chars long`, - expectedGitHubError: `The %s "%s" is invalid: spec.githubAPI.tls.certificateAuthorityDataSource.key: Invalid value: "": spec.githubAPI.tls.certificateAuthorityDataSource.key in body should be at least 1 chars long`, + expectedErrorSnippets: []string{`The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.key: Invalid value: "": spec.tls.certificateAuthorityDataSource.key in body should be at least 1 chars long`}, + expectedGitHubErrorSnippets: []string{`The %s "%s" is invalid: spec.githubAPI.tls.certificateAuthorityDataSource.key: Invalid value: "": spec.githubAPI.tls.certificateAuthorityDataSource.key in body should be at least 1 chars long`}, }, { name: "should disallow certificate authority data source with missing kind", @@ -141,11 +141,11 @@ func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { name: foo key: bar `), - expectedError: `The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.kind: Required value`, - expectedGitHubError: here.Doc(` - The %s "%s" is invalid: - * spec.githubAPI.tls.certificateAuthorityDataSource.kind: Required value - * : Invalid value: "null": some validation rules were not checked because the object was invalid; correct the existing errors to complete validation`), + expectedErrorSnippets: []string{`The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.kind: Required value`}, + expectedGitHubErrorSnippets: []string{ + `The %s "%s" is invalid:`, + "* spec.githubAPI.tls.certificateAuthorityDataSource.kind: Required value", + }, }, { name: "should disallow certificate authority data source with empty value for kind", @@ -156,11 +156,11 @@ func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { name: foo key: bar `), - expectedError: `The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.kind: Unsupported value: "": supported values: "Secret", "ConfigMap"`, - expectedGitHubError: here.Doc(` - The %s "%s" is invalid: - * spec.githubAPI.tls.certificateAuthorityDataSource.kind: Unsupported value: "": supported values: "Secret", "ConfigMap" - * : Invalid value: "null": some validation rules were not checked because the object was invalid; correct the existing errors to complete validation`), + expectedErrorSnippets: []string{`The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.kind: Unsupported value: "": supported values: "Secret", "ConfigMap"`}, + expectedGitHubErrorSnippets: []string{ + `The %s "%s" is invalid:`, + `spec.githubAPI.tls.certificateAuthorityDataSource.kind: Unsupported value: "": supported values: "Secret", "ConfigMap"`, + }, }, { name: "should disallow certificate authority data source with invalid kind", @@ -171,11 +171,11 @@ func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { name: foo key: bar `), - expectedError: `The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.kind: Unsupported value: "sorcery": supported values: "Secret", "ConfigMap"`, - expectedGitHubError: here.Doc(` - The %s "%s" is invalid: - * spec.githubAPI.tls.certificateAuthorityDataSource.kind: Unsupported value: "sorcery": supported values: "Secret", "ConfigMap" - * : Invalid value: "null": some validation rules were not checked because the object was invalid; correct the existing errors to complete validation`), + expectedErrorSnippets: []string{`The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.kind: Unsupported value: "sorcery": supported values: "Secret", "ConfigMap"`}, + expectedGitHubErrorSnippets: []string{ + `The %s "%s" is invalid:`, + `spec.githubAPI.tls.certificateAuthorityDataSource.kind: Unsupported value: "sorcery": supported values: "Secret", "ConfigMap"`, + }, }, { name: "should create a custom resource passing all validations using a Secret source", @@ -186,7 +186,8 @@ func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { name: foo key: bar `), - expectedError: "", + expectedErrorSnippets: nil, + expectedGitHubErrorSnippets: nil, }, { name: "should create a custom resource passing all validations using a ConfigMap source", @@ -197,12 +198,14 @@ func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { name: foo key: bar `), - expectedError: "", + expectedErrorSnippets: nil, + expectedGitHubErrorSnippets: nil, }, { - name: "should create a custom resource without any tls spec", - tlsYAML: "", - expectedError: "", + name: "should create a custom resource without any tls spec", + tlsYAML: "", + expectedErrorSnippets: nil, + expectedGitHubErrorSnippets: nil, }, } @@ -224,7 +227,7 @@ func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { t, yamlBytes, `oidcidentityprovider.idp.supervisor.pinniped.dev`, - tc.expectedError, + tc.expectedErrorSnippets, "OIDCIdentityProvider", resourceName, ) @@ -239,7 +242,7 @@ func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { t, yamlBytes, `ldapidentityprovider.idp.supervisor.pinniped.dev`, - tc.expectedError, + tc.expectedErrorSnippets, "LDAPIdentityProvider", resourceName, ) @@ -254,7 +257,7 @@ func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { t, yamlBytes, `activedirectoryidentityprovider.idp.supervisor.pinniped.dev`, - tc.expectedError, + tc.expectedErrorSnippets, "ActiveDirectoryIdentityProvider", resourceName, ) @@ -264,9 +267,6 @@ func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { // GitHub is nested deeper indentedTLSYAMLForGitHub := strings.ReplaceAll(indentedTLSYAML, "\n", "\n ") - // This is how kubectl shows this error - expectedGitHubError := strings.ReplaceAll(tc.expectedGitHubError, "invalid:\n", "invalid: \n") - resourceName := "test-github-idp-" + testlib.RandHex(t, 7) yamlBytes := []byte(fmt.Sprintf(githubIDPTemplate, env.APIGroupSuffix, resourceName, indentedTLSYAMLForGitHub)) @@ -275,7 +275,7 @@ func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { t, yamlBytes, `githubidentityprovider.idp.supervisor.pinniped.dev`, - expectedGitHubError, + tc.expectedGitHubErrorSnippets, "GitHubIdentityProvider", resourceName, ) From 414ff503ef77b6368a0c44717d6fe5d845c1d926 Mon Sep 17 00:00:00 2001 From: Ryan Richard Date: Wed, 24 Jul 2024 10:00:55 -0700 Subject: [PATCH 42/99] extract some common condition reason string constants --- .../jwtcachefiller/jwtcachefiller.go | 17 +++++++---------- .../webhookcachefiller/webhookcachefiller.go | 11 ++++------- .../conditionsutil/conditions_util.go | 8 ++++++-- .../federation_domain_watcher.go | 11 ++++------- .../github_upstream_watcher.go | 15 +++++++++------ 5 files changed, 30 insertions(+), 32 deletions(-) diff --git a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go index ed61a436c..fce5a0c5e 100644 --- a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go +++ b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go @@ -56,9 +56,6 @@ const ( typeJWKSFetchValid = "JWKSFetchValid" typeAuthenticatorValid = "AuthenticatorValid" - reasonNotReady = "NotReady" - reasonUnableToValidate = "UnableToValidate" - reasonInvalidIssuerURL = "InvalidIssuerURL" reasonInvalidIssuerURLScheme = "InvalidIssuerURLScheme" reasonInvalidIssuerURLFragment = "InvalidIssuerURLContainsFragment" reasonInvalidIssuerURLQueryParams = "InvalidIssuerURLContainsQueryParams" @@ -322,7 +319,7 @@ func (c *jwtCacheFillerController) validateIssuer(issuer string, conditions []*m conditions = append(conditions, &metav1.Condition{ Type: typeIssuerURLValid, Status: metav1.ConditionFalse, - Reason: reasonInvalidIssuerURL, + Reason: conditionsutil.ReasonInvalidIssuerURL, Message: msg, }) return nil, conditions, false @@ -386,7 +383,7 @@ func (c *jwtCacheFillerController) validateProviderDiscovery(ctx context.Context conditions = append(conditions, &metav1.Condition{ Type: typeDiscoveryValid, Status: metav1.ConditionUnknown, - Reason: reasonUnableToValidate, + Reason: conditionsutil.ReasonUnableToValidate, Message: msgUnableToValidate, }) return nil, nil, conditions, nil @@ -421,7 +418,7 @@ func (c *jwtCacheFillerController) validateProviderJWKSURL(provider *coreosoidc. conditions = append(conditions, &metav1.Condition{ Type: typeJWKSURLValid, Status: metav1.ConditionUnknown, - Reason: reasonUnableToValidate, + Reason: conditionsutil.ReasonUnableToValidate, Message: msgUnableToValidate, }) return "", conditions, nil @@ -484,7 +481,7 @@ func (c *jwtCacheFillerController) validateJWKSFetch(ctx context.Context, jwksUR conditions = append(conditions, &metav1.Condition{ Type: typeJWKSFetchValid, Status: metav1.ConditionUnknown, - Reason: reasonUnableToValidate, + Reason: conditionsutil.ReasonUnableToValidate, Message: msgUnableToValidate, }) return nil, conditions, nil @@ -540,7 +537,7 @@ func (c *jwtCacheFillerController) validateJWKSFetch(ctx context.Context, jwksUR conditions = append(conditions, &metav1.Condition{ Type: typeJWKSFetchValid, Status: metav1.ConditionUnknown, - Reason: reasonUnableToValidate, + Reason: conditionsutil.ReasonUnableToValidate, Message: msg, }) return nil, conditions, fmt.Errorf("%s: %w", errText, verifyWithKeySetErr) @@ -559,7 +556,7 @@ func (c *jwtCacheFillerController) newCachedJWTAuthenticator( conditions = append(conditions, &metav1.Condition{ Type: typeAuthenticatorValid, Status: metav1.ConditionUnknown, - Reason: reasonUnableToValidate, + Reason: conditionsutil.ReasonUnableToValidate, Message: msgUnableToValidate, }) return nil, conditions, nil @@ -641,7 +638,7 @@ func (c *jwtCacheFillerController) updateStatus( conditions = append(conditions, &metav1.Condition{ Type: typeReady, Status: metav1.ConditionFalse, - Reason: reasonNotReady, + Reason: conditionsutil.ReasonNotReady, Message: "the JWTAuthenticator is not ready: see other conditions for details", }) } else { diff --git a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go index ac6c615cc..45ab3f30e 100644 --- a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go +++ b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go @@ -50,13 +50,10 @@ const ( typeEndpointURLValid = "EndpointURLValid" typeAuthenticatorValid = "AuthenticatorValid" - reasonNotReady = "NotReady" - reasonUnableToValidate = "UnableToValidate" reasonUnableToCreateClient = "UnableToCreateClient" reasonUnableToInstantiateWebhook = "UnableToInstantiateWebhook" reasonInvalidEndpointURL = "InvalidEndpointURL" reasonInvalidEndpointURLScheme = "InvalidEndpointURLScheme" - reasonUnableToDialServer = "UnableToDialServer" msgUnableToValidate = "unable to validate; see other conditions for details" ) @@ -252,7 +249,7 @@ func newWebhookAuthenticator( conditions = append(conditions, &metav1.Condition{ Type: typeAuthenticatorValid, Status: metav1.ConditionUnknown, - Reason: reasonUnableToValidate, + Reason: conditionsutil.ReasonUnableToValidate, Message: msgUnableToValidate, }) return nil, conditions, nil @@ -324,7 +321,7 @@ func (c *webhookCacheFillerController) validateConnection(certPool *x509.CertPoo conditions = append(conditions, &metav1.Condition{ Type: typeWebhookConnectionValid, Status: metav1.ConditionUnknown, - Reason: reasonUnableToValidate, + Reason: conditionsutil.ReasonUnableToValidate, Message: msgUnableToValidate, }) return conditions, nil @@ -338,7 +335,7 @@ func (c *webhookCacheFillerController) validateConnection(certPool *x509.CertPoo conditions = append(conditions, &metav1.Condition{ Type: typeWebhookConnectionValid, Status: metav1.ConditionFalse, - Reason: reasonUnableToDialServer, + Reason: conditionsutil.ReasonUnableToDialServer, Message: msg, }) return conditions, fmt.Errorf("%s: %w", errText, err) @@ -418,7 +415,7 @@ func (c *webhookCacheFillerController) updateStatus( conditions = append(conditions, &metav1.Condition{ Type: typeReady, Status: metav1.ConditionFalse, - Reason: reasonNotReady, + Reason: conditionsutil.ReasonNotReady, Message: "the WebhookAuthenticator is not ready: see other conditions for details", }) } else { diff --git a/internal/controller/conditionsutil/conditions_util.go b/internal/controller/conditionsutil/conditions_util.go index fd667ca10..91bc24c63 100644 --- a/internal/controller/conditionsutil/conditions_util.go +++ b/internal/controller/conditionsutil/conditions_util.go @@ -12,9 +12,13 @@ import ( "go.pinniped.dev/internal/plog" ) +// Some common reasons shared by conditions of various resources. const ( - // TODO: why only move one here, why not more? - ReasonSuccess = "Success" + ReasonSuccess = "Success" + ReasonNotReady = "NotReady" + ReasonUnableToValidate = "UnableToValidate" + ReasonUnableToDialServer = "UnableToDialServer" + ReasonInvalidIssuerURL = "InvalidIssuerURL" ) // MergeConditions merges conditions into conditionsToUpdate. diff --git a/internal/controller/supervisorconfig/federation_domain_watcher.go b/internal/controller/supervisorconfig/federation_domain_watcher.go index cfd91a3c4..715b2b36f 100644 --- a/internal/controller/supervisorconfig/federation_domain_watcher.go +++ b/internal/controller/supervisorconfig/federation_domain_watcher.go @@ -48,9 +48,6 @@ const ( typeTransformsExpressionsValid = "TransformsExpressionsValid" typeTransformsExamplesPassed = "TransformsExamplesPassed" - reasonNotReady = "NotReady" - reasonUnableToValidate = "UnableToValidate" - reasonInvalidIssuerURL = "InvalidIssuerURL" reasonDuplicateIssuer = "DuplicateIssuer" reasonDifferentSecretRefsFound = "DifferentSecretRefsFound" reasonLegacyConfigurationSuccess = "LegacyConfigurationSuccess" @@ -792,7 +789,7 @@ func appendIssuerURLValidCondition(err error, conditions []*metav1.Condition) [] conditions = append(conditions, &metav1.Condition{ Type: typeIssuerURLValid, Status: metav1.ConditionFalse, - Reason: reasonInvalidIssuerURL, + Reason: conditionsutil.ReasonInvalidIssuerURL, Message: err.Error(), }) } else { @@ -818,7 +815,7 @@ func (c *federationDomainWatcherController) updateStatus( conditions = append(conditions, &metav1.Condition{ Type: typeReady, Status: metav1.ConditionFalse, - Reason: reasonNotReady, + Reason: conditionsutil.ReasonNotReady, Message: "the FederationDomain is not ready: see other conditions for details", }) } else { @@ -885,13 +882,13 @@ func (v *crossFederationDomainConfigValidator) Validate(federationDomain *superv conditions = append(conditions, &metav1.Condition{ Type: typeIssuerIsUnique, Status: metav1.ConditionUnknown, - Reason: reasonUnableToValidate, + Reason: conditionsutil.ReasonUnableToValidate, Message: "unable to check if spec.issuer is unique among all FederationDomains because URL cannot be parsed", }) conditions = append(conditions, &metav1.Condition{ Type: typeOneTLSSecretPerIssuerHostname, Status: metav1.ConditionUnknown, - Reason: reasonUnableToValidate, + Reason: conditionsutil.ReasonUnableToValidate, Message: "unable to check if all FederationDomains are using the same TLS secret when using the same hostname in the spec.issuer URL because URL cannot be parsed", }) return conditions diff --git a/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher.go b/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher.go index d54006cfd..7d8da94be 100644 --- a/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher.go +++ b/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher.go @@ -61,6 +61,9 @@ const ( GitHubConnectionValid string = "GitHubConnectionValid" ClaimsValid string = "ClaimsValid" + reasonInvalid = "Invalid" + reasonInvalidHost = "InvalidHost" + defaultHost = "github.com" defaultApiBaseURL = "https://api.github.com" ) @@ -284,7 +287,7 @@ func validateOrganizationsPolicy(organizationsSpec *idpv1alpha1.GitHubOrganizati return &metav1.Condition{ Type: OrganizationsPolicyValid, Status: metav1.ConditionFalse, - Reason: "Invalid", + Reason: reasonInvalid, Message: "spec.allowAuthentication.organizations.policy must be 'OnlyUsersFromAllowedOrganizations' when spec.allowAuthentication.organizations.allowed has organizations listed", } } @@ -292,7 +295,7 @@ func validateOrganizationsPolicy(organizationsSpec *idpv1alpha1.GitHubOrganizati return &metav1.Condition{ Type: OrganizationsPolicyValid, Status: metav1.ConditionFalse, - Reason: "Invalid", + Reason: reasonInvalid, Message: "spec.allowAuthentication.organizations.policy must be 'AllGitHubUsers' when spec.allowAuthentication.organizations.allowed is empty", } } @@ -397,7 +400,7 @@ func validateHost(gitHubAPIConfig idpv1alpha1.GitHubAPIConfig) (*metav1.Conditio return &metav1.Condition{ Type: HostValid, Status: metav1.ConditionFalse, - Reason: "InvalidHost", + Reason: reasonInvalidHost, Message: fmt.Sprintf("spec.githubAPI.host (%q) is not valid: %s", host, reason), } } @@ -432,7 +435,7 @@ func (c *gitHubWatcherController) validateGitHubConnection( return &metav1.Condition{ Type: GitHubConnectionValid, Status: metav1.ConditionUnknown, - Reason: "UnableToValidate", + Reason: conditionsutil.ReasonUnableToValidate, Message: "unable to validate; see other conditions for details", }, "", nil, nil } @@ -445,7 +448,7 @@ func (c *gitHubWatcherController) validateGitHubConnection( return &metav1.Condition{ Type: GitHubConnectionValid, Status: metav1.ConditionFalse, - Reason: "UnableToDialServer", + Reason: conditionsutil.ReasonUnableToDialServer, Message: fmt.Sprintf("cannot dial server spec.githubAPI.host (%q): %s", address, buildDialErrorMessage(tlsDialErr)), }, "", nil, tlsDialErr } @@ -483,7 +486,7 @@ func validateUserAndGroupAttributes(upstream *idpv1alpha1.GitHubIdentityProvider return &metav1.Condition{ Type: ClaimsValid, Status: metav1.ConditionFalse, - Reason: "Invalid", + Reason: reasonInvalid, Message: message, } } From 60f82d2a55e118e76b318c8ffc6786a00e73a151 Mon Sep 17 00:00:00 2001 From: Joshua Casey Date: Wed, 24 Jul 2024 12:06:05 -0500 Subject: [PATCH 43/99] Fix integration test typo --- test/integration/supervisor_tls_spec_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/integration/supervisor_tls_spec_test.go b/test/integration/supervisor_tls_spec_test.go index a8a6ab607..57543638d 100644 --- a/test/integration/supervisor_tls_spec_test.go +++ b/test/integration/supervisor_tls_spec_test.go @@ -92,7 +92,7 @@ func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { expectedErrorSnippets: []string{`The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.name: Required value`}, expectedGitHubErrorSnippets: []string{ `The %s "%s" is invalid:`, - "* spec.githubAPI.tls.certificateAuthorityDataSource.name: Required value", + "spec.githubAPI.tls.certificateAuthorityDataSource.name: Required value", }, }, { @@ -118,7 +118,7 @@ func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { expectedErrorSnippets: []string{`The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.key: Required value`}, expectedGitHubErrorSnippets: []string{ `The %s "%s" is invalid:`, - "* spec.githubAPI.tls.certificateAuthorityDataSource.key: Required value", + "spec.githubAPI.tls.certificateAuthorityDataSource.key: Required value", }, }, { @@ -144,7 +144,7 @@ func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { expectedErrorSnippets: []string{`The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.kind: Required value`}, expectedGitHubErrorSnippets: []string{ `The %s "%s" is invalid:`, - "* spec.githubAPI.tls.certificateAuthorityDataSource.kind: Required value", + "spec.githubAPI.tls.certificateAuthorityDataSource.kind: Required value", }, }, { From ca2dd2d476838557f5c691d6cd12a425e9a38b41 Mon Sep 17 00:00:00 2001 From: Ryan Richard Date: Wed, 24 Jul 2024 10:27:39 -0700 Subject: [PATCH 44/99] refactor InferSupervisorIssuerURL() func; remove a TODO Co-authored-by: Joshua Casey Co-authored-by: Ashish Amarnath --- test/integration/concierge_tls_spec_test.go | 2 +- test/integration/e2e_test.go | 2 +- test/integration/supervisor_login_test.go | 5 +---- test/integration/supervisor_warnings_test.go | 2 +- test/testlib/env.go | 6 +++--- 5 files changed, 7 insertions(+), 10 deletions(-) diff --git a/test/integration/concierge_tls_spec_test.go b/test/integration/concierge_tls_spec_test.go index b9a42c5e7..669a3357c 100644 --- a/test/integration/concierge_tls_spec_test.go +++ b/test/integration/concierge_tls_spec_test.go @@ -178,7 +178,7 @@ func TestTLSSpecKubeBuilderValidationConcierge_Parallel(t *testing.T) { }) t.Run("apply jwt authenticator", func(t *testing.T) { - _, supervisorIssuer := env.SupervisorUpstreamOIDC.InferTheIssuerURL(t) + _, supervisorIssuer := env.InferSupervisorIssuerURL(t) jwtAuthenticatorResourceName := "test-jwt-authenticator-" + testlib.RandHex(t, 7) jwtAuthenticatorYamlBytes := []byte(fmt.Sprintf(jwtAuthenticatorYamlTemplate, diff --git a/test/integration/e2e_test.go b/test/integration/e2e_test.go index 935b6d5ea..bf4b14c4d 100644 --- a/test/integration/e2e_test.go +++ b/test/integration/e2e_test.go @@ -70,7 +70,7 @@ func TestE2EFullIntegration_Browser(t *testing.T) { // Build pinniped CLI. pinnipedExe := testlib.PinnipedCLIPath(t) - issuerURL, _ := env.SupervisorUpstreamOIDC.InferTheIssuerURL(t) + issuerURL, _ := env.InferSupervisorIssuerURL(t) // Generate a CA bundle with which to serve this provider. t.Logf("generating test CA") diff --git a/test/integration/supervisor_login_test.go b/test/integration/supervisor_login_test.go index 6555143fa..ee4490fa2 100644 --- a/test/integration/supervisor_login_test.go +++ b/test/integration/supervisor_login_test.go @@ -1240,7 +1240,6 @@ func TestSupervisorLogin_Browser(t *testing.T) { }, wantDownstreamIDTokenGroups: env.SupervisorUpstreamActiveDirectory.TestUserIndirectGroupsSAMAccountPlusDomainNames, }, - // TODO: this test is disabled- where can this be run? { name: "active directory IDP using secret of type opaque to source ca bundle with all default options", maybeSkip: skipActiveDirectoryTests, @@ -1282,7 +1281,6 @@ func TestSupervisorLogin_Browser(t *testing.T) { }, wantDownstreamIDTokenGroups: env.SupervisorUpstreamActiveDirectory.TestUserIndirectGroupsSAMAccountPlusDomainNames, }, - // TODO: this test is disabled- where can this be run? { name: "active directory IDP using secret of type TLS to source ca bundle with all default options", maybeSkip: skipActiveDirectoryTests, @@ -1326,7 +1324,6 @@ func TestSupervisorLogin_Browser(t *testing.T) { }, wantDownstreamIDTokenGroups: env.SupervisorUpstreamActiveDirectory.TestUserIndirectGroupsSAMAccountPlusDomainNames, }, - // TODO: this test is disabled- where can this be run? { name: "active directory IDP using configmaps to source ca bundle with all default options", maybeSkip: skipActiveDirectoryTests, @@ -2948,7 +2945,7 @@ func testSupervisorLogin( ctx, cancel := context.WithTimeout(context.Background(), 7*time.Minute) defer cancel() - issuerURL, _ := env.SupervisorUpstreamOIDC.InferTheIssuerURL(t) + issuerURL, _ := env.InferSupervisorIssuerURL(t) // Generate a CA bundle with which to serve this provider. t.Logf("generating test CA") diff --git a/test/integration/supervisor_warnings_test.go b/test/integration/supervisor_warnings_test.go index a2a6d4536..f633d7fb3 100644 --- a/test/integration/supervisor_warnings_test.go +++ b/test/integration/supervisor_warnings_test.go @@ -48,7 +48,7 @@ func TestSupervisorWarnings_Browser(t *testing.T) { pinnipedExe := testlib.PinnipedCLIPath(t) tempDir := t.TempDir() - issuerURL, _ := env.SupervisorUpstreamOIDC.InferTheIssuerURL(t) + issuerURL, _ := env.InferSupervisorIssuerURL(t) // Generate a CA bundle with which to serve this provider. t.Logf("generating test CA") diff --git a/test/testlib/env.go b/test/testlib/env.go index 2e4e57611..23833c783 100644 --- a/test/testlib/env.go +++ b/test/testlib/env.go @@ -84,10 +84,10 @@ type TestOIDCUpstream struct { ExpectedGroups []string `json:"expectedGroups"` } -// InferTheIssuerURL infers the downstream issuer URL from the callback associated with the upstream test client registration. -func (upstream *TestOIDCUpstream) InferTheIssuerURL(t *testing.T) (*url.URL, string) { +// InferSupervisorIssuerURL infers the downstream issuer URL from the callback associated with the upstream test client registration. +func (e *TestEnv) InferSupervisorIssuerURL(t *testing.T) (*url.URL, string) { t.Helper() - issuerURL, err := url.Parse(upstream.CallbackURL) + issuerURL, err := url.Parse(e.SupervisorUpstreamOIDC.CallbackURL) require.NoError(t, err) require.True(t, strings.HasSuffix(issuerURL.Path, "/callback")) issuerURL.Path = strings.TrimSuffix(issuerURL.Path, "/callback") From 06b47a5792b7769f4844368118bf229c2cdfe332 Mon Sep 17 00:00:00 2001 From: Ryan Richard Date: Wed, 24 Jul 2024 16:31:01 -0700 Subject: [PATCH 45/99] jwtcachefiller controller loops over all jwtauthenticators Co-authored-by: Joshua Casey --- .../jwtcachefiller/jwtcachefiller.go | 55 ++- .../jwtcachefiller/jwtcachefiller_test.go | 450 +++++++++++------- .../webhookcachefiller/webhookcachefiller.go | 7 +- .../concierge_jwtauthenticator_status_test.go | 96 +++- 4 files changed, 404 insertions(+), 204 deletions(-) diff --git a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go index fce5a0c5e..ee11a13e6 100644 --- a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go +++ b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go @@ -14,6 +14,7 @@ import ( "net/http" "net/url" "reflect" + "slices" "strings" "time" @@ -21,8 +22,8 @@ import ( "github.com/go-jose/go-jose/v4" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/equality" - apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" utilerrors "k8s.io/apimachinery/pkg/util/errors" "k8s.io/apiserver/pkg/apis/apiserver" "k8s.io/apiserver/pkg/authentication/authenticator" @@ -154,7 +155,7 @@ func New( }, withInformer( jwtAuthenticators, - pinnipedcontroller.MatchAnythingFilter(nil), // nil parent func is fine because each event is distinct + pinnipedcontroller.MatchAnythingFilter(pinnipedcontroller.SingletonQueue()), controllerlib.InformerOption{}, ), withInformer( @@ -165,7 +166,7 @@ func New( corev1.SecretTypeTLS, }, pinnipedcontroller.SingletonQueue(), - ), // nil parent func is fine because each event is distinct + ), controllerlib.InformerOption{}, ), withInformer( @@ -189,24 +190,40 @@ type jwtCacheFillerController struct { // Sync implements controllerlib.Syncer. func (c *jwtCacheFillerController) Sync(ctx controllerlib.Context) error { - obj, err := c.jwtAuthenticators.Lister().Get(ctx.Key.Name) - if err != nil && apierrors.IsNotFound(err) { - c.log.Info("Sync() found that the JWTAuthenticator does not exist yet or was deleted") - return nil - } + jwtAuthenticators, err := c.jwtAuthenticators.Lister().List(labels.Everything()) if err != nil { - // no unit test for this failure - return fmt.Errorf("failed to get JWTAuthenticator %s/%s: %w", ctx.Key.Namespace, ctx.Key.Name, err) + return err } + if len(jwtAuthenticators) == 0 { + c.log.Info("No JWTAuthenticators found") + return nil + } + + // Sort them by name so that order is predictable and therefore output is consistent for tests and logs. + slices.SortStableFunc(jwtAuthenticators, func(a, b *authenticationv1alpha1.JWTAuthenticator) int { + return strings.Compare(a.Name, b.Name) + }) + + var errs []error + for _, jwtAuthenticator := range jwtAuthenticators { + err = c.syncIndividualJWTAuthenticator(ctx.Context, jwtAuthenticator) + if err != nil { + errs = append(errs, fmt.Errorf("error for JWTAuthenticator %s: %w", jwtAuthenticator.Name, err)) + } + } + return utilerrors.NewAggregate(errs) +} + +func (c *jwtCacheFillerController) syncIndividualJWTAuthenticator(ctx context.Context, jwtAuthenticator *authenticationv1alpha1.JWTAuthenticator) error { cacheKey := authncache.Key{ APIGroup: authenticationv1alpha1.GroupName, Kind: "JWTAuthenticator", - Name: ctx.Key.Name, + Name: jwtAuthenticator.Name, } conditions := make([]*metav1.Condition, 0) - certPool, caBundlePEM, conditions, tlsBundleOk := c.validateTLSBundle(obj.Spec.TLS, conditions) + certPool, caBundlePEM, conditions, tlsBundleOk := c.validateTLSBundle(jwtAuthenticator.Spec.TLS, conditions) caBundlePEMSHA256 := sha256.Sum256(caBundlePEM) // note that this will always return the same hash for nil input // Only revalidate and update the cache if the cached authenticator is different from the desired authenticator. @@ -221,10 +238,10 @@ func (c *jwtCacheFillerController) Sync(ctx controllerlib.Context) error { if valueFromCache := c.cache.Get(cacheKey); valueFromCache != nil { jwtAuthenticatorFromCache = c.cacheValueAsJWTAuthenticator(valueFromCache) if jwtAuthenticatorFromCache != nil && - reflect.DeepEqual(jwtAuthenticatorFromCache.spec, &obj.Spec) && + reflect.DeepEqual(jwtAuthenticatorFromCache.spec, &jwtAuthenticator.Spec) && tlsBundleOk && // if there was any error while validating the CA bundle, then run remaining validations and update status jwtAuthenticatorFromCache.caBundlePEMSHA256 == caBundlePEMSHA256 { - c.log.WithValues("jwtAuthenticator", klog.KObj(obj), "issuer", obj.Spec.Issuer). + c.log.WithValues("jwtAuthenticator", klog.KObj(jwtAuthenticator), "issuer", jwtAuthenticator.Spec.Issuer). Info("actual jwt authenticator and desired jwt authenticator are the same") // Stop, no more work to be done. This authenticator is already validated and cached. return nil @@ -232,14 +249,14 @@ func (c *jwtCacheFillerController) Sync(ctx controllerlib.Context) error { } var errs []error - _, conditions, issuerOk := c.validateIssuer(obj.Spec.Issuer, conditions) + _, conditions, issuerOk := c.validateIssuer(jwtAuthenticator.Spec.Issuer, conditions) okSoFar := tlsBundleOk && issuerOk client := phttp.Default(certPool) client.Timeout = 30 * time.Second // copied from Kube OIDC code coreOSCtx := coreosoidc.ClientContext(context.Background(), client) - pJSON, provider, conditions, providerErr := c.validateProviderDiscovery(coreOSCtx, obj.Spec.Issuer, conditions, okSoFar) + pJSON, provider, conditions, providerErr := c.validateProviderDiscovery(coreOSCtx, jwtAuthenticator.Spec.Issuer, conditions, okSoFar) errs = append(errs, providerErr) okSoFar = okSoFar && providerErr == nil @@ -253,7 +270,7 @@ func (c *jwtCacheFillerController) Sync(ctx controllerlib.Context) error { newJWTAuthenticatorForCache, conditions, err := c.newCachedJWTAuthenticator( client, - obj.Spec.DeepCopy(), // deep copy to avoid caching original object + jwtAuthenticator.Spec.DeepCopy(), // deep copy to avoid caching original object keySet, caBundlePEMSHA256, conditions, @@ -267,7 +284,7 @@ func (c *jwtCacheFillerController) Sync(ctx controllerlib.Context) error { c.cache.Delete(cacheKey) } else { c.cache.Store(cacheKey, newJWTAuthenticatorForCache) - c.log.WithValues("jwtAuthenticator", klog.KObj(obj), "issuer", obj.Spec.Issuer). + c.log.WithValues("jwtAuthenticator", klog.KObj(jwtAuthenticator), "issuer", jwtAuthenticator.Spec.Issuer). Info("added new jwt authenticator") } @@ -276,7 +293,7 @@ func (c *jwtCacheFillerController) Sync(ctx controllerlib.Context) error { // removed from the cache, because we do not want any end-user authentications to use a closed authenticator. jwtAuthenticatorFromCache.Close() - err = c.updateStatus(ctx.Context, obj, conditions) + err = c.updateStatus(ctx, jwtAuthenticator, conditions) errs = append(errs, err) // Sync loop errors: diff --git a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller_test.go b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller_test.go index 5e2cbb1c2..01489c69f 100644 --- a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller_test.go +++ b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller_test.go @@ -703,7 +703,6 @@ func TestController(t *testing.T) { tests := []struct { name string cache func(*testing.T, *authncache.Cache, bool) - syncKey controllerlib.Key jwtAuthenticators []runtime.Object secretsAndConfigMaps []runtime.Object // for modifying the clients to hack in arbitrary api responses @@ -713,23 +712,22 @@ func TestController(t *testing.T) { // Errors such as url.Parse of the issuer are not returned as they imply a user error. // Since these errors trigger a resync, we are careful only to return an error when // something can be automatically corrected on a retry (ie an error that might be networking). - wantSyncLoopErr testutil.RequireErrorStringFunc - wantLogs []map[string]any - wantActions func() []coretesting.Action - wantCacheEntries int - wantUsernameClaim string - wantGroupsClaim string - runTestsOnResultingAuthenticator bool + wantSyncLoopErr testutil.RequireErrorStringFunc + wantLogs []map[string]any + wantActions func() []coretesting.Action + wantUsernameClaim string + wantGroupsClaim string + wantNamesOfJWTAuthenticatorsInCache []string + skipTestingCachedAuthenticator bool }{ { - name: "Sync: JWTAuthenticator not found will abort sync loop, no status conditions", - syncKey: controllerlib.Key{Name: "test-name"}, + name: "Sync: no JWTAuthenticators found results in no errors and no status conditions", wantLogs: []map[string]any{ { "level": "info", "timestamp": "2099-08-08T13:57:36.123456Z", "logger": "jwtcachefiller-controller", - "message": "Sync() found that the JWTAuthenticator does not exist yet or was deleted", + "message": "No JWTAuthenticators found", }, }, wantActions: func() []coretesting.Action { @@ -740,8 +738,7 @@ func TestController(t *testing.T) { }, }, { - name: "Sync: valid and unchanged JWTAuthenticator: loop will preserve existing status conditions", - syncKey: controllerlib.Key{Name: "test-name"}, + name: "Sync: valid and unchanged JWTAuthenticator: loop will preserve existing status conditions", jwtAuthenticators: []runtime.Object{ &authenticationv1alpha1.JWTAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -770,11 +767,136 @@ func TestController(t *testing.T) { coretesting.NewWatchAction(jwtAuthenticatorsGVR, "", metav1.ListOptions{}), } }, - wantCacheEntries: 1, + wantNamesOfJWTAuthenticatorsInCache: []string{"test-name"}, }, { - name: "Sync: changed JWTAuthenticator: loop will update timestamps only on relevant statuses", - syncKey: controllerlib.Key{Name: "test-name"}, + name: "Sync: multiple valid and multiple invalid JWTAuthenticators", + jwtAuthenticators: []runtime.Object{ + &authenticationv1alpha1.JWTAuthenticator{ + ObjectMeta: metav1.ObjectMeta{ + Name: "existing-jwt-authenticator", + }, + Spec: *someJWTAuthenticatorSpec, + Status: authenticationv1alpha1.JWTAuthenticatorStatus{ + Conditions: allHappyConditionsSuccess(goodIssuer, frozenMetav1Now, 0), + Phase: "Ready", + }, + }, + &authenticationv1alpha1.JWTAuthenticator{ + ObjectMeta: metav1.ObjectMeta{ + Name: "new-jwt-authenticator", + }, + Spec: *someJWTAuthenticatorSpec, + }, + &authenticationv1alpha1.JWTAuthenticator{ + ObjectMeta: metav1.ObjectMeta{ + Name: "invalid-jwt-authenticator", + }, + Spec: *badIssuerJWKSURIJWTAuthenticatorSpec, + }, + &authenticationv1alpha1.JWTAuthenticator{ + ObjectMeta: metav1.ObjectMeta{ + Name: "another-invalid-jwt-authenticator", + }, + Spec: *badIssuerJWKSURIJWTAuthenticatorSpec, + }, + }, + wantSyncLoopErr: testutil.WantExactErrorString("[" + + `error for JWTAuthenticator another-invalid-jwt-authenticator: could not parse provider jwks_uri: parse "https://.café .com/café/café/café/coffee/jwks.json": invalid character " " in host name` + + ", " + + `error for JWTAuthenticator invalid-jwt-authenticator: could not parse provider jwks_uri: parse "https://.café .com/café/café/café/coffee/jwks.json": invalid character " " in host name` + + "]", + ), + wantLogs: []map[string]any{ + { + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "jwtcachefiller-controller", + "message": "added new jwt authenticator", + "issuer": goodIssuer, + "jwtAuthenticator": map[string]any{ + "name": "existing-jwt-authenticator", + }, + }, + { + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "jwtcachefiller-controller", + "message": "added new jwt authenticator", + "issuer": goodIssuer, + "jwtAuthenticator": map[string]any{ + "name": "new-jwt-authenticator", + }, + }, + }, + wantActions: func() []coretesting.Action { + updateValidStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ + ObjectMeta: metav1.ObjectMeta{ + Name: "new-jwt-authenticator", + }, + Spec: *someJWTAuthenticatorSpec, + Status: authenticationv1alpha1.JWTAuthenticatorStatus{ + Conditions: allHappyConditionsSuccess(goodIssuer, frozenMetav1Now, 0), + Phase: "Ready", + }, + }) + updateValidStatusAction.Subresource = "status" + updateInvalidStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ + ObjectMeta: metav1.ObjectMeta{ + Name: "invalid-jwt-authenticator", + }, + Spec: *badIssuerJWKSURIJWTAuthenticatorSpec, + Status: authenticationv1alpha1.JWTAuthenticatorStatus{ + Conditions: conditionstestutil.Replace( + allHappyConditionsSuccess(goodIssuer, frozenMetav1Now, 0), + []metav1.Condition{ + happyIssuerURLValid(frozenMetav1Now, 0), + sadReadyCondition(frozenMetav1Now, 0), + unknownAuthenticatorValid(frozenMetav1Now, 0), + sadJWKSURLValidParseURI("https://.café .com/café/café/café/coffee/jwks.json", frozenMetav1Now, 0), + unknownJWKSFetch(frozenMetav1Now, 0), + }, + ), + Phase: "Error", + }, + }) + updateInvalidStatusAction.Subresource = "status" + updateValidStatusAction.Subresource = "status" + updateAnotherInvalidStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ + ObjectMeta: metav1.ObjectMeta{ + Name: "another-invalid-jwt-authenticator", + }, + Spec: *badIssuerJWKSURIJWTAuthenticatorSpec, + Status: authenticationv1alpha1.JWTAuthenticatorStatus{ + Conditions: conditionstestutil.Replace( + allHappyConditionsSuccess(goodIssuer, frozenMetav1Now, 0), + []metav1.Condition{ + happyIssuerURLValid(frozenMetav1Now, 0), + sadReadyCondition(frozenMetav1Now, 0), + unknownAuthenticatorValid(frozenMetav1Now, 0), + sadJWKSURLValidParseURI("https://.café .com/café/café/café/coffee/jwks.json", frozenMetav1Now, 0), + unknownJWKSFetch(frozenMetav1Now, 0), + }, + ), + Phase: "Error", + }, + }) + updateAnotherInvalidStatusAction.Subresource = "status" + return []coretesting.Action{ + coretesting.NewListAction(jwtAuthenticatorsGVR, jwtAUthenticatorGVK, "", metav1.ListOptions{}), + coretesting.NewWatchAction(jwtAuthenticatorsGVR, "", metav1.ListOptions{}), + updateAnotherInvalidStatusAction, + updateInvalidStatusAction, + updateValidStatusAction, + } + }, + wantNamesOfJWTAuthenticatorsInCache: []string{ + "existing-jwt-authenticator", + "new-jwt-authenticator", + }, + }, + { + name: "Sync: changed JWTAuthenticator: loop will update timestamps only on relevant statuses", jwtAuthenticators: []runtime.Object{ &authenticationv1alpha1.JWTAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -835,11 +957,10 @@ func TestController(t *testing.T) { updateStatusAction, } }, - wantCacheEntries: 1, + wantNamesOfJWTAuthenticatorsInCache: []string{"test-name"}, }, { - name: "Sync: valid JWTAuthenticator with CA: loop will complete successfully and update status conditions", - syncKey: controllerlib.Key{Name: "test-name"}, + name: "Sync: valid JWTAuthenticator with CA: loop will complete successfully and update status conditions", jwtAuthenticators: []runtime.Object{ &authenticationv1alpha1.JWTAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -876,12 +997,10 @@ func TestController(t *testing.T) { updateStatusAction, } }, - wantCacheEntries: 1, - runTestsOnResultingAuthenticator: true, + wantNamesOfJWTAuthenticatorsInCache: []string{"test-name"}, }, { - name: "Sync: valid JWTAuthenticator with CA from Secret: loop will complete successfully and update status conditions", - syncKey: controllerlib.Key{Name: "test-name"}, + name: "Sync: valid JWTAuthenticator with CA from Secret: loop will complete successfully and update status conditions", jwtAuthenticators: []runtime.Object{ &authenticationv1alpha1.JWTAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -921,12 +1040,10 @@ func TestController(t *testing.T) { updateStatusAction, } }, - wantCacheEntries: 1, - runTestsOnResultingAuthenticator: true, + wantNamesOfJWTAuthenticatorsInCache: []string{"test-name"}, }, { - name: "Sync: valid JWTAuthenticator with CA from ConfigMap: loop will complete successfully and update status conditions", - syncKey: controllerlib.Key{Name: "test-name"}, + name: "Sync: valid JWTAuthenticator with CA from ConfigMap: loop will complete successfully and update status conditions", jwtAuthenticators: []runtime.Object{ &authenticationv1alpha1.JWTAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -966,12 +1083,10 @@ func TestController(t *testing.T) { updateStatusAction, } }, - wantCacheEntries: 1, - runTestsOnResultingAuthenticator: true, + wantNamesOfJWTAuthenticatorsInCache: []string{"test-name"}, }, { - name: "Sync: JWTAuthenticator with custom username claim: loop will complete successfully and update status conditions", - syncKey: controllerlib.Key{Name: "test-name"}, + name: "Sync: JWTAuthenticator with custom username claim: loop will complete successfully and update status conditions", jwtAuthenticators: []runtime.Object{ &authenticationv1alpha1.JWTAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -1008,13 +1123,11 @@ func TestController(t *testing.T) { updateStatusAction, } }, - wantCacheEntries: 1, - wantUsernameClaim: someJWTAuthenticatorSpecWithUsernameClaim.Claims.Username, - runTestsOnResultingAuthenticator: true, + wantUsernameClaim: someJWTAuthenticatorSpecWithUsernameClaim.Claims.Username, + wantNamesOfJWTAuthenticatorsInCache: []string{"test-name"}, }, { - name: "Sync: JWTAuthenticator with custom groups claim: loop will complete successfully and update status conditions", - syncKey: controllerlib.Key{Name: "test-name"}, + name: "Sync: JWTAuthenticator with custom groups claim: loop will complete successfully and update status conditions", jwtAuthenticators: []runtime.Object{ &authenticationv1alpha1.JWTAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -1051,9 +1164,8 @@ func TestController(t *testing.T) { updateStatusAction, } }, - wantCacheEntries: 1, - wantGroupsClaim: someJWTAuthenticatorSpecWithGroupsClaim.Claims.Groups, - runTestsOnResultingAuthenticator: true, + wantGroupsClaim: someJWTAuthenticatorSpecWithGroupsClaim.Claims.Groups, + wantNamesOfJWTAuthenticatorsInCache: []string{"test-name"}, }, { name: "Sync: JWTAuthenticator with new spec fields: loop will close previous instance of JWTAuthenticator and complete successfully and update status conditions", @@ -1070,7 +1182,6 @@ func TestController(t *testing.T) { ) }, wantClose: true, - syncKey: controllerlib.Key{Name: "test-name"}, jwtAuthenticators: []runtime.Object{ &authenticationv1alpha1.JWTAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -1107,8 +1218,7 @@ func TestController(t *testing.T) { updateStatusAction, } }, - wantCacheEntries: 1, - runTestsOnResultingAuthenticator: true, + wantNamesOfJWTAuthenticatorsInCache: []string{"test-name"}, }, { name: "Sync: JWTAuthenticator with external and changed CA bundle: loop will close previous instance of JWTAuthenticator and complete successfully and update status conditions", @@ -1123,7 +1233,6 @@ func TestController(t *testing.T) { ) }, wantClose: true, - syncKey: controllerlib.Key{Name: "test-name"}, jwtAuthenticators: []runtime.Object{ &authenticationv1alpha1.JWTAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -1163,8 +1272,7 @@ func TestController(t *testing.T) { updateStatusAction, } }, - wantCacheEntries: 1, - runTestsOnResultingAuthenticator: true, + wantNamesOfJWTAuthenticatorsInCache: []string{"test-name"}, }, { name: "Sync: JWTAuthenticator with no change: loop will abort early and not update status conditions", @@ -1181,7 +1289,6 @@ func TestController(t *testing.T) { ) }, wantClose: false, - syncKey: controllerlib.Key{Name: "test-name"}, jwtAuthenticators: []runtime.Object{ &authenticationv1alpha1.JWTAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -1206,8 +1313,9 @@ func TestController(t *testing.T) { coretesting.NewWatchAction(jwtAuthenticatorsGVR, "", metav1.ListOptions{}), } }, - wantCacheEntries: 1, - runTestsOnResultingAuthenticator: false, // skip the tests because the authenticator left in the cache is the mock version that was added above + wantNamesOfJWTAuthenticatorsInCache: []string{"test-name"}, + // skip the tests because the authenticator pre-loaded into the cache is the mock version that was added above + skipTestingCachedAuthenticator: true, }, { name: "Sync: authenticator update when cached authenticator is the wrong data type, which should never really happen: loop will complete successfully and update status conditions", @@ -1224,7 +1332,6 @@ func TestController(t *testing.T) { struct{ authenticator.Token }{}, ) }, - syncKey: controllerlib.Key{Name: "test-name"}, jwtAuthenticators: []runtime.Object{ &authenticationv1alpha1.JWTAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -1270,12 +1377,10 @@ func TestController(t *testing.T) { updateStatusAction, } }, - wantCacheEntries: 1, - runTestsOnResultingAuthenticator: true, + wantNamesOfJWTAuthenticatorsInCache: []string{"test-name"}, }, { - name: "Sync: valid JWTAuthenticator without CA: loop will fail to cache the authenticator, will write failed and unknown status conditions, and will enqueue resync", - syncKey: controllerlib.Key{Name: "test-name"}, + name: "Sync: valid JWTAuthenticator without CA: loop will fail to cache the authenticator, will write failed and unknown status conditions, and will enqueue resync", jwtAuthenticators: []runtime.Object{ &authenticationv1alpha1.JWTAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -1314,12 +1419,10 @@ func TestController(t *testing.T) { }, // no explicit logs, this is an issue of config, the user must provide TLS config for the // custom cert provided for this server. - wantSyncLoopErr: testutil.WantSprintfErrorString(`could not perform oidc discovery on provider issuer: Get "%s/.well-known/openid-configuration": tls: failed to verify certificate: x509: certificate signed by unknown authority`, goodIssuer), - wantCacheEntries: 0, + wantSyncLoopErr: testutil.WantSprintfErrorString(`error for JWTAuthenticator test-name: could not perform oidc discovery on provider issuer: Get "%s/.well-known/openid-configuration": tls: failed to verify certificate: x509: certificate signed by unknown authority`, goodIssuer), }, { - name: "validateTLS: JWTAuthenticator with invalid CA: loop will fail, will write failed and unknown status conditions, but will not enqueue a resync due to user config error", - syncKey: controllerlib.Key{Name: "test-name"}, + name: "validateTLS: JWTAuthenticator with invalid CA: loop will fail, will write failed and unknown status conditions, but will not enqueue a resync due to user config error", jwtAuthenticators: []runtime.Object{ &authenticationv1alpha1.JWTAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -1356,11 +1459,9 @@ func TestController(t *testing.T) { updateStatusAction, } }, - wantCacheEntries: 0, }, { - name: "previously valid cached authenticator (which did not specify a CA bundle) changes and becomes invalid due to any problem with the CA bundle: loop will fail sync, will write failed and unknown status conditions, and will remove authenticator from cache", - syncKey: controllerlib.Key{Name: "test-name"}, + name: "previously valid cached authenticator (which did not specify a CA bundle) changes and becomes invalid due to any problem with the CA bundle: loop will fail sync, will write failed and unknown status conditions, and will remove authenticator from cache", cache: func(t *testing.T, cache *authncache.Cache, wantClose bool) { cache.Store( authncache.Key{ @@ -1411,8 +1512,7 @@ func TestController(t *testing.T) { updateStatusAction, } }, - wantClose: true, - wantCacheEntries: 0, + wantClose: true, }, { name: "previously valid cached authenticator's spec changes and becomes invalid for any other reason (this test uses an invalid spec.issuer URL): loop will fail sync, will write failed and unknown status conditions, and will remove authenticator from cache", @@ -1436,7 +1536,6 @@ func TestController(t *testing.T) { Spec: *invalidIssuerJWTAuthenticatorSpec, }, }, - syncKey: controllerlib.Key{Name: "test-name"}, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -1465,8 +1564,8 @@ func TestController(t *testing.T) { updateStatusAction, } }, - wantCacheEntries: 0, // removed from cache - wantClose: true, // the removed cache entry was also closed + wantNamesOfJWTAuthenticatorsInCache: []string{}, // it was removed from the cache + wantClose: true, // the removed cache entry was also closed }, { name: "validateIssuer: parsing error (spec.issuer URL is invalid): loop will fail sync, will write failed and unknown status conditions, but will not enqueue a resync due to user config error", @@ -1478,7 +1577,6 @@ func TestController(t *testing.T) { Spec: *invalidIssuerJWTAuthenticatorSpec, }, }, - syncKey: controllerlib.Key{Name: "test-name"}, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -1518,7 +1616,6 @@ func TestController(t *testing.T) { Spec: *invalidIssuerSchemeJWTAuthenticatorSpec, }, }, - syncKey: controllerlib.Key{Name: "test-name"}, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -1562,7 +1659,6 @@ func TestController(t *testing.T) { }, }, }, - syncKey: controllerlib.Key{Name: "test-name"}, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -1610,7 +1706,6 @@ func TestController(t *testing.T) { }, }, }, - syncKey: controllerlib.Key{Name: "test-name"}, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -1658,7 +1753,6 @@ func TestController(t *testing.T) { }, }, }, - syncKey: controllerlib.Key{Name: "test-name"}, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -1702,7 +1796,6 @@ func TestController(t *testing.T) { Spec: *validIssuerURLButDoesNotExistJWTAuthenticatorSpec, }, }, - syncKey: controllerlib.Key{Name: "test-name"}, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -1732,7 +1825,7 @@ func TestController(t *testing.T) { updateStatusAction, } }, - wantSyncLoopErr: testutil.WantExactErrorString(`could not perform oidc discovery on provider issuer: Get "` + goodIssuer + `/foo/bar/baz/shizzle/.well-known/openid-configuration": tls: failed to verify certificate: x509: certificate signed by unknown authority`), + wantSyncLoopErr: testutil.WantExactErrorString(`error for JWTAuthenticator test-name: could not perform oidc discovery on provider issuer: Get "` + goodIssuer + `/foo/bar/baz/shizzle/.well-known/openid-configuration": tls: failed to verify certificate: x509: certificate signed by unknown authority`), }, { name: "validateProviderDiscovery: excessively long errors truncated: loop will fail sync, will write failed and unknown conditions, and will enqueue new sync", @@ -1748,7 +1841,6 @@ func TestController(t *testing.T) { }, }, }, - syncKey: controllerlib.Key{Name: "test-name"}, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -1783,7 +1875,7 @@ func TestController(t *testing.T) { } }, // not currently truncating the logged err - wantSyncLoopErr: testutil.WantExactErrorString("could not perform oidc discovery on provider issuer: 404 Not Found: \n\t\t \t404 not found page\n\t\t\tlots of text that is at least 300 characters long 0123456789 abcdefghijklmnopqrstuvwxyz lots of text that is at least 300 characters long 0123456789 abcdefghijklmnopqrstuvwxyz lots of text that is at least 300 characters long 0123456789 abcdefghijklmnopqrstuvwxyz lots of text that is at least 300 characters long 0123456789 abcdefghijklmnopqrstuvwxyz lots of text that is at least 300 characters long 0123456789 abcdefghijklmnopqrstuvwxyz lots of text that is at least 300 characters long 0123456789 abcdefghijklmnopqrstuvwxyz lots of text that is at least 300 characters long 0123456789 abcdefghijklmnopqrstuvwxyz lots of text that is at least 300 characters long 0123456789 abcdefghijklmnopqrstuvwxyz should not reach end of string\n\t\t"), + wantSyncLoopErr: testutil.WantExactErrorString("error for JWTAuthenticator test-name: could not perform oidc discovery on provider issuer: 404 Not Found: \n\t\t \t404 not found page\n\t\t\tlots of text that is at least 300 characters long 0123456789 abcdefghijklmnopqrstuvwxyz lots of text that is at least 300 characters long 0123456789 abcdefghijklmnopqrstuvwxyz lots of text that is at least 300 characters long 0123456789 abcdefghijklmnopqrstuvwxyz lots of text that is at least 300 characters long 0123456789 abcdefghijklmnopqrstuvwxyz lots of text that is at least 300 characters long 0123456789 abcdefghijklmnopqrstuvwxyz lots of text that is at least 300 characters long 0123456789 abcdefghijklmnopqrstuvwxyz lots of text that is at least 300 characters long 0123456789 abcdefghijklmnopqrstuvwxyz lots of text that is at least 300 characters long 0123456789 abcdefghijklmnopqrstuvwxyz should not reach end of string\n\t\t"), }, // cannot be tested currently the way the coreos lib works. // the constructor requires an issuer in the payload and validates the issuer matches the actual issuer, @@ -1799,7 +1891,6 @@ func TestController(t *testing.T) { Spec: *badIssuerJWKSURIJWTAuthenticatorSpec, }, }, - syncKey: controllerlib.Key{Name: "test-name"}, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -1827,7 +1918,7 @@ func TestController(t *testing.T) { updateStatusAction, } }, - wantSyncLoopErr: testutil.WantExactErrorString(`could not parse provider jwks_uri: parse "https://.café .com/café/café/café/coffee/jwks.json": invalid character " " in host name`), + wantSyncLoopErr: testutil.WantExactErrorString(`error for JWTAuthenticator test-name: could not parse provider jwks_uri: parse "https://.café .com/café/café/café/coffee/jwks.json": invalid character " " in host name`), }, { name: "validateProviderJWKSURL: invalid scheme, requires 'https': loop will fail sync, will write failed and unknown conditions, and will enqueue new sync", @@ -1839,7 +1930,6 @@ func TestController(t *testing.T) { Spec: *badIssuerJWKSURISchemeJWTAuthenticatorSpec, }, }, - syncKey: controllerlib.Key{Name: "test-name"}, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -1867,7 +1957,7 @@ func TestController(t *testing.T) { updateStatusAction, } }, - wantSyncLoopErr: testutil.WantExactErrorString("jwks_uri http://.café.com/café/café/café/coffee/jwks.json has invalid scheme, require 'https'"), + wantSyncLoopErr: testutil.WantExactErrorString("error for JWTAuthenticator test-name: jwks_uri http://.café.com/café/café/café/coffee/jwks.json has invalid scheme, require 'https'"), }, { name: "validateProviderJWKSURL: remote jwks should not have been able to verify hardcoded test jwt token: loop will fail sync, will write failed and unknown conditions, and will enqueue new sync", @@ -1879,7 +1969,6 @@ func TestController(t *testing.T) { Spec: *badIssuerUsesOurHardcodedPrivateKeyJWTAuthenticatorSpec, }, }, - syncKey: controllerlib.Key{Name: "test-name"}, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -1908,7 +1997,7 @@ func TestController(t *testing.T) { updateStatusAction, } }, - wantSyncLoopErr: testutil.WantExactErrorString("remote jwks should not have been able to verify hardcoded test jwt token"), + wantSyncLoopErr: testutil.WantExactErrorString("error for JWTAuthenticator test-name: remote jwks should not have been able to verify hardcoded test jwt token"), }, { name: "validateJWKSFetch: could not fetch keys: loop will fail sync, will write failed and unknown status conditions, and will enqueue a resync", @@ -1920,7 +2009,6 @@ func TestController(t *testing.T) { Spec: *jwksFetchShouldFailJWTAuthenticatorSpec, }, }, - syncKey: controllerlib.Key{Name: "test-name"}, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -1948,7 +2036,7 @@ func TestController(t *testing.T) { updateStatusAction, } }, - wantSyncLoopErr: testutil.WantExactErrorString("could not fetch keys: fetching keys oidc: get keys failed: 404 Not Found 404 page not found\n"), + wantSyncLoopErr: testutil.WantExactErrorString("error for JWTAuthenticator test-name: could not fetch keys: fetching keys oidc: get keys failed: 404 Not Found 404 page not found\n"), }, { name: "updateStatus: called with matching original and updated conditions: will not make request to update conditions", @@ -1964,7 +2052,6 @@ func TestController(t *testing.T) { }, }, }, - syncKey: controllerlib.Key{Name: "test-name"}, wantLogs: []map[string]any{{ "level": "info", "timestamp": "2099-08-08T13:57:36.123456Z", @@ -1981,7 +2068,7 @@ func TestController(t *testing.T) { coretesting.NewWatchAction(jwtAuthenticatorsGVR, "", metav1.ListOptions{}), } }, - wantCacheEntries: 1, + wantNamesOfJWTAuthenticatorsInCache: []string{"test-name"}, }, { name: "updateStatus: called with different original and updated conditions: will make request to update conditions", @@ -2002,7 +2089,6 @@ func TestController(t *testing.T) { }, }, }, - syncKey: controllerlib.Key{Name: "test-name"}, wantLogs: []map[string]any{{ "level": "info", "timestamp": "2099-08-08T13:57:36.123456Z", @@ -2031,7 +2117,7 @@ func TestController(t *testing.T) { updateStatusAction, } }, - wantCacheEntries: 1, + wantNamesOfJWTAuthenticatorsInCache: []string{"test-name"}, }, { name: "updateStatus: when update request fails: error will enqueue a resync", @@ -2052,7 +2138,6 @@ func TestController(t *testing.T) { }, }, }, - syncKey: controllerlib.Key{Name: "test-name"}, configClient: func(client *conciergefake.Clientset) { client.PrependReactor( "update", @@ -2093,8 +2178,8 @@ func TestController(t *testing.T) { "name": "test-name", }, }}, - wantSyncLoopErr: testutil.WantExactErrorString("some update error"), - wantCacheEntries: 1, + wantSyncLoopErr: testutil.WantExactErrorString("error for JWTAuthenticator test-name: some update error"), + wantNamesOfJWTAuthenticatorsInCache: []string{"test-name"}, }, // cannot be tested the way we are invoking oidc.New as we don't provide enough configuration // knobs to actually invoke the code in a broken way. We always give a good client, good keys, and @@ -2140,7 +2225,7 @@ func TestController(t *testing.T) { kubeInformers.Start(ctx.Done()) controllerlib.TestRunSynchronously(t, controller) - syncCtx := controllerlib.Context{Context: ctx, Key: tt.syncKey} + syncCtx := controllerlib.Context{Context: ctx} if err := controllerlib.TestSync(t, controller, syncCtx); tt.wantSyncLoopErr != nil { testutil.RequireErrorStringFromErr(t, err, tt.wantSyncLoopErr) @@ -2155,7 +2240,7 @@ func TestController(t *testing.T) { require.Fail(t, cmp.Diff(tt.wantActions(), pinnipedAPIClient.Actions()), "actions should be exactly the expected number of actions and also contain the correct resources") } - require.Equal(t, tt.wantCacheEntries, len(cache.Keys()), fmt.Sprintf("expected cache entries is incorrect. wanted:%d, got: %d, keys: %v", tt.wantCacheEntries, len(cache.Keys()), cache.Keys())) + require.Equal(t, len(tt.wantNamesOfJWTAuthenticatorsInCache), len(cache.Keys()), fmt.Sprintf("expected cache entries is incorrect. wanted:%d, got: %d, keys: %v", len(tt.wantNamesOfJWTAuthenticatorsInCache), len(cache.Keys()), cache.Keys())) actualLogLines := testutil.SplitByNewline(log.String()) require.Equal(t, len(tt.wantLogs), len(actualLogLines), "log line count should be correct") @@ -2183,105 +2268,112 @@ func TestController(t *testing.T) { } } - if !tt.runTestsOnResultingAuthenticator { - return // end of test unless we wanted to run tests on the resulting authenticator from the cache - } + for _, name := range tt.wantNamesOfJWTAuthenticatorsInCache { + // We expected the cache to have an entry, so pull that entry from the cache and test it. + expectedCacheKey := authncache.Key{ + APIGroup: authenticationv1alpha1.GroupName, + Kind: "JWTAuthenticator", + Name: name, + } + temp := cache.Get(expectedCacheKey) + require.NotNil(t, temp) + require.IsType(t, &cachedJWTAuthenticator{}, temp) - // We expected the cache to have an entry, so pull that entry from the cache and test it. - expectedCacheKey := authncache.Key{ - APIGroup: authenticationv1alpha1.GroupName, - Kind: "JWTAuthenticator", - Name: syncCtx.Key.Name, - } - cachedAuthenticator, ok := cache.Get(expectedCacheKey).(tokenAuthenticatorCloser) - require.True(t, ok) - require.NotNil(t, cachedAuthenticator) + if tt.skipTestingCachedAuthenticator { + continue // skip the rest of this test for this authenticator + } - // Schedule it to be closed at the end of the test. - t.Cleanup(cachedAuthenticator.Close) + require.NotNil(t, temp.(*cachedJWTAuthenticator).Token) + cachedAuthenticator, ok := temp.(tokenAuthenticatorCloser) + require.True(t, ok) - const ( - goodSubject = "some-subject" - group0 = "some-group-0" - group1 = "some-group-1" - goodUsername = "pinny123" - ) + // Schedule it to be closed at the end of the test. + t.Cleanup(cachedAuthenticator.Close) - if tt.wantUsernameClaim == "" { - tt.wantUsernameClaim = "username" - } + const ( + goodSubject = "some-subject" + group0 = "some-group-0" + group1 = "some-group-1" + goodUsername = "pinny123" + ) - if tt.wantGroupsClaim == "" { - tt.wantGroupsClaim = "groups" - } + if tt.wantUsernameClaim == "" { + tt.wantUsernameClaim = "username" + } - for _, test := range testTableForAuthenticateTokenTests( - t, - goodRSASigningKey, - goodRSASigningAlgo, - goodRSASigningKeyID, - group0, - group1, - goodUsername, - tt.wantUsernameClaim, - tt.wantGroupsClaim, - goodIssuer, - ) { - t.Run(test.name, func(t *testing.T) { - t.Parallel() + if tt.wantGroupsClaim == "" { + tt.wantGroupsClaim = "groups" + } - wellKnownClaims := josejwt.Claims{ - Issuer: goodIssuer, - Subject: goodSubject, - Audience: []string{goodAudience}, - Expiry: josejwt.NewNumericDate(time.Now().Add(time.Hour)), - NotBefore: josejwt.NewNumericDate(time.Now().Add(-time.Hour)), - IssuedAt: josejwt.NewNumericDate(time.Now().Add(-time.Hour)), - } - var groups any - username := goodUsername - if test.jwtClaims != nil { - test.jwtClaims(&wellKnownClaims, &groups, &username) - } + for _, test := range testTableForAuthenticateTokenTests( + t, + goodRSASigningKey, + goodRSASigningAlgo, + goodRSASigningKeyID, + group0, + group1, + goodUsername, + tt.wantUsernameClaim, + tt.wantGroupsClaim, + goodIssuer, + ) { + t.Run(test.name, func(t *testing.T) { + t.Parallel() - var signingKey any = goodECSigningKey - signingAlgo := goodECSigningAlgo - signingKID := goodECSigningKeyID - if test.jwtSignature != nil { - test.jwtSignature(&signingKey, &signingAlgo, &signingKID) - } + wellKnownClaims := josejwt.Claims{ + Issuer: goodIssuer, + Subject: goodSubject, + Audience: []string{goodAudience}, + Expiry: josejwt.NewNumericDate(time.Now().Add(time.Hour)), + NotBefore: josejwt.NewNumericDate(time.Now().Add(-time.Hour)), + IssuedAt: josejwt.NewNumericDate(time.Now().Add(-time.Hour)), + } + var groups any + username := goodUsername + if test.jwtClaims != nil { + test.jwtClaims(&wellKnownClaims, &groups, &username) + } - jwt := createJWT( - t, - signingKey, - signingAlgo, - signingKID, - &wellKnownClaims, - tt.wantGroupsClaim, - groups, - test.distributedGroupsClaimURL, - tt.wantUsernameClaim, - username, - ) + var signingKey any = goodECSigningKey + signingAlgo := goodECSigningAlgo + signingKID := goodECSigningKeyID + if test.jwtSignature != nil { + test.jwtSignature(&signingKey, &signingAlgo, &signingKID) + } - // Loop for a while here to allow the underlying OIDC authenticator to initialize itself asynchronously. - var ( - rsp *authenticator.Response - authenticated bool - err error - ) - _ = wait.PollUntilContextTimeout(context.Background(), 10*time.Millisecond, 5*time.Second, true, func(ctx context.Context) (bool, error) { - rsp, authenticated, err = cachedAuthenticator.AuthenticateToken(context.Background(), jwt) - return !isNotInitialized(err), nil + jwt := createJWT( + t, + signingKey, + signingAlgo, + signingKID, + &wellKnownClaims, + tt.wantGroupsClaim, + groups, + test.distributedGroupsClaimURL, + tt.wantUsernameClaim, + username, + ) + + // Loop for a while here to allow the underlying OIDC authenticator to initialize itself asynchronously. + var ( + rsp *authenticator.Response + authenticated bool + err error + ) + _ = wait.PollUntilContextTimeout(context.Background(), 10*time.Millisecond, 5*time.Second, true, func(ctx context.Context) (bool, error) { + require.NotEmpty(t, cachedAuthenticator) + rsp, authenticated, err = cachedAuthenticator.AuthenticateToken(context.Background(), jwt) + return !isNotInitialized(err), nil + }) + if test.wantErr != nil { + testutil.RequireErrorStringFromErr(t, err, test.wantErr) + } else { + require.NoError(t, err) + require.Equal(t, test.wantResponse, rsp) + require.Equal(t, test.wantAuthenticated, authenticated) + } }) - if test.wantErr != nil { - testutil.RequireErrorStringFromErr(t, err, test.wantErr) - } else { - require.NoError(t, err) - require.Equal(t, test.wantResponse, rsp) - require.Equal(t, test.wantAuthenticated, authenticated) - } - }) + } } }) } diff --git a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go index 45ab3f30e..630364a15 100644 --- a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go +++ b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go @@ -92,7 +92,7 @@ func New( }, withInformer( webhooks, - pinnipedcontroller.MatchAnythingFilter(nil), // nil parent func is fine because each event is distinct + pinnipedcontroller.MatchAnythingFilter(nil), // TODO: use pinnipedcontroller.SingletonQueue() controllerlib.InformerOption{}, ), withInformer( @@ -103,7 +103,7 @@ func New( corev1.SecretTypeTLS, }, pinnipedcontroller.SingletonQueue(), - ), // nil parent func is fine because each event is distinct + ), controllerlib.InformerOption{}, ), withInformer( @@ -127,6 +127,9 @@ type webhookCacheFillerController struct { // Sync implements controllerlib.Syncer. func (c *webhookCacheFillerController) Sync(ctx controllerlib.Context) error { + // TODO: can ctx.Key.Name be the name of a Secret or ConfigMap?????? + // Because the withInformer function calls above for secrets and configmaps use SingletonQueue(), the key will be empty for secrets and configmaps + // Every Sync should loop over all webhookAuthenticators because any could have a CA bundle that was indirectly changed. obj, err := c.webhooks.Lister().Get(ctx.Key.Name) if err != nil && apierrors.IsNotFound(err) { c.log.Info("Sync() found that the WebhookAuthenticator does not exist yet or was deleted") diff --git a/test/integration/concierge_jwtauthenticator_status_test.go b/test/integration/concierge_jwtauthenticator_status_test.go index 94a8f907d..9c6d3297f 100644 --- a/test/integration/concierge_jwtauthenticator_status_test.go +++ b/test/integration/concierge_jwtauthenticator_status_test.go @@ -11,6 +11,7 @@ import ( "time" "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -18,6 +19,94 @@ import ( "go.pinniped.dev/test/testlib" ) +func TestConciergeJWTAuthenticatorWithExternalCABundleStatusIsUpdatedWhenExternalBundleIsUpdated_Parallel(t *testing.T) { + env := testlib.IntegrationEnv(t) + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute) + t.Cleanup(cancel) + + client := testlib.NewKubernetesClientset(t) + + tests := []struct { + name string + caBundleSourceSpecKind string + createResourceForCABundle func(t *testing.T, caBundle string) string + updateCABundle func(t *testing.T, resourceName, caBundle string) + }{ + { + name: "for a CA bundle from a ConfigMap", + caBundleSourceSpecKind: "ConfigMap", + createResourceForCABundle: func(t *testing.T, caBundle string) string { + createdResource := testlib.CreateTestConfigMap(t, env.ConciergeNamespace, "ca-bundle", map[string]string{ + "ca.crt": caBundle, + }) + return createdResource.Name + }, + updateCABundle: func(t *testing.T, resourceName, caBundle string) { + configMap, err := client.CoreV1().ConfigMaps(env.ConciergeNamespace).Get(ctx, resourceName, metav1.GetOptions{}) + require.NoError(t, err) + + configMap.Data["ca.crt"] = caBundle + + _, err = client.CoreV1().ConfigMaps(env.ConciergeNamespace).Update(ctx, configMap, metav1.UpdateOptions{}) + require.NoError(t, err) + }, + }, + { + name: "for a CA bundle from a Secret", + caBundleSourceSpecKind: "Secret", + createResourceForCABundle: func(t *testing.T, caBundle string) string { + createdResource := testlib.CreateTestSecret(t, env.ConciergeNamespace, "ca-bundle", corev1.SecretTypeOpaque, map[string]string{ + "ca.crt": caBundle, + }) + return createdResource.Name + }, + updateCABundle: func(t *testing.T, resourceName, caBundle string) { + secret, err := client.CoreV1().Secrets(env.ConciergeNamespace).Get(ctx, resourceName, metav1.GetOptions{}) + require.NoError(t, err) + + secret.Data["ca.crt"] = []byte(caBundle) + + _, err = client.CoreV1().Secrets(env.ConciergeNamespace).Update(ctx, secret, metav1.UpdateOptions{}) + require.NoError(t, err) + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + t.Parallel() + + // Run several times because there is always a chance that the test could pass because the controller + // will resync every 3 minutes even if it does not pay attention to changes in ConfigMaps and Secrets. + for i := range 3 { + t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { + t.Parallel() + + caBundleResourceName := test.createResourceForCABundle(t, env.SupervisorUpstreamOIDC.CABundle) + + authenticator := testlib.CreateTestJWTAuthenticator(ctx, t, authenticationv1alpha1.JWTAuthenticatorSpec{ + Issuer: env.SupervisorUpstreamOIDC.Issuer, + Audience: "does-not-matter", + TLS: &authenticationv1alpha1.TLSSpec{ + CertificateAuthorityDataSource: &authenticationv1alpha1.CABundleSource{ + Kind: test.caBundleSourceSpecKind, + Name: caBundleResourceName, + Key: "ca.crt", + }, + }, + }, authenticationv1alpha1.JWTAuthenticatorPhaseReady) + + test.updateCABundle(t, caBundleResourceName, "this is not a valid CA bundle value") + testlib.WaitForJWTAuthenticatorStatusPhase(ctx, t, authenticator.Name, authenticationv1alpha1.JWTAuthenticatorPhaseError) + + test.updateCABundle(t, caBundleResourceName, env.SupervisorUpstreamOIDC.CABundle) + testlib.WaitForJWTAuthenticatorStatusPhase(ctx, t, authenticator.Name, authenticationv1alpha1.JWTAuthenticatorPhaseReady) + }) + } + }) + } +} + func TestConciergeJWTAuthenticatorStatus_Parallel(t *testing.T) { env := testlib.IntegrationEnv(t) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute) @@ -25,7 +114,7 @@ func TestConciergeJWTAuthenticatorStatus_Parallel(t *testing.T) { tests := []struct { name string - run func(t *testing.T) + run func(t *testing.T) // TODO: refactor this to make it a proper test table }{ { name: "valid spec with no errors and all good status conditions and phase will result in a jwt authenticator that is ready", @@ -205,10 +294,9 @@ func TestConciergeJWTAuthenticatorStatus_Parallel(t *testing.T) { }, } for _, test := range tests { - tt := test - t.Run(tt.name, func(t *testing.T) { + t.Run(test.name, func(t *testing.T) { t.Parallel() - tt.run(t) + test.run(t) }) } } From adb460b64432525e8ddde0c44b14e80f194abc00 Mon Sep 17 00:00:00 2001 From: Ryan Richard Date: Wed, 24 Jul 2024 16:57:23 -0700 Subject: [PATCH 46/99] refactor integration test to use proper test table --- .../concierge_jwtauthenticator_status_test.go | 305 ++++++++---------- 1 file changed, 142 insertions(+), 163 deletions(-) diff --git a/test/integration/concierge_jwtauthenticator_status_test.go b/test/integration/concierge_jwtauthenticator_status_test.go index 9c6d3297f..0dea96d4d 100644 --- a/test/integration/concierge_jwtauthenticator_status_test.go +++ b/test/integration/concierge_jwtauthenticator_status_test.go @@ -113,190 +113,169 @@ func TestConciergeJWTAuthenticatorStatus_Parallel(t *testing.T) { t.Cleanup(cancel) tests := []struct { - name string - run func(t *testing.T) // TODO: refactor this to make it a proper test table + name string + spec authenticationv1alpha1.JWTAuthenticatorSpec + wantPhase authenticationv1alpha1.JWTAuthenticatorPhase + wantConditions []metav1.Condition }{ { name: "valid spec with no errors and all good status conditions and phase will result in a jwt authenticator that is ready", - run: func(t *testing.T) { - caBundleString := base64.StdEncoding.EncodeToString([]byte(env.SupervisorUpstreamOIDC.CABundle)) - jwtAuthenticator := testlib.CreateTestJWTAuthenticator(ctx, t, authenticationv1alpha1.JWTAuthenticatorSpec{ - Issuer: env.SupervisorUpstreamOIDC.Issuer, - Audience: "some-fake-audience", - TLS: &authenticationv1alpha1.TLSSpec{ - CertificateAuthorityData: caBundleString, - }, - }, authenticationv1alpha1.JWTAuthenticatorPhaseReady) - - testlib.WaitForJWTAuthenticatorStatusConditions( - ctx, t, - jwtAuthenticator.Name, - allSuccessfulJWTAuthenticatorConditions(len(caBundleString) != 0)) + spec: authenticationv1alpha1.JWTAuthenticatorSpec{ + Issuer: env.SupervisorUpstreamOIDC.Issuer, + Audience: "some-fake-audience", + TLS: &authenticationv1alpha1.TLSSpec{ + CertificateAuthorityData: base64.StdEncoding.EncodeToString([]byte(env.SupervisorUpstreamOIDC.CABundle)), + }, }, + wantPhase: authenticationv1alpha1.JWTAuthenticatorPhaseReady, + wantConditions: allSuccessfulJWTAuthenticatorConditions(true), }, { name: "valid spec with invalid CA in TLS config will result in a jwt authenticator that is not ready", - run: func(t *testing.T) { - caBundleString := "invalid base64-encoded data" - jwtAuthenticator := testlib.CreateTestJWTAuthenticator(ctx, t, authenticationv1alpha1.JWTAuthenticatorSpec{ - Issuer: env.SupervisorUpstreamOIDC.Issuer, - Audience: "some-fake-audience", - TLS: &authenticationv1alpha1.TLSSpec{ - CertificateAuthorityData: caBundleString, - }, - }, authenticationv1alpha1.JWTAuthenticatorPhaseError) - - testlib.WaitForJWTAuthenticatorStatusConditions( - ctx, t, - jwtAuthenticator.Name, - replaceSomeConditions( - allSuccessfulJWTAuthenticatorConditions(len(caBundleString) != 0), - []metav1.Condition{ - { - Type: "Ready", - Status: "False", - Reason: "NotReady", - Message: "the JWTAuthenticator is not ready: see other conditions for details", - }, { - Type: "AuthenticatorValid", - Status: "Unknown", - Reason: "UnableToValidate", - Message: "unable to validate; see other conditions for details", - }, { - Type: "JWKSURLValid", - Status: "Unknown", - Reason: "UnableToValidate", - Message: "unable to validate; see other conditions for details", - }, { - Type: "JWKSFetchValid", - Status: "Unknown", - Reason: "UnableToValidate", - Message: "unable to validate; see other conditions for details", - }, { - Type: "DiscoveryURLValid", - Status: "Unknown", - Reason: "UnableToValidate", - Message: "unable to validate; see other conditions for details", - }, { - Type: "TLSConfigurationValid", - Status: "False", - Reason: "InvalidTLSConfig", - Message: "spec.tls.certificateAuthorityData is invalid: illegal base64 data at input byte 7", - }, - }, - )) + spec: authenticationv1alpha1.JWTAuthenticatorSpec{ + Issuer: env.SupervisorUpstreamOIDC.Issuer, + Audience: "some-fake-audience", + TLS: &authenticationv1alpha1.TLSSpec{ + CertificateAuthorityData: "invalid base64-encoded data", + }, }, + wantPhase: authenticationv1alpha1.JWTAuthenticatorPhaseError, + wantConditions: replaceSomeConditions( + allSuccessfulJWTAuthenticatorConditions(true), + []metav1.Condition{ + { + Type: "Ready", + Status: "False", + Reason: "NotReady", + Message: "the JWTAuthenticator is not ready: see other conditions for details", + }, { + Type: "AuthenticatorValid", + Status: "Unknown", + Reason: "UnableToValidate", + Message: "unable to validate; see other conditions for details", + }, { + Type: "JWKSURLValid", + Status: "Unknown", + Reason: "UnableToValidate", + Message: "unable to validate; see other conditions for details", + }, { + Type: "JWKSFetchValid", + Status: "Unknown", + Reason: "UnableToValidate", + Message: "unable to validate; see other conditions for details", + }, { + Type: "DiscoveryURLValid", + Status: "Unknown", + Reason: "UnableToValidate", + Message: "unable to validate; see other conditions for details", + }, { + Type: "TLSConfigurationValid", + Status: "False", + Reason: "InvalidTLSConfig", + Message: "spec.tls.certificateAuthorityData is invalid: illegal base64 data at input byte 7", + }, + }, + ), }, { name: "valid spec with valid CA in TLS config but does not match issuer server will result in a jwt authenticator that is not ready", - run: func(t *testing.T) { - caBundleString := "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURVVENDQWptZ0F3SUJBZ0lWQUpzNStTbVRtaTJXeUI0bGJJRXBXaUs5a1RkUE1BMEdDU3FHU0liM0RRRUIKQ3dVQU1COHhDekFKQmdOVkJBWVRBbFZUTVJBd0RnWURWUVFLREFkUWFYWnZkR0ZzTUI0WERUSXdNRFV3TkRFMgpNamMxT0ZvWERUSTBNRFV3TlRFMk1qYzFPRm93SHpFTE1Ba0dBMVVFQmhNQ1ZWTXhFREFPQmdOVkJBb01CMUJwCmRtOTBZV3d3Z2dFaU1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQkR3QXdnZ0VLQW9JQkFRRERZWmZvWGR4Z2NXTEMKZEJtbHB5a0tBaG9JMlBuUWtsVFNXMno1cGcwaXJjOGFRL1E3MXZzMTRZYStmdWtFTGlvOTRZYWw4R01DdVFrbApMZ3AvUEE5N1VYelhQNDBpK25iNXcwRGpwWWd2dU9KQXJXMno2MFRnWE5NSFh3VHk4ME1SZEhpUFVWZ0VZd0JpCmtkNThzdEFVS1Y1MnBQTU1reTJjNy9BcFhJNmRXR2xjalUvaFBsNmtpRzZ5dEw2REtGYjJQRWV3MmdJM3pHZ2IKOFVVbnA1V05DZDd2WjNVY0ZHNXlsZEd3aGc3cnZ4U1ZLWi9WOEhCMGJmbjlxamlrSVcxWFM4dzdpUUNlQmdQMApYZWhKZmVITlZJaTJtZlczNlVQbWpMdnVKaGpqNDIrdFBQWndvdDkzdWtlcEgvbWpHcFJEVm9wamJyWGlpTUYrCkYxdnlPNGMxQWdNQkFBR2pnWU13Z1lBd0hRWURWUjBPQkJZRUZNTWJpSXFhdVkwajRVWWphWDl0bDJzby9LQ1IKTUI4R0ExVWRJd1FZTUJhQUZNTWJpSXFhdVkwajRVWWphWDl0bDJzby9LQ1JNQjBHQTFVZEpRUVdNQlFHQ0NzRwpBUVVGQndNQ0JnZ3JCZ0VGQlFjREFUQVBCZ05WSFJNQkFmOEVCVEFEQVFIL01BNEdBMVVkRHdFQi93UUVBd0lCCkJqQU5CZ2txaGtpRzl3MEJBUXNGQUFPQ0FRRUFYbEh4M2tIMDZwY2NDTDlEVE5qTnBCYnlVSytGd2R6T2IwWFYKcmpNaGtxdHVmdEpUUnR5T3hKZ0ZKNXhUR3pCdEtKamcrVU1pczBOV0t0VDBNWThVMU45U2c5SDl0RFpHRHBjVQpxMlVRU0Y4dXRQMVR3dnJIUzIrdzB2MUoxdHgrTEFiU0lmWmJCV0xXQ21EODUzRlVoWlFZekkvYXpFM28vd0p1CmlPUklMdUpNUk5vNlBXY3VLZmRFVkhaS1RTWnk3a25FcHNidGtsN3EwRE91eUFWdG9HVnlkb3VUR0FOdFhXK2YKczNUSTJjKzErZXg3L2RZOEJGQTFzNWFUOG5vZnU3T1RTTzdiS1kzSkRBUHZOeFQzKzVZUXJwNGR1Nmh0YUFMbAppOHNaRkhidmxpd2EzdlhxL3p1Y2JEaHEzQzBhZnAzV2ZwRGxwSlpvLy9QUUFKaTZLQT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K" - jwtAuthenticator := testlib.CreateTestJWTAuthenticator(ctx, t, authenticationv1alpha1.JWTAuthenticatorSpec{ - Issuer: env.SupervisorUpstreamOIDC.Issuer, - Audience: "some-fake-audience", - // Some random generated cert - // Issuer: C=US, O=Pivotal - // No SAN provided - TLS: &authenticationv1alpha1.TLSSpec{ - CertificateAuthorityData: caBundleString, - }, - }, authenticationv1alpha1.JWTAuthenticatorPhaseError) - - testlib.WaitForJWTAuthenticatorStatusConditions( - ctx, t, - jwtAuthenticator.Name, - replaceSomeConditions( - allSuccessfulJWTAuthenticatorConditions(len(caBundleString) != 0), - []metav1.Condition{ - { - Type: "Ready", - Status: "False", - Reason: "NotReady", - Message: "the JWTAuthenticator is not ready: see other conditions for details", - }, { - Type: "AuthenticatorValid", - Status: "Unknown", - Reason: "UnableToValidate", - Message: "unable to validate; see other conditions for details", - }, { - Type: "JWKSURLValid", - Status: "Unknown", - Reason: "UnableToValidate", - Message: "unable to validate; see other conditions for details", - }, { - Type: "JWKSFetchValid", - Status: "Unknown", - Reason: "UnableToValidate", - Message: "unable to validate; see other conditions for details", - }, { - Type: "DiscoveryURLValid", - Status: "False", - Reason: "InvalidDiscoveryProbe", - Message: `could not perform oidc discovery on provider issuer: Get "` + env.SupervisorUpstreamOIDC.Issuer + `/.well-known/openid-configuration": tls: failed to verify certificate: x509: certificate signed by unknown authority`, - }, { - Type: "TLSConfigurationValid", - Status: "True", - Reason: "Success", - Message: "spec.tls is valid: loaded TLS configuration", - }, - }, - )) + spec: authenticationv1alpha1.JWTAuthenticatorSpec{ + Issuer: env.SupervisorUpstreamOIDC.Issuer, + Audience: "some-fake-audience", + // Some random generated cert + // Issuer: C=US, O=Pivotal + // No SAN provided + TLS: &authenticationv1alpha1.TLSSpec{ + CertificateAuthorityData: "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURVVENDQWptZ0F3SUJBZ0lWQUpzNStTbVRtaTJXeUI0bGJJRXBXaUs5a1RkUE1BMEdDU3FHU0liM0RRRUIKQ3dVQU1COHhDekFKQmdOVkJBWVRBbFZUTVJBd0RnWURWUVFLREFkUWFYWnZkR0ZzTUI0WERUSXdNRFV3TkRFMgpNamMxT0ZvWERUSTBNRFV3TlRFMk1qYzFPRm93SHpFTE1Ba0dBMVVFQmhNQ1ZWTXhFREFPQmdOVkJBb01CMUJwCmRtOTBZV3d3Z2dFaU1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQkR3QXdnZ0VLQW9JQkFRRERZWmZvWGR4Z2NXTEMKZEJtbHB5a0tBaG9JMlBuUWtsVFNXMno1cGcwaXJjOGFRL1E3MXZzMTRZYStmdWtFTGlvOTRZYWw4R01DdVFrbApMZ3AvUEE5N1VYelhQNDBpK25iNXcwRGpwWWd2dU9KQXJXMno2MFRnWE5NSFh3VHk4ME1SZEhpUFVWZ0VZd0JpCmtkNThzdEFVS1Y1MnBQTU1reTJjNy9BcFhJNmRXR2xjalUvaFBsNmtpRzZ5dEw2REtGYjJQRWV3MmdJM3pHZ2IKOFVVbnA1V05DZDd2WjNVY0ZHNXlsZEd3aGc3cnZ4U1ZLWi9WOEhCMGJmbjlxamlrSVcxWFM4dzdpUUNlQmdQMApYZWhKZmVITlZJaTJtZlczNlVQbWpMdnVKaGpqNDIrdFBQWndvdDkzdWtlcEgvbWpHcFJEVm9wamJyWGlpTUYrCkYxdnlPNGMxQWdNQkFBR2pnWU13Z1lBd0hRWURWUjBPQkJZRUZNTWJpSXFhdVkwajRVWWphWDl0bDJzby9LQ1IKTUI4R0ExVWRJd1FZTUJhQUZNTWJpSXFhdVkwajRVWWphWDl0bDJzby9LQ1JNQjBHQTFVZEpRUVdNQlFHQ0NzRwpBUVVGQndNQ0JnZ3JCZ0VGQlFjREFUQVBCZ05WSFJNQkFmOEVCVEFEQVFIL01BNEdBMVVkRHdFQi93UUVBd0lCCkJqQU5CZ2txaGtpRzl3MEJBUXNGQUFPQ0FRRUFYbEh4M2tIMDZwY2NDTDlEVE5qTnBCYnlVSytGd2R6T2IwWFYKcmpNaGtxdHVmdEpUUnR5T3hKZ0ZKNXhUR3pCdEtKamcrVU1pczBOV0t0VDBNWThVMU45U2c5SDl0RFpHRHBjVQpxMlVRU0Y4dXRQMVR3dnJIUzIrdzB2MUoxdHgrTEFiU0lmWmJCV0xXQ21EODUzRlVoWlFZekkvYXpFM28vd0p1CmlPUklMdUpNUk5vNlBXY3VLZmRFVkhaS1RTWnk3a25FcHNidGtsN3EwRE91eUFWdG9HVnlkb3VUR0FOdFhXK2YKczNUSTJjKzErZXg3L2RZOEJGQTFzNWFUOG5vZnU3T1RTTzdiS1kzSkRBUHZOeFQzKzVZUXJwNGR1Nmh0YUFMbAppOHNaRkhidmxpd2EzdlhxL3p1Y2JEaHEzQzBhZnAzV2ZwRGxwSlpvLy9QUUFKaTZLQT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", + }, }, + wantPhase: authenticationv1alpha1.JWTAuthenticatorPhaseError, + wantConditions: replaceSomeConditions( + allSuccessfulJWTAuthenticatorConditions(true), + []metav1.Condition{ + { + Type: "Ready", + Status: "False", + Reason: "NotReady", + Message: "the JWTAuthenticator is not ready: see other conditions for details", + }, { + Type: "AuthenticatorValid", + Status: "Unknown", + Reason: "UnableToValidate", + Message: "unable to validate; see other conditions for details", + }, { + Type: "JWKSURLValid", + Status: "Unknown", + Reason: "UnableToValidate", + Message: "unable to validate; see other conditions for details", + }, { + Type: "JWKSFetchValid", + Status: "Unknown", + Reason: "UnableToValidate", + Message: "unable to validate; see other conditions for details", + }, { + Type: "DiscoveryURLValid", + Status: "False", + Reason: "InvalidDiscoveryProbe", + Message: `could not perform oidc discovery on provider issuer: Get "` + env.SupervisorUpstreamOIDC.Issuer + `/.well-known/openid-configuration": tls: failed to verify certificate: x509: certificate signed by unknown authority`, + }, { + Type: "TLSConfigurationValid", + Status: "True", + Reason: "Success", + Message: "spec.tls is valid: loaded TLS configuration", + }, + }, + ), }, { name: "invalid with bad issuer will result in a jwt authenticator that is not ready", - run: func(t *testing.T) { - caBundleString := base64.StdEncoding.EncodeToString([]byte(env.SupervisorUpstreamOIDC.CABundle)) - fakeIssuerURL := "https://127.0.0.1:443/some-fake-issuer" - jwtAuthenticator := testlib.CreateTestJWTAuthenticator(ctx, t, authenticationv1alpha1.JWTAuthenticatorSpec{ - Issuer: fakeIssuerURL, - Audience: "some-fake-audience", - TLS: &authenticationv1alpha1.TLSSpec{ - CertificateAuthorityData: caBundleString, - }, - }, authenticationv1alpha1.JWTAuthenticatorPhaseError) - - testlib.WaitForJWTAuthenticatorStatusConditions( - ctx, t, - jwtAuthenticator.Name, - replaceSomeConditions( - allSuccessfulJWTAuthenticatorConditions(len(caBundleString) != 0), - []metav1.Condition{ - { - Type: "Ready", - Status: "False", - Reason: "NotReady", - Message: "the JWTAuthenticator is not ready: see other conditions for details", - }, { - Type: "AuthenticatorValid", - Status: "Unknown", - Reason: "UnableToValidate", - Message: "unable to validate; see other conditions for details", - }, { - Type: "JWKSURLValid", - Status: "Unknown", - Reason: "UnableToValidate", - Message: "unable to validate; see other conditions for details", - }, { - Type: "JWKSFetchValid", - Status: "Unknown", - Reason: "UnableToValidate", - Message: "unable to validate; see other conditions for details", - }, { - Type: "DiscoveryURLValid", - Status: "False", - Reason: "InvalidDiscoveryProbe", - Message: fmt.Sprintf(`could not perform oidc discovery on provider issuer: Get "%s/.well-known/openid-configuration": dial tcp 127.0.0.1:443: connect: connection refused`, fakeIssuerURL), - }, - }, - )) + spec: authenticationv1alpha1.JWTAuthenticatorSpec{ + Issuer: env.SupervisorUpstreamOIDC.Issuer + "/this-is-the-wrong-path", + Audience: "some-fake-audience", + TLS: &authenticationv1alpha1.TLSSpec{ + CertificateAuthorityData: base64.StdEncoding.EncodeToString([]byte(env.SupervisorUpstreamOIDC.CABundle)), + }, }, + wantPhase: authenticationv1alpha1.JWTAuthenticatorPhaseError, + wantConditions: replaceSomeConditions( + allSuccessfulJWTAuthenticatorConditions(true), + []metav1.Condition{ + { + Type: "Ready", + Status: "False", + Reason: "NotReady", + Message: "the JWTAuthenticator is not ready: see other conditions for details", + }, { + Type: "AuthenticatorValid", + Status: "Unknown", + Reason: "UnableToValidate", + Message: "unable to validate; see other conditions for details", + }, { + Type: "JWKSURLValid", + Status: "Unknown", + Reason: "UnableToValidate", + Message: "unable to validate; see other conditions for details", + }, { + Type: "JWKSFetchValid", + Status: "Unknown", + Reason: "UnableToValidate", + Message: "unable to validate; see other conditions for details", + }, { + Type: "DiscoveryURLValid", + Status: "False", + Reason: "InvalidDiscoveryProbe", + Message: "could not perform oidc discovery on provider issuer: 404 Not Found: 404 page not found\n", + }, + }, + ), }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { t.Parallel() - test.run(t) + + jwtAuthenticator := testlib.CreateTestJWTAuthenticator(ctx, t, test.spec, test.wantPhase) + testlib.WaitForJWTAuthenticatorStatusConditions(ctx, t, jwtAuthenticator.Name, test.wantConditions) }) } } From 9420bfde5b5405b569eb17e69d75104a242ed47c Mon Sep 17 00:00:00 2001 From: Joshua Casey Date: Wed, 24 Jul 2024 22:16:48 -0500 Subject: [PATCH 47/99] webhookcachefiller controller loops over all webhookauthenticators --- .../webhookcachefiller/webhookcachefiller.go | 65 ++-- .../webhookcachefiller_test.go | 348 +++++++++++------- ...cierge_webhookauthenticator_status_test.go | 97 ++++- 3 files changed, 354 insertions(+), 156 deletions(-) diff --git a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go index 630364a15..fa47f1c74 100644 --- a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go +++ b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go @@ -12,13 +12,15 @@ import ( "fmt" "net/url" "reflect" + "slices" + "strings" "time" k8sauthv1beta1 "k8s.io/api/authentication/v1beta1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/equality" - apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" utilerrors "k8s.io/apimachinery/pkg/util/errors" k8snetutil "k8s.io/apimachinery/pkg/util/net" "k8s.io/apiserver/pkg/authentication/authenticator" @@ -69,7 +71,7 @@ func New( namespace string, cache *authncache.Cache, client conciergeclientset.Interface, - webhooks authinformers.WebhookAuthenticatorInformer, + webhookInformer authinformers.WebhookAuthenticatorInformer, secretInformer corev1informers.SecretInformer, configMapInformer corev1informers.ConfigMapInformer, withInformer pinnipedcontroller.WithInformerOptionFunc, @@ -83,7 +85,7 @@ func New( namespace: namespace, cache: cache, client: client, - webhooks: webhooks, + webhookInformer: webhookInformer, secretInformer: secretInformer, configMapInformer: configMapInformer, clock: clock, @@ -91,8 +93,8 @@ func New( }, }, withInformer( - webhooks, - pinnipedcontroller.MatchAnythingFilter(nil), // TODO: use pinnipedcontroller.SingletonQueue() + webhookInformer, + pinnipedcontroller.MatchAnythingFilter(pinnipedcontroller.SingletonQueue()), controllerlib.InformerOption{}, ), withInformer( @@ -117,7 +119,7 @@ func New( type webhookCacheFillerController struct { namespace string cache *authncache.Cache - webhooks authinformers.WebhookAuthenticatorInformer + webhookInformer authinformers.WebhookAuthenticatorInformer secretInformer corev1informers.SecretInformer configMapInformer corev1informers.ConfigMapInformer client conciergeclientset.Interface @@ -127,27 +129,40 @@ type webhookCacheFillerController struct { // Sync implements controllerlib.Syncer. func (c *webhookCacheFillerController) Sync(ctx controllerlib.Context) error { - // TODO: can ctx.Key.Name be the name of a Secret or ConfigMap?????? - // Because the withInformer function calls above for secrets and configmaps use SingletonQueue(), the key will be empty for secrets and configmaps - // Every Sync should loop over all webhookAuthenticators because any could have a CA bundle that was indirectly changed. - obj, err := c.webhooks.Lister().Get(ctx.Key.Name) - if err != nil && apierrors.IsNotFound(err) { - c.log.Info("Sync() found that the WebhookAuthenticator does not exist yet or was deleted") - return nil - } + webhookAuthenticators, err := c.webhookInformer.Lister().List(labels.Everything()) if err != nil { - // no unit test for this failure - return fmt.Errorf("failed to get WebhookAuthenticator %s/%s: %w", ctx.Key.Namespace, ctx.Key.Name, err) + return err } + if len(webhookAuthenticators) == 0 { + c.log.Info("No WebhookAuthenticators found") + return nil + } + + // Sort them by name so that order is predictable and therefore output is consistent for tests and logs. + slices.SortStableFunc(webhookAuthenticators, func(a, b *authenticationv1alpha1.WebhookAuthenticator) int { + return strings.Compare(a.Name, b.Name) + }) + + var errs []error + for _, webhookAuthenticator := range webhookAuthenticators { + err = c.syncIndividualWebhookAuthenticator(ctx.Context, webhookAuthenticator) + if err != nil { + errs = append(errs, fmt.Errorf("error for WebhookAuthenticator %s: %w", webhookAuthenticator.Name, err)) + } + } + return utilerrors.NewAggregate(errs) +} + +func (c *webhookCacheFillerController) syncIndividualWebhookAuthenticator(ctx context.Context, webhookAuthenticator *authenticationv1alpha1.WebhookAuthenticator) error { cacheKey := authncache.Key{ APIGroup: authenticationv1alpha1.GroupName, Kind: "WebhookAuthenticator", - Name: ctx.Key.Name, + Name: webhookAuthenticator.Name, } conditions := make([]*metav1.Condition, 0) - certPool, caBundlePEM, conditions, tlsBundleOk := c.validateTLSBundle(obj.Spec.TLS, conditions) + certPool, caBundlePEM, conditions, tlsBundleOk := c.validateTLSBundle(webhookAuthenticator.Spec.TLS, conditions) caBundlePEMSHA256 := sha256.Sum256(caBundlePEM) // note that this will always return the same hash for nil input // Only revalidate and update the cache if the cached authenticator is different from the desired authenticator. @@ -161,10 +176,10 @@ func (c *webhookCacheFillerController) Sync(ctx controllerlib.Context) error { if valueFromCache := c.cache.Get(cacheKey); valueFromCache != nil { webhookAuthenticatorFromCache := c.cacheValueAsWebhookAuthenticator(valueFromCache) if webhookAuthenticatorFromCache != nil && - reflect.DeepEqual(webhookAuthenticatorFromCache.spec, &obj.Spec) && + reflect.DeepEqual(webhookAuthenticatorFromCache.spec, &webhookAuthenticator.Spec) && tlsBundleOk && // if there was any error while validating the CA bundle, then run remaining validations and update status webhookAuthenticatorFromCache.caBundlePEMSHA256 == caBundlePEMSHA256 { - c.log.WithValues("webhookAuthenticator", klog.KObj(obj), "endpoint", obj.Spec.Endpoint). + c.log.WithValues("webhookAuthenticator", klog.KObj(webhookAuthenticator), "endpoint", webhookAuthenticator.Spec.Endpoint). Info("actual webhook authenticator and desired webhook authenticator are the same") // Stop, no more work to be done. This authenticator is already validated and cached. return nil @@ -172,7 +187,7 @@ func (c *webhookCacheFillerController) Sync(ctx controllerlib.Context) error { } var errs []error - endpointHostPort, conditions, endpointOk := c.validateEndpoint(obj.Spec.Endpoint, conditions) + endpointHostPort, conditions, endpointOk := c.validateEndpoint(webhookAuthenticator.Spec.Endpoint, conditions) okSoFar := tlsBundleOk && endpointOk conditions, tlsNegotiateErr := c.validateConnection(certPool, endpointHostPort, conditions, okSoFar) @@ -182,7 +197,7 @@ func (c *webhookCacheFillerController) Sync(ctx controllerlib.Context) error { newWebhookAuthenticatorForCache, conditions, err := newWebhookAuthenticator( // Note that we use the whole URL when constructing the webhook client, // not just the host and port that we validated above. We need the path, etc. - obj.Spec.Endpoint, + webhookAuthenticator.Spec.Endpoint, caBundlePEM, conditions, okSoFar, @@ -197,14 +212,14 @@ func (c *webhookCacheFillerController) Sync(ctx controllerlib.Context) error { } else { c.cache.Store(cacheKey, &cachedWebhookAuthenticator{ Token: newWebhookAuthenticatorForCache, - spec: obj.Spec.DeepCopy(), // deep copy to avoid caching original object + spec: webhookAuthenticator.Spec.DeepCopy(), // deep copy to avoid caching original object caBundlePEMSHA256: caBundlePEMSHA256, }) - c.log.WithValues("webhook", klog.KObj(obj), "endpoint", obj.Spec.Endpoint). + c.log.WithValues("webhook", klog.KObj(webhookAuthenticator), "endpoint", webhookAuthenticator.Spec.Endpoint). Info("added new webhook authenticator") } - err = c.updateStatus(ctx.Context, obj, conditions) + err = c.updateStatus(ctx, webhookAuthenticator, conditions) errs = append(errs, err) // Sync loop errors: diff --git a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go index f1dd871d4..5b4990201 100644 --- a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go +++ b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go @@ -405,27 +405,26 @@ func TestController(t *testing.T) { } tests := []struct { - name string - cache func(*testing.T, *authncache.Cache) - syncKey controllerlib.Key - webhooks []runtime.Object - secretsAndConfigMaps []runtime.Object + name string + cache func(*testing.T, *authncache.Cache) + webhookAuthenticators []runtime.Object + secretsAndConfigMaps []runtime.Object // for modifying the clients to hack in arbitrary api responses - configClient func(*conciergefake.Clientset) - wantSyncLoopErr testutil.RequireErrorStringFunc - wantLogs []map[string]any - wantActions func() []coretesting.Action - wantCacheEntries int + configClient func(*conciergefake.Clientset) + wantSyncLoopErr testutil.RequireErrorStringFunc + wantLogs []map[string]any + wantActions func() []coretesting.Action + // random comment so lines above don't have huge indents + wantNamesOfWebhookAuthenticatorsInCache []string }{ { - name: "Sync: WebhookAuthenticator not found will abort sync loop, no status conditions", - syncKey: controllerlib.Key{Name: "test-name"}, + name: "Sync: No WebhookAuthenticators found results in no errors and no status conditions", wantLogs: []map[string]any{ { "level": "info", "timestamp": "2099-08-08T13:57:36.123456Z", "logger": "webhookcachefiller-controller", - "message": "Sync() found that the WebhookAuthenticator does not exist yet or was deleted", + "message": "No WebhookAuthenticators found", }, }, wantActions: func() []coretesting.Action { @@ -434,12 +433,11 @@ func TestController(t *testing.T) { coretesting.NewWatchAction(webhookAuthenticatorGVR, "", metav1.ListOptions{}), } }, - wantCacheEntries: 0, + wantNamesOfWebhookAuthenticatorsInCache: []string{}, }, { - name: "Sync: valid and unchanged WebhookAuthenticator: loop will preserve existing status conditions", - syncKey: controllerlib.Key{Name: "test-name"}, - webhooks: []runtime.Object{ + name: "Sync: valid and unchanged WebhookAuthenticator: loop will preserve existing status conditions", + webhookAuthenticators: []runtime.Object{ &authenticationv1alpha1.WebhookAuthenticator{ ObjectMeta: metav1.ObjectMeta{ Name: "test-name", @@ -469,12 +467,129 @@ func TestController(t *testing.T) { coretesting.NewWatchAction(webhookAuthenticatorGVR, "", metav1.ListOptions{}), } }, - wantCacheEntries: 1, + wantNamesOfWebhookAuthenticatorsInCache: []string{"test-name"}, }, { - name: "Sync: valid WebhookAuthenticator with CA from Secret: loop will complete successfully and update status conditions", - syncKey: controllerlib.Key{Name: "test-name"}, - webhooks: []runtime.Object{ + name: "Sync: multiple valid and multiple invalid WebhookAuthenticators", + webhookAuthenticators: []runtime.Object{ + &authenticationv1alpha1.WebhookAuthenticator{ + ObjectMeta: metav1.ObjectMeta{ + Name: "existing-webhook-authenticator", + }, + Spec: goodWebhookAuthenticatorSpecWithCA, + Status: authenticationv1alpha1.WebhookAuthenticatorStatus{ + Conditions: allHappyConditionsSuccess(goodWebhookDefaultServingCertEndpoint, frozenMetav1Now, 0), + Phase: "Ready", + }, + }, + &authenticationv1alpha1.WebhookAuthenticator{ + ObjectMeta: metav1.ObjectMeta{ + Name: "new-webhook-authenticator", + }, + Spec: goodWebhookAuthenticatorSpecWithCA, + }, + &authenticationv1alpha1.WebhookAuthenticator{ + ObjectMeta: metav1.ObjectMeta{ + Name: "invalid-webhook-authenticator", + }, + Spec: badWebhookAuthenticatorSpecInvalidTLS, + }, + &authenticationv1alpha1.WebhookAuthenticator{ + ObjectMeta: metav1.ObjectMeta{ + Name: "another-invalid-webhook-authenticator", + }, + Spec: badWebhookAuthenticatorSpecInvalidTLS, + }, + }, + wantLogs: []map[string]any{ + { + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "webhookcachefiller-controller", + "message": "added new webhook authenticator", + "endpoint": goodWebhookDefaultServingCertEndpoint, + "webhook": map[string]any{ + "name": "existing-webhook-authenticator", + }, + }, + { + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "webhookcachefiller-controller", + "message": "added new webhook authenticator", + "endpoint": goodWebhookDefaultServingCertEndpoint, + "webhook": map[string]any{ + "name": "new-webhook-authenticator", + }, + }, + }, + wantActions: func() []coretesting.Action { + updateValidStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ + ObjectMeta: metav1.ObjectMeta{ + Name: "new-webhook-authenticator", + }, + Spec: goodWebhookAuthenticatorSpecWithCA, + Status: authenticationv1alpha1.WebhookAuthenticatorStatus{ + Conditions: allHappyConditionsSuccess(goodWebhookDefaultServingCertEndpoint, frozenMetav1Now, 0), + Phase: "Ready", + }, + }) + updateValidStatusAction.Subresource = "status" + updateInvalidStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ + ObjectMeta: metav1.ObjectMeta{ + Name: "invalid-webhook-authenticator", + }, + Spec: badWebhookAuthenticatorSpecInvalidTLS, + Status: authenticationv1alpha1.WebhookAuthenticatorStatus{ + Conditions: conditionstestutil.Replace( + allHappyConditionsSuccess(goodWebhookDefaultServingCertEndpoint, frozenMetav1Now, 0), + []metav1.Condition{ + sadTLSConfigurationValid(frozenMetav1Now, 0), + unknownWebhookConnectionValid(frozenMetav1Now, 0), + unknownAuthenticatorValid(frozenMetav1Now, 0), + sadReadyCondition(frozenMetav1Now, 0), + }, + ), + Phase: "Error", + }, + }) + updateInvalidStatusAction.Subresource = "status" + updateValidStatusAction.Subresource = "status" + updateAnotherInvalidStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ + ObjectMeta: metav1.ObjectMeta{ + Name: "another-invalid-webhook-authenticator", + }, + Spec: badWebhookAuthenticatorSpecInvalidTLS, + Status: authenticationv1alpha1.WebhookAuthenticatorStatus{ + Conditions: conditionstestutil.Replace( + allHappyConditionsSuccess(goodWebhookDefaultServingCertEndpoint, frozenMetav1Now, 0), + []metav1.Condition{ + sadTLSConfigurationValid(frozenMetav1Now, 0), + unknownWebhookConnectionValid(frozenMetav1Now, 0), + unknownAuthenticatorValid(frozenMetav1Now, 0), + sadReadyCondition(frozenMetav1Now, 0), + }, + ), + Phase: "Error", + }, + }) + updateAnotherInvalidStatusAction.Subresource = "status" + return []coretesting.Action{ + coretesting.NewListAction(webhookAuthenticatorGVR, webhookAuthenticatorGVK, "", metav1.ListOptions{}), + coretesting.NewWatchAction(webhookAuthenticatorGVR, "", metav1.ListOptions{}), + updateAnotherInvalidStatusAction, + updateInvalidStatusAction, + updateValidStatusAction, + } + }, + wantNamesOfWebhookAuthenticatorsInCache: []string{ + "existing-webhook-authenticator", + "new-webhook-authenticator", + }, + }, + { + name: "Sync: valid WebhookAuthenticator with CA from Secret: loop will complete successfully and update status conditions", + webhookAuthenticators: []runtime.Object{ &authenticationv1alpha1.WebhookAuthenticator{ ObjectMeta: metav1.ObjectMeta{ Name: "test-name", @@ -515,12 +630,11 @@ func TestController(t *testing.T) { updateStatusAction, } }, - wantCacheEntries: 1, + wantNamesOfWebhookAuthenticatorsInCache: []string{"test-name"}, }, { - name: "Sync: valid WebhookAuthenticator with CA from ConfigMap: loop will complete successfully and update status conditions", - syncKey: controllerlib.Key{Name: "test-name"}, - webhooks: []runtime.Object{ + name: "Sync: valid WebhookAuthenticator with CA from ConfigMap: loop will complete successfully and update status conditions", + webhookAuthenticators: []runtime.Object{ &authenticationv1alpha1.WebhookAuthenticator{ ObjectMeta: metav1.ObjectMeta{ Name: "test-name", @@ -561,11 +675,10 @@ func TestController(t *testing.T) { updateStatusAction, } }, - wantCacheEntries: 1, + wantNamesOfWebhookAuthenticatorsInCache: []string{"test-name"}, }, { - name: "Sync: valid WebhookAuthenticator with external and changed CA bundle: loop will complete successfully and update status conditions", - syncKey: controllerlib.Key{Name: "test-name"}, + name: "Sync: valid WebhookAuthenticator with external and changed CA bundle: loop will complete successfully and update status conditions", cache: func(t *testing.T, cache *authncache.Cache) { cache.Store( authncache.Key{ @@ -576,7 +689,7 @@ func TestController(t *testing.T) { newCacheValue(t, goodWebhookAuthenticatorSpecWithCAFromConfigMap, "some-stale-ca-bundle-pem-content-from-secret"), ) }, - webhooks: []runtime.Object{ + webhookAuthenticators: []runtime.Object{ &authenticationv1alpha1.WebhookAuthenticator{ ObjectMeta: metav1.ObjectMeta{ Name: "test-name", @@ -617,11 +730,10 @@ func TestController(t *testing.T) { updateStatusAction, } }, - wantCacheEntries: 1, + wantNamesOfWebhookAuthenticatorsInCache: []string{"test-name"}, }, { - name: "previously valid cached authenticator (which did not specify a CA bundle) changes and becomes invalid due to any problem with the CA bundle: loop will fail sync, will write failed and unknown status conditions, and will remove authenticator from cache", - syncKey: controllerlib.Key{Name: "test-name"}, + name: "previously valid cached authenticator (which did not specify a CA bundle) changes and becomes invalid due to any problem with the CA bundle: loop will fail sync, will write failed and unknown status conditions, and will remove authenticator from cache", cache: func(t *testing.T, cache *authncache.Cache) { cache.Store( authncache.Key{ @@ -636,7 +748,7 @@ func TestController(t *testing.T) { newCacheValue(t, badWebhookAuthenticatorSpecInvalidTLS, ""), ) }, - webhooks: []runtime.Object{ + webhookAuthenticators: []runtime.Object{ &authenticationv1alpha1.WebhookAuthenticator{ ObjectMeta: metav1.ObjectMeta{ Name: "test-name", @@ -670,7 +782,7 @@ func TestController(t *testing.T) { updateStatusAction, } }, - wantCacheEntries: 0, + wantNamesOfWebhookAuthenticatorsInCache: []string{}, }, { name: "Sync: valid and unchanged WebhookAuthenticator which was already cached: skips any updates to status or cache", @@ -686,8 +798,7 @@ func TestController(t *testing.T) { newCacheValue(t, goodWebhookAuthenticatorSpecWithCA, string(oldCA)), ) }, - syncKey: controllerlib.Key{Name: "test-name"}, - webhooks: []runtime.Object{ + webhookAuthenticators: []runtime.Object{ &authenticationv1alpha1.WebhookAuthenticator{ ObjectMeta: metav1.ObjectMeta{ Name: "test-name", @@ -717,7 +828,7 @@ func TestController(t *testing.T) { coretesting.NewWatchAction(webhookAuthenticatorGVR, "", metav1.ListOptions{}), } }, - wantCacheEntries: 1, + wantNamesOfWebhookAuthenticatorsInCache: []string{"test-name"}, }, { name: "Sync: authenticator update when cached authenticator is the wrong data type, which should never really happen: loop will complete successfully and update status conditions", @@ -734,8 +845,7 @@ func TestController(t *testing.T) { struct{ authenticator.Token }{}, ) }, - syncKey: controllerlib.Key{Name: "test-name"}, - webhooks: []runtime.Object{ + webhookAuthenticators: []runtime.Object{ &authenticationv1alpha1.WebhookAuthenticator{ ObjectMeta: metav1.ObjectMeta{ Name: "test-name", @@ -780,7 +890,7 @@ func TestController(t *testing.T) { updateStatusAction, } }, - wantCacheEntries: 1, + wantNamesOfWebhookAuthenticatorsInCache: []string{"test-name"}, }, { name: "Sync: changed WebhookAuthenticator: loop will update timestamps only on relevant statuses", @@ -796,8 +906,7 @@ func TestController(t *testing.T) { newCacheValue(t, goodWebhookAuthenticatorSpecWith404Endpoint, string(oldCA)), ) }, - syncKey: controllerlib.Key{Name: "test-name"}, - webhooks: []runtime.Object{ + webhookAuthenticators: []runtime.Object{ &authenticationv1alpha1.WebhookAuthenticator{ ObjectMeta: metav1.ObjectMeta{ Name: "test-name", @@ -852,12 +961,11 @@ func TestController(t *testing.T) { updateStatusAction, } }, - wantCacheEntries: 1, + wantNamesOfWebhookAuthenticatorsInCache: []string{"test-name"}, }, { - name: "Sync: valid WebhookAuthenticator with CA: will complete sync loop successfully with success conditions and ready phase", - syncKey: controllerlib.Key{Name: "test-name"}, - webhooks: []runtime.Object{ + name: "Sync: valid WebhookAuthenticator with CA: will complete sync loop successfully with success conditions and ready phase", + webhookAuthenticators: []runtime.Object{ &authenticationv1alpha1.WebhookAuthenticator{ ObjectMeta: metav1.ObjectMeta{ Name: "test-name", @@ -895,12 +1003,11 @@ func TestController(t *testing.T) { updateStatusAction, } }, - wantCacheEntries: 1, + wantNamesOfWebhookAuthenticatorsInCache: []string{"test-name"}, }, { - name: "Sync: valid WebhookAuthenticator with IPV6 and CA: will complete sync loop successfully with success conditions and ready phase", - syncKey: controllerlib.Key{Name: "test-name"}, - webhooks: []runtime.Object{ + name: "Sync: valid WebhookAuthenticator with IPV6 and CA: will complete sync loop successfully with success conditions and ready phase", + webhookAuthenticators: []runtime.Object{ &authenticationv1alpha1.WebhookAuthenticator{ ObjectMeta: metav1.ObjectMeta{ Name: "test-name", @@ -952,12 +1059,11 @@ func TestController(t *testing.T) { updateStatusAction, } }, - wantCacheEntries: 1, + wantNamesOfWebhookAuthenticatorsInCache: []string{"test-name"}, }, { - name: "Sync: valid WebhookAuthenticator without CA: loop will fail to cache the authenticator, will write failed and unknown status conditions, and will enqueue resync", - syncKey: controllerlib.Key{Name: "test-name"}, - webhooks: []runtime.Object{ + name: "Sync: valid WebhookAuthenticator without CA: loop will fail to cache the authenticator, will write failed and unknown status conditions, and will enqueue resync", + webhookAuthenticators: []runtime.Object{ &authenticationv1alpha1.WebhookAuthenticator{ ObjectMeta: metav1.ObjectMeta{ Name: "test-name", @@ -991,13 +1097,12 @@ func TestController(t *testing.T) { updateStatusAction, } }, - wantSyncLoopErr: testutil.WantExactErrorString(`cannot dial server: tls: failed to verify certificate: x509: certificate signed by unknown authority`), - wantCacheEntries: 0, + wantSyncLoopErr: testutil.WantExactErrorString(`error for WebhookAuthenticator test-name: cannot dial server: tls: failed to verify certificate: x509: certificate signed by unknown authority`), + wantNamesOfWebhookAuthenticatorsInCache: []string{}, }, { - name: "validateTLS: WebhookAuthenticator with invalid CA will fail sync loop and will report failed and unknown conditions and Error phase, but will not enqueue a resync due to user config error", - syncKey: controllerlib.Key{Name: "test-name"}, - webhooks: []runtime.Object{ + name: "validateTLS: WebhookAuthenticator with invalid CA will fail sync loop and will report failed and unknown conditions and Error phase, but will not enqueue a resync due to user config error", + webhookAuthenticators: []runtime.Object{ &authenticationv1alpha1.WebhookAuthenticator{ ObjectMeta: metav1.ObjectMeta{ Name: "test-name", @@ -1031,7 +1136,7 @@ func TestController(t *testing.T) { updateStatusAction, } }, - wantCacheEntries: 0, + wantNamesOfWebhookAuthenticatorsInCache: []string{}, }, { name: "previously valid cached authenticator's spec changes and becomes invalid (e.g. spec.issuer URL is invalid): loop will fail sync, will write failed and unknown status conditions, and will remove authenticator from cache", @@ -1047,8 +1152,7 @@ func TestController(t *testing.T) { newCacheValue(t, goodWebhookAuthenticatorSpecWithCA, string(oldCA)), ) }, - syncKey: controllerlib.Key{Name: "test-name"}, - webhooks: []runtime.Object{ + webhookAuthenticators: []runtime.Object{ &authenticationv1alpha1.WebhookAuthenticator{ ObjectMeta: metav1.ObjectMeta{ Name: "test-name", @@ -1087,12 +1191,11 @@ func TestController(t *testing.T) { updateStatusAction, } }, - wantCacheEntries: 0, // removed from cache + wantNamesOfWebhookAuthenticatorsInCache: []string{}, // removed from cache }, { - name: "validateEndpoint: parsing error (spec.endpoint URL is invalid) will fail sync loop and will report failed and unknown conditions and Error phase, but will not enqueue a resync due to user config error", - syncKey: controllerlib.Key{Name: "test-name"}, - webhooks: []runtime.Object{ + name: "validateEndpoint: parsing error (spec.endpoint URL is invalid) will fail sync loop and will report failed and unknown conditions and Error phase, but will not enqueue a resync due to user config error", + webhookAuthenticators: []runtime.Object{ &authenticationv1alpha1.WebhookAuthenticator{ ObjectMeta: metav1.ObjectMeta{ Name: "test-name", @@ -1131,12 +1234,11 @@ func TestController(t *testing.T) { updateStatusAction, } }, - wantCacheEntries: 0, + wantNamesOfWebhookAuthenticatorsInCache: []string{}, }, { - name: "validateEndpoint: parsing error (spec.endpoint URL has invalid scheme, requires https) will fail sync loop, will write failed and unknown status conditions, but will not enqueue a resync due to user config error", - syncKey: controllerlib.Key{Name: "test-name"}, - webhooks: []runtime.Object{ + name: "validateEndpoint: parsing error (spec.endpoint URL has invalid scheme, requires https) will fail sync loop, will write failed and unknown status conditions, but will not enqueue a resync due to user config error", + webhookAuthenticators: []runtime.Object{ &authenticationv1alpha1.WebhookAuthenticator{ ObjectMeta: metav1.ObjectMeta{ Name: "test-name", @@ -1175,12 +1277,11 @@ func TestController(t *testing.T) { updateStatusAction, } }, - wantCacheEntries: 0, + wantNamesOfWebhookAuthenticatorsInCache: []string{}, }, { - name: "validateEndpoint: should error if endpoint cannot be parsed", - syncKey: controllerlib.Key{Name: "test-name"}, - webhooks: []runtime.Object{ + name: "validateEndpoint: should error if endpoint cannot be parsed", + webhookAuthenticators: []runtime.Object{ &authenticationv1alpha1.WebhookAuthenticator{ ObjectMeta: metav1.ObjectMeta{ Name: "test-name", @@ -1224,12 +1325,11 @@ func TestController(t *testing.T) { updateStatusAction, } }, - wantCacheEntries: 0, + wantNamesOfWebhookAuthenticatorsInCache: []string{}, }, { - name: "validateConnection: CA does not validate serving certificate for host, the dialer will error, will fail sync loop, will write failed and unknown status conditions, but will not enqueue a resync due to user config error", - syncKey: controllerlib.Key{Name: "test-name"}, - webhooks: []runtime.Object{ + name: "validateConnection: CA does not validate serving certificate for host, the dialer will error, will fail sync loop, will write failed and unknown status conditions, but will not enqueue a resync due to user config error", + webhookAuthenticators: []runtime.Object{ &authenticationv1alpha1.WebhookAuthenticator{ ObjectMeta: metav1.ObjectMeta{ Name: "test-name", @@ -1237,7 +1337,7 @@ func TestController(t *testing.T) { Spec: badWebhookAuthenticatorSpecGoodEndpointButUnknownCA, }, }, - wantSyncLoopErr: testutil.WantExactErrorString("cannot dial server: tls: failed to verify certificate: x509: certificate signed by unknown authority"), + wantSyncLoopErr: testutil.WantExactErrorString("error for WebhookAuthenticator test-name: cannot dial server: tls: failed to verify certificate: x509: certificate signed by unknown authority"), wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -1263,15 +1363,14 @@ func TestController(t *testing.T) { updateStatusAction, } }, - wantCacheEntries: 0, + wantNamesOfWebhookAuthenticatorsInCache: []string{}, }, - // No unit test for system roots. We don't test the JWTAuthenticator's use of system roots either. + // No unit test for system roots. We don't test the WebhookAuthenticator's use of system roots either. // We would have to find a way to mock out roots by adding a dummy cert in order to test this // { name: "validateConnection: TLS bundle not provided should use system roots to validate server cert signed by a well-known CA",}, { - name: "validateConnection: 404 endpoint on a valid server will still validate server certificate, will complete sync loop successfully with success conditions and ready phase", - syncKey: controllerlib.Key{Name: "test-name"}, - webhooks: []runtime.Object{ + name: "validateConnection: 404 endpoint on a valid server will still validate server certificate, will complete sync loop successfully with success conditions and ready phase", + webhookAuthenticators: []runtime.Object{ &authenticationv1alpha1.WebhookAuthenticator{ ObjectMeta: metav1.ObjectMeta{ Name: "test-name", @@ -1309,12 +1408,11 @@ func TestController(t *testing.T) { updateStatusAction, } }, - wantCacheEntries: 1, + wantNamesOfWebhookAuthenticatorsInCache: []string{"test-name"}, }, { - name: "validateConnection: localhost hostname instead of 127.0.0.1 should still dial correctly as dialer should handle hostnames as well as IPv4", - syncKey: controllerlib.Key{Name: "test-name"}, - webhooks: []runtime.Object{ + name: "validateConnection: localhost hostname instead of 127.0.0.1 should still dial correctly as dialer should handle hostnames as well as IPv4", + webhookAuthenticators: []runtime.Object{ &authenticationv1alpha1.WebhookAuthenticator{ ObjectMeta: metav1.ObjectMeta{ Name: "test-name", @@ -1350,12 +1448,11 @@ func TestController(t *testing.T) { coretesting.NewWatchAction(webhookAuthenticatorGVR, "", metav1.ListOptions{}), } }, - wantCacheEntries: 1, + wantNamesOfWebhookAuthenticatorsInCache: []string{"test-name"}, }, { - name: "validateConnection: IPv6 address with port: should call dialer func with correct arguments", - syncKey: controllerlib.Key{Name: "test-name"}, - webhooks: []runtime.Object{ + name: "validateConnection: IPv6 address with port: should call dialer func with correct arguments", + webhookAuthenticators: []runtime.Object{ &authenticationv1alpha1.WebhookAuthenticator{ ObjectMeta: metav1.ObjectMeta{ Name: "test-name", @@ -1398,13 +1495,12 @@ func TestController(t *testing.T) { updateStatusAction, } }, - wantSyncLoopErr: testutil.WantExactErrorString(`cannot dial server: dial tcp [::1]:4242: connect: connection refused`), - wantCacheEntries: 0, + wantSyncLoopErr: testutil.WantExactErrorString(`error for WebhookAuthenticator test-name: cannot dial server: dial tcp [::1]:4242: connect: connection refused`), + wantNamesOfWebhookAuthenticatorsInCache: []string{}, }, { - name: "validateConnection: IPv6 address without port: should call dialer func with correct arguments", - syncKey: controllerlib.Key{Name: "test-name"}, - webhooks: []runtime.Object{ + name: "validateConnection: IPv6 address without port: should call dialer func with correct arguments", + webhookAuthenticators: []runtime.Object{ &authenticationv1alpha1.WebhookAuthenticator{ ObjectMeta: metav1.ObjectMeta{ Name: "test-name", @@ -1447,13 +1543,12 @@ func TestController(t *testing.T) { updateStatusAction, } }, - wantSyncLoopErr: testutil.WantExactErrorString(`cannot dial server: dial tcp [::1]:443: connect: connection refused`), - wantCacheEntries: 0, + wantSyncLoopErr: testutil.WantExactErrorString(`error for WebhookAuthenticator test-name: cannot dial server: dial tcp [::1]:443: connect: connection refused`), + wantNamesOfWebhookAuthenticatorsInCache: []string{}, }, { - name: "validateConnection: localhost as IP address 127.0.0.1 should still dial correctly as dialer should handle hostnames as well as IPv4 and IPv6 addresses", - syncKey: controllerlib.Key{Name: "test-name"}, - webhooks: []runtime.Object{ + name: "validateConnection: localhost as IP address 127.0.0.1 should still dial correctly as dialer should handle hostnames as well as IPv4 and IPv6 addresses", + webhookAuthenticators: []runtime.Object{ &authenticationv1alpha1.WebhookAuthenticator{ ObjectMeta: metav1.ObjectMeta{ Name: "test-name", @@ -1488,12 +1583,11 @@ func TestController(t *testing.T) { coretesting.NewWatchAction(webhookAuthenticatorGVR, "", metav1.ListOptions{}), } }, - wantCacheEntries: 1, + wantNamesOfWebhookAuthenticatorsInCache: []string{"test-name"}, }, { - name: "validateConnection: CA for example.com, serving cert for example.com, but endpoint 127.0.0.1 will fail to validate certificate and will fail sync loop and will report failed and unknown conditions and Error phase, but will not enqueue a resync due to user config error", - syncKey: controllerlib.Key{Name: "test-name"}, - webhooks: []runtime.Object{ + name: "validateConnection: CA for example.com, serving cert for example.com, but endpoint 127.0.0.1 will fail to validate certificate and will fail sync loop and will report failed and unknown conditions and Error phase, but will not enqueue a resync due to user config error", + webhookAuthenticators: []runtime.Object{ &authenticationv1alpha1.WebhookAuthenticator{ ObjectMeta: metav1.ObjectMeta{ Name: "test-name", @@ -1530,13 +1624,12 @@ func TestController(t *testing.T) { updateStatusAction, } }, - wantCacheEntries: 0, - wantSyncLoopErr: testutil.WantExactErrorString(`cannot dial server: tls: failed to verify certificate: x509: cannot validate certificate for 127.0.0.1 because it doesn't contain any IP SANs`), + wantNamesOfWebhookAuthenticatorsInCache: []string{}, + wantSyncLoopErr: testutil.WantExactErrorString(`error for WebhookAuthenticator test-name: cannot dial server: tls: failed to verify certificate: x509: cannot validate certificate for 127.0.0.1 because it doesn't contain any IP SANs`), }, { - name: "validateConnection: IPv6 address without port or brackets: should succeed since IPv6 brackets are optional without port", - syncKey: controllerlib.Key{Name: "test-name"}, - webhooks: []runtime.Object{ + name: "validateConnection: IPv6 address without port or brackets: should succeed since IPv6 brackets are optional without port", + webhookAuthenticators: []runtime.Object{ &authenticationv1alpha1.WebhookAuthenticator{ ObjectMeta: metav1.ObjectMeta{ Name: "test-name", @@ -1579,13 +1672,12 @@ func TestController(t *testing.T) { updateStatusAction, } }, - wantSyncLoopErr: testutil.WantExactErrorString(`cannot dial server: dial tcp [::1]:443: connect: connection refused`), - wantCacheEntries: 0, + wantSyncLoopErr: testutil.WantExactErrorString(`error for WebhookAuthenticator test-name: cannot dial server: dial tcp [::1]:443: connect: connection refused`), + wantNamesOfWebhookAuthenticatorsInCache: []string{}, }, { - name: "updateStatus: called with matching original and updated conditions: will not make request to update conditions", - syncKey: controllerlib.Key{Name: "test-name"}, - webhooks: []runtime.Object{ + name: "updateStatus: called with matching original and updated conditions: will not make request to update conditions", + webhookAuthenticators: []runtime.Object{ &authenticationv1alpha1.WebhookAuthenticator{ ObjectMeta: metav1.ObjectMeta{ Name: "test-name", @@ -1615,12 +1707,11 @@ func TestController(t *testing.T) { coretesting.NewWatchAction(webhookAuthenticatorGVR, "", metav1.ListOptions{}), } }, - wantCacheEntries: 1, + wantNamesOfWebhookAuthenticatorsInCache: []string{"test-name"}, }, { - name: "updateStatus: called with different original and updated conditions: will make request to update conditions", - syncKey: controllerlib.Key{Name: "test-name"}, - webhooks: []runtime.Object{ + name: "updateStatus: called with different original and updated conditions: will make request to update conditions", + webhookAuthenticators: []runtime.Object{ &authenticationv1alpha1.WebhookAuthenticator{ ObjectMeta: metav1.ObjectMeta{ Name: "test-name", @@ -1667,11 +1758,10 @@ func TestController(t *testing.T) { updateStatusAction, } }, - wantCacheEntries: 1, + wantNamesOfWebhookAuthenticatorsInCache: []string{"test-name"}, }, { - name: "updateStatus: when update request fails: error will enqueue a resync", - syncKey: controllerlib.Key{Name: "test-name"}, + name: "updateStatus: when update request fails: error will enqueue a resync", configClient: func(client *conciergefake.Clientset) { client.PrependReactor( "update", @@ -1681,7 +1771,7 @@ func TestController(t *testing.T) { }, ) }, - webhooks: []runtime.Object{ + webhookAuthenticators: []runtime.Object{ &authenticationv1alpha1.WebhookAuthenticator{ ObjectMeta: metav1.ObjectMeta{ Name: "test-name", @@ -1728,15 +1818,15 @@ func TestController(t *testing.T) { updateStatusAction, } }, - wantSyncLoopErr: testutil.WantExactErrorString("some update error"), - wantCacheEntries: 1, + wantSyncLoopErr: testutil.WantExactErrorString("error for WebhookAuthenticator test-name: some update error"), + wantNamesOfWebhookAuthenticatorsInCache: []string{"test-name"}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { t.Parallel() - pinnipedAPIClient := conciergefake.NewSimpleClientset(tt.webhooks...) + pinnipedAPIClient := conciergefake.NewSimpleClientset(tt.webhookAuthenticators...) if tt.configClient != nil { tt.configClient(pinnipedAPIClient) } @@ -1769,7 +1859,7 @@ func TestController(t *testing.T) { kubeInformers.Start(ctx.Done()) controllerlib.TestRunSynchronously(t, controller) - syncCtx := controllerlib.Context{Context: ctx, Key: tt.syncKey} + syncCtx := controllerlib.Context{Context: ctx} if err := controllerlib.TestSync(t, controller, syncCtx); tt.wantSyncLoopErr != nil { testutil.RequireErrorStringFromErr(t, err, tt.wantSyncLoopErr) @@ -1779,7 +1869,7 @@ func TestController(t *testing.T) { require.NotEmpty(t, tt.wantActions, "wantActions is required for test %s", tt.name) require.Equal(t, tt.wantActions(), pinnipedAPIClient.Actions()) - require.Equal(t, tt.wantCacheEntries, len(cache.Keys()), fmt.Sprintf("expected cache entries is incorrect. wanted:%d, got: %d, keys: %v", tt.wantCacheEntries, len(cache.Keys()), cache.Keys())) + require.Equal(t, len(tt.wantNamesOfWebhookAuthenticatorsInCache), len(cache.Keys()), fmt.Sprintf("expected cache entries is incorrect. wanted:%d, got: %d, keys: %v", len(tt.wantNamesOfWebhookAuthenticatorsInCache), len(cache.Keys()), cache.Keys())) actualLogLines := testutil.SplitByNewline(log.String()) require.Equal(t, len(tt.wantLogs), len(actualLogLines), "log line count should be correct") diff --git a/test/integration/concierge_webhookauthenticator_status_test.go b/test/integration/concierge_webhookauthenticator_status_test.go index 2841f93b8..6cb6475b1 100644 --- a/test/integration/concierge_webhookauthenticator_status_test.go +++ b/test/integration/concierge_webhookauthenticator_status_test.go @@ -5,10 +5,13 @@ package integration import ( "context" + "encoding/base64" + "fmt" "testing" "time" "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -16,6 +19,96 @@ import ( "go.pinniped.dev/test/testlib" ) +func TestConciergeWebhookAuthenticatorWithExternalCABundleStatusIsUpdatedWhenExternalBundleIsUpdated_Parallel(t *testing.T) { + env := testlib.IntegrationEnv(t) + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute) + t.Cleanup(cancel) + + client := testlib.NewKubernetesClientset(t) + + tests := []struct { + name string + caBundleSourceSpecKind string + createResourceForCABundle func(t *testing.T, caBundle string) string + updateCABundle func(t *testing.T, resourceName, caBundle string) + }{ + { + name: "for a CA bundle from a ConfigMap", + caBundleSourceSpecKind: "ConfigMap", + createResourceForCABundle: func(t *testing.T, caBundle string) string { + createdResource := testlib.CreateTestConfigMap(t, env.ConciergeNamespace, "ca-bundle", map[string]string{ + "ca.crt": caBundle, + }) + return createdResource.Name + }, + updateCABundle: func(t *testing.T, resourceName, caBundle string) { + configMap, err := client.CoreV1().ConfigMaps(env.ConciergeNamespace).Get(ctx, resourceName, metav1.GetOptions{}) + require.NoError(t, err) + + configMap.Data["ca.crt"] = caBundle + + _, err = client.CoreV1().ConfigMaps(env.ConciergeNamespace).Update(ctx, configMap, metav1.UpdateOptions{}) + require.NoError(t, err) + }, + }, + { + name: "for a CA bundle from a Secret", + caBundleSourceSpecKind: "Secret", + createResourceForCABundle: func(t *testing.T, caBundle string) string { + createdResource := testlib.CreateTestSecret(t, env.ConciergeNamespace, "ca-bundle", corev1.SecretTypeOpaque, map[string]string{ + "ca.crt": caBundle, + }) + return createdResource.Name + }, + updateCABundle: func(t *testing.T, resourceName, caBundle string) { + secret, err := client.CoreV1().Secrets(env.ConciergeNamespace).Get(ctx, resourceName, metav1.GetOptions{}) + require.NoError(t, err) + + secret.Data["ca.crt"] = []byte(caBundle) + + _, err = client.CoreV1().Secrets(env.ConciergeNamespace).Update(ctx, secret, metav1.UpdateOptions{}) + require.NoError(t, err) + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + t.Parallel() + + // Run several times because there is always a chance that the test could pass because the controller + // will resync every 3 minutes even if it does not pay attention to changes in ConfigMaps and Secrets. + for i := range 3 { + t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { + t.Parallel() + + caBundlePEM, err := base64.StdEncoding.DecodeString(testlib.IntegrationEnv(t).TestWebhook.TLS.CertificateAuthorityData) + require.NoError(t, err) + + caBundleResourceName := test.createResourceForCABundle(t, string(caBundlePEM)) + + authenticator := testlib.CreateTestWebhookAuthenticator(ctx, t, &authenticationv1alpha1.WebhookAuthenticatorSpec{ + Endpoint: testlib.IntegrationEnv(t).TestWebhook.Endpoint, + TLS: &authenticationv1alpha1.TLSSpec{ + CertificateAuthorityDataSource: &authenticationv1alpha1.CABundleSource{ + Kind: test.caBundleSourceSpecKind, + Name: caBundleResourceName, + Key: "ca.crt", + }, + }, + }, authenticationv1alpha1.WebhookAuthenticatorPhaseReady) + + test.updateCABundle(t, caBundleResourceName, "this is not a valid CA bundle value") + testlib.WaitForWebhookAuthenticatorStatusPhase(ctx, t, authenticator.Name, authenticationv1alpha1.WebhookAuthenticatorPhaseError) + + test.updateCABundle(t, caBundleResourceName, string(caBundlePEM)) + testlib.WaitForWebhookAuthenticatorStatusPhase(ctx, t, authenticator.Name, authenticationv1alpha1.WebhookAuthenticatorPhaseReady) + }) + } + }) + } +} + func TestConciergeWebhookAuthenticatorStatus_Parallel(t *testing.T) { testEnv := testlib.IntegrationEnv(t) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute) @@ -218,7 +311,7 @@ func TestConciergeWebhookAuthenticatorCRDValidations_Parallel(t *testing.T) { { name: "valid authenticator can have empty TLS CertificateAuthorityData", webhookAuthenticator: &authenticationv1alpha1.WebhookAuthenticator{ - ObjectMeta: testlib.ObjectMetaWithRandomName(t, "jwtauthenticator"), + ObjectMeta: testlib.ObjectMetaWithRandomName(t, "webhookauthenticator"), Spec: authenticationv1alpha1.WebhookAuthenticatorSpec{ Endpoint: "https://localhost/webhook-isnt-actually-here", TLS: &authenticationv1alpha1.TLSSpec{ @@ -231,7 +324,7 @@ func TestConciergeWebhookAuthenticatorCRDValidations_Parallel(t *testing.T) { // since the CRD validations do not assess fitness of the value provided name: "valid authenticator can have TLS CertificateAuthorityData string that is an invalid certificate", webhookAuthenticator: &authenticationv1alpha1.WebhookAuthenticator{ - ObjectMeta: testlib.ObjectMetaWithRandomName(t, "jwtauthenticator"), + ObjectMeta: testlib.ObjectMetaWithRandomName(t, "webhookauthenticator"), Spec: authenticationv1alpha1.WebhookAuthenticatorSpec{ Endpoint: "https://localhost/webhook-isnt-actually-here", TLS: &authenticationv1alpha1.TLSSpec{ From de86809b69d5c8a8613cc82e76c15b8707450815 Mon Sep 17 00:00:00 2001 From: Joshua Casey Date: Thu, 25 Jul 2024 10:05:59 -0500 Subject: [PATCH 48/99] Fix some integration tests --- test/integration/concierge_tls_spec_test.go | 4 ++-- test/integration/e2e_test.go | 3 +-- test/integration/supervisor_tls_spec_test.go | 6 +++--- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/test/integration/concierge_tls_spec_test.go b/test/integration/concierge_tls_spec_test.go index 669a3357c..783a17b29 100644 --- a/test/integration/concierge_tls_spec_test.go +++ b/test/integration/concierge_tls_spec_test.go @@ -170,7 +170,7 @@ func TestTLSSpecKubeBuilderValidationConcierge_Parallel(t *testing.T) { performKubectlApply( t, webhookYamlBytes, - `webhookauthenticator.authentication.concierge.pinniped.dev`, + fmt.Sprintf(`webhookauthenticator.authentication.concierge.%s`, env.APIGroupSuffix), tc.expectedErrorSnippets, "WebhookAuthenticator", webhookResourceName, @@ -187,7 +187,7 @@ func TestTLSSpecKubeBuilderValidationConcierge_Parallel(t *testing.T) { performKubectlApply( t, jwtAuthenticatorYamlBytes, - `jwtauthenticator.authentication.concierge.pinniped.dev`, + fmt.Sprintf(`jwtauthenticator.authentication.concierge.%s`, env.APIGroupSuffix), tc.expectedErrorSnippets, "JWTAuthenticator", jwtAuthenticatorResourceName, diff --git a/test/integration/e2e_test.go b/test/integration/e2e_test.go index bf4b14c4d..bd6e7bb08 100644 --- a/test/integration/e2e_test.go +++ b/test/integration/e2e_test.go @@ -1696,11 +1696,10 @@ func TestE2EFullIntegration_Browser(t *testing.T) { expectedDownstreamLDAPGroups := env.SupervisorUpstreamLDAP.TestUserDirectGroupsDNs expectedDownstreamOIDCGroups := env.SupervisorUpstreamOIDC.ExpectedGroups - authenticator := testlib.CreateTestJWTAuthenticator(testCtx, t, defaultJWTAuthenticatorSpec, authenticationv1alpha1.JWTAuthenticatorPhaseError) createdLDAPProvider := setupClusterForEndToEndLDAPTest(t, expectedDownstreamLDAPUsername, env) // Having one IDP should put the FederationDomain into a ready state. testlib.WaitForFederationDomainStatusPhase(testCtx, t, federationDomain.Name, supervisorconfigv1alpha1.FederationDomainPhaseReady) - testlib.WaitForJWTAuthenticatorStatusPhase(testCtx, t, authenticator.Name, authenticationv1alpha1.JWTAuthenticatorPhaseReady) + authenticator := testlib.CreateTestJWTAuthenticator(testCtx, t, defaultJWTAuthenticatorSpec, authenticationv1alpha1.JWTAuthenticatorPhaseReady) // Create a ClusterRoleBinding to give our test user from the upstream read-only access to the cluster. testlib.CreateTestClusterRoleBinding(t, diff --git a/test/integration/supervisor_tls_spec_test.go b/test/integration/supervisor_tls_spec_test.go index 57543638d..af9e441ec 100644 --- a/test/integration/supervisor_tls_spec_test.go +++ b/test/integration/supervisor_tls_spec_test.go @@ -226,7 +226,7 @@ func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { performKubectlApply( t, yamlBytes, - `oidcidentityprovider.idp.supervisor.pinniped.dev`, + fmt.Sprintf(`oidcidentityprovider.idp.supervisor.%s`, env.APIGroupSuffix), tc.expectedErrorSnippets, "OIDCIdentityProvider", resourceName, @@ -241,7 +241,7 @@ func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { performKubectlApply( t, yamlBytes, - `ldapidentityprovider.idp.supervisor.pinniped.dev`, + fmt.Sprintf(`ldapidentityprovider.idp.supervisor.%s`, env.APIGroupSuffix), tc.expectedErrorSnippets, "LDAPIdentityProvider", resourceName, @@ -256,7 +256,7 @@ func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { performKubectlApply( t, yamlBytes, - `activedirectoryidentityprovider.idp.supervisor.pinniped.dev`, + fmt.Sprintf(`activedirectoryidentityprovider.idp.supervisor.%s`, env.APIGroupSuffix), tc.expectedErrorSnippets, "ActiveDirectoryIdentityProvider", resourceName, From 9a16dc28b7664284520bff28f0deb7a18181680f Mon Sep 17 00:00:00 2001 From: Joshua Casey Date: Thu, 25 Jul 2024 12:45:52 -0500 Subject: [PATCH 49/99] Fix another integration test --- test/integration/supervisor_tls_spec_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/supervisor_tls_spec_test.go b/test/integration/supervisor_tls_spec_test.go index af9e441ec..12990eedc 100644 --- a/test/integration/supervisor_tls_spec_test.go +++ b/test/integration/supervisor_tls_spec_test.go @@ -274,7 +274,7 @@ func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { performKubectlApply( t, yamlBytes, - `githubidentityprovider.idp.supervisor.pinniped.dev`, + fmt.Sprintf(`githubidentityprovider.idp.supervisor.%s`, env.APIGroupSuffix), tc.expectedGitHubErrorSnippets, "GitHubIdentityProvider", resourceName, From e3ed7222526fa200232c701f06fd67814e1475b2 Mon Sep 17 00:00:00 2001 From: Joshua Casey Date: Thu, 25 Jul 2024 13:40:21 -0500 Subject: [PATCH 50/99] Minor refactor Co-authored-by: Ryan Richard --- .../tlsconfigutil/tls_config_util.go | 12 ++++--- .../tlsconfigutil/tls_config_util_test.go | 36 +++++++++---------- 2 files changed, 25 insertions(+), 23 deletions(-) diff --git a/internal/controller/tlsconfigutil/tls_config_util.go b/internal/controller/tlsconfigutil/tls_config_util.go index 121609517..14d1e26dd 100644 --- a/internal/controller/tlsconfigutil/tls_config_util.go +++ b/internal/controller/tlsconfigutil/tls_config_util.go @@ -15,7 +15,6 @@ import ( authenticationv1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/authentication/v1alpha1" idpv1alpha1 "go.pinniped.dev/generated/latest/apis/supervisor/idp/v1alpha1" - "go.pinniped.dev/internal/constable" "go.pinniped.dev/internal/controller/conditionsutil" ) @@ -25,8 +24,6 @@ const ( noTLSConfigurationMessage = "no TLS configuration provided" loadedTLSConfigurationMessage = "loaded TLS configuration" typeTLSConfigurationValid = "TLSConfigurationValid" - - ErrNoCertificates = constable.Error("no certificates found") ) type caBundleSource struct { @@ -87,7 +84,6 @@ func TLSSpecForConcierge(source *authenticationv1alpha1.TLSSpec) *TLSSpec { // - a condition of type TLSConfigurationValid based on the validity of the ca bundle, // - a pem encoded ca bundle // - a X509 cert pool with the ca bundle -// TODO: should this show the resource version of the Secret/ConfigMap to the user on all conditions? func ValidateTLSConfig( tlsSpec *TLSSpec, conditionPrefix string, @@ -95,6 +91,12 @@ func ValidateTLSConfig( secretInformer corev1informers.SecretInformer, configMapInformer corev1informers.ConfigMapInformer, ) (*metav1.Condition, []byte, *x509.CertPool) { + // TODO: This func should return a struct that abstracts away the internals of how a CA bundle is held in memory + // and can return the CA bundle as string PEM, []byte base64-encoded, CertPool, hash, etc, as well as compare itself + // to either a different struct instance or a hash. + // + // TODO: There could easily be a hash type struct alias for the specific hash value (e.g. "[32]byte") with an Equality function. + certPool, bundle, err := getCertPool(tlsSpec, conditionPrefix, namespace, secretInformer, configMapInformer) if err != nil { return invalidTLSCondition(err.Error()), nil, nil @@ -165,7 +167,7 @@ func getCertPool( ca := x509.NewCertPool() ok := ca.AppendCertsFromPEM(bundleBytes) if !ok { - return nil, nil, fmt.Errorf("%s is invalid: %s", field, ErrNoCertificates) + return nil, nil, fmt.Errorf("%s is invalid: no certificates found", field) } return ca, bundleBytes, nil diff --git a/internal/controller/tlsconfigutil/tls_config_util_test.go b/internal/controller/tlsconfigutil/tls_config_util_test.go index 25b11f1e8..cff871cb2 100644 --- a/internal/controller/tlsconfigutil/tls_config_util_test.go +++ b/internal/controller/tlsconfigutil/tls_config_util_test.go @@ -48,7 +48,7 @@ func TestValidateTLSConfig(t *testing.T) { Type: typeTLSConfigurationValid, Status: metav1.ConditionTrue, Reason: conditionsutil.ReasonSuccess, - Message: "tls is valid: " + noTLSConfigurationMessage, + Message: "spec.foo.tls is valid: " + noTLSConfigurationMessage, }, }, { @@ -58,7 +58,7 @@ func TestValidateTLSConfig(t *testing.T) { Type: typeTLSConfigurationValid, Status: metav1.ConditionTrue, Reason: conditionsutil.ReasonSuccess, - Message: "tls is valid: " + noTLSConfigurationMessage, + Message: "spec.foo.tls is valid: " + noTLSConfigurationMessage, }, }, { @@ -72,7 +72,7 @@ func TestValidateTLSConfig(t *testing.T) { Type: typeTLSConfigurationValid, Status: metav1.ConditionTrue, Reason: conditionsutil.ReasonSuccess, - Message: "tls is valid: " + loadedTLSConfigurationMessage, + Message: "spec.foo.tls is valid: " + loadedTLSConfigurationMessage, }, }, { @@ -84,7 +84,7 @@ func TestValidateTLSConfig(t *testing.T) { Type: typeTLSConfigurationValid, Status: metav1.ConditionFalse, Reason: ReasonInvalidTLSConfig, - Message: "tls.certificateAuthorityData is invalid: " + ErrNoCertificates.Error(), + Message: "spec.foo.tls.certificateAuthorityData is invalid: no certificates found", }, }, { @@ -96,7 +96,7 @@ func TestValidateTLSConfig(t *testing.T) { Type: typeTLSConfigurationValid, Status: metav1.ConditionFalse, Reason: ReasonInvalidTLSConfig, - Message: "tls.certificateAuthorityData is invalid: illegal base64 data at input byte 3", + Message: "spec.foo.tls.certificateAuthorityData is invalid: illegal base64 data at input byte 3", }, }, { @@ -113,7 +113,7 @@ func TestValidateTLSConfig(t *testing.T) { Type: typeTLSConfigurationValid, Status: metav1.ConditionFalse, Reason: ReasonInvalidTLSConfig, - Message: "tls is invalid: both tls.certificateAuthorityDataSource and tls.certificateAuthorityData provided", + Message: "spec.foo.tls is invalid: both tls.certificateAuthorityDataSource and tls.certificateAuthorityData provided", }, }, { @@ -144,7 +144,7 @@ func TestValidateTLSConfig(t *testing.T) { Type: typeTLSConfigurationValid, Status: metav1.ConditionTrue, Reason: conditionsutil.ReasonSuccess, - Message: "tls is valid: loaded TLS configuration", + Message: "spec.foo.tls is valid: loaded TLS configuration", }, }, { @@ -175,7 +175,7 @@ func TestValidateTLSConfig(t *testing.T) { Type: typeTLSConfigurationValid, Status: metav1.ConditionTrue, Reason: conditionsutil.ReasonSuccess, - Message: "tls is valid: loaded TLS configuration", + Message: "spec.foo.tls is valid: loaded TLS configuration", }, }, { @@ -204,7 +204,7 @@ func TestValidateTLSConfig(t *testing.T) { Type: typeTLSConfigurationValid, Status: metav1.ConditionFalse, Reason: ReasonInvalidTLSConfig, - Message: `tls.certificateAuthorityDataSource is invalid: secret "awesome-namespace/awesome-secret-ba" of type "kubernetes.io/basic-auth" cannot be used as a certificate authority data source`, + Message: `spec.foo.tls.certificateAuthorityDataSource is invalid: secret "awesome-namespace/awesome-secret-ba" of type "kubernetes.io/basic-auth" cannot be used as a certificate authority data source`, }, }, { @@ -233,7 +233,7 @@ func TestValidateTLSConfig(t *testing.T) { Type: typeTLSConfigurationValid, Status: metav1.ConditionFalse, Reason: ReasonInvalidTLSConfig, - Message: `tls.certificateAuthorityDataSource is invalid: key "ca-bundle" not found in secret "awesome-namespace/awesome-secret"`, + Message: `spec.foo.tls.certificateAuthorityDataSource is invalid: key "ca-bundle" not found in secret "awesome-namespace/awesome-secret"`, }, }, { @@ -262,7 +262,7 @@ func TestValidateTLSConfig(t *testing.T) { Type: typeTLSConfigurationValid, Status: metav1.ConditionFalse, Reason: ReasonInvalidTLSConfig, - Message: `tls.certificateAuthorityDataSource is invalid: key "ca-bundle" has empty value in secret "awesome-namespace/awesome-secret"`, + Message: `spec.foo.tls.certificateAuthorityDataSource is invalid: key "ca-bundle" has empty value in secret "awesome-namespace/awesome-secret"`, }, }, { @@ -290,7 +290,7 @@ func TestValidateTLSConfig(t *testing.T) { Type: typeTLSConfigurationValid, Status: metav1.ConditionFalse, Reason: ReasonInvalidTLSConfig, - Message: `tls.certificateAuthorityDataSource is invalid: key "ca-bundle" not found in configmap "awesome-namespace/awesome-configmap"`, + Message: `spec.foo.tls.certificateAuthorityDataSource is invalid: key "ca-bundle" not found in configmap "awesome-namespace/awesome-configmap"`, }, }, { @@ -318,7 +318,7 @@ func TestValidateTLSConfig(t *testing.T) { Type: typeTLSConfigurationValid, Status: metav1.ConditionFalse, Reason: ReasonInvalidTLSConfig, - Message: `tls.certificateAuthorityDataSource is invalid: key "ca-bundle" has empty value in configmap "awesome-namespace/awesome-configmap"`, + Message: `spec.foo.tls.certificateAuthorityDataSource is invalid: key "ca-bundle" has empty value in configmap "awesome-namespace/awesome-configmap"`, }, }, { @@ -348,7 +348,7 @@ func TestValidateTLSConfig(t *testing.T) { Type: typeTLSConfigurationValid, Status: metav1.ConditionTrue, Reason: conditionsutil.ReasonSuccess, - Message: "tls is valid: loaded TLS configuration", + Message: "spec.foo.tls is valid: loaded TLS configuration", }, }, { @@ -366,7 +366,7 @@ func TestValidateTLSConfig(t *testing.T) { Type: typeTLSConfigurationValid, Status: metav1.ConditionFalse, Reason: ReasonInvalidTLSConfig, - Message: `tls.certificateAuthorityDataSource is invalid: failed to get secret "awesome-namespace/does-not-exist": secret "does-not-exist" not found`, + Message: `spec.foo.tls.certificateAuthorityDataSource is invalid: failed to get secret "awesome-namespace/does-not-exist": secret "does-not-exist" not found`, }, }, { @@ -384,7 +384,7 @@ func TestValidateTLSConfig(t *testing.T) { Type: typeTLSConfigurationValid, Status: metav1.ConditionFalse, Reason: ReasonInvalidTLSConfig, - Message: `tls.certificateAuthorityDataSource is invalid: failed to get configmap "awesome-namespace/does-not-exist": configmap "does-not-exist" not found`, + Message: `spec.foo.tls.certificateAuthorityDataSource is invalid: failed to get configmap "awesome-namespace/does-not-exist": configmap "does-not-exist" not found`, }, }, { @@ -412,7 +412,7 @@ func TestValidateTLSConfig(t *testing.T) { Type: typeTLSConfigurationValid, Status: metav1.ConditionFalse, Reason: ReasonInvalidTLSConfig, - Message: "tls.certificateAuthorityDataSource is invalid: unsupported CA bundle source kind: SomethingElse", + Message: "spec.foo.tls.certificateAuthorityDataSource is invalid: unsupported CA bundle source kind: SomethingElse", }, }, } @@ -442,7 +442,7 @@ func TestValidateTLSConfig(t *testing.T) { // which would do this same call for us. sharedInformers.WaitForCacheSync(ctx.Done()) - actualCondition, actualBundle, actualCertPool := ValidateTLSConfig(tt.tlsSpec, "tls", tt.namespace, secretsInformer, configMapInformer) + actualCondition, actualBundle, actualCertPool := ValidateTLSConfig(tt.tlsSpec, "spec.foo.tls", tt.namespace, secretsInformer, configMapInformer) require.Equal(t, tt.expectedCondition, actualCondition) require.Equal(t, tt.expectedBundle, actualBundle) From 242fa8afb2fc791f553cba564be3a473a8486aa9 Mon Sep 17 00:00:00 2001 From: Joshua Casey Date: Thu, 25 Jul 2024 14:19:17 -0500 Subject: [PATCH 51/99] When reading CA bundle from a secret/configmap, return more specific err When the bundle does not contain any certs, make the error more specific. Co-authored-by: Ryan Richard --- .../active_directory_upstream_watcher_test.go | 2 +- .../github_upstream_watcher_test.go | 4 +- .../ldap_upstream_watcher_test.go | 2 +- .../oidc_upstream_watcher_test.go | 12 ++-- .../tlsconfigutil/tls_config_util.go | 14 ++++- .../tlsconfigutil/tls_config_util_test.go | 59 ++++++++++++++++++- 6 files changed, 80 insertions(+), 13 deletions(-) diff --git a/internal/controller/supervisorconfig/activedirectoryupstreamwatcher/active_directory_upstream_watcher_test.go b/internal/controller/supervisorconfig/activedirectoryupstreamwatcher/active_directory_upstream_watcher_test.go index f94cc70f8..c956a9635 100644 --- a/internal/controller/supervisorconfig/activedirectoryupstreamwatcher/active_directory_upstream_watcher_test.go +++ b/internal/controller/supervisorconfig/activedirectoryupstreamwatcher/active_directory_upstream_watcher_test.go @@ -598,7 +598,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { Status: "False", LastTransitionTime: now, Reason: "InvalidTLSConfig", - Message: "spec.tls.certificateAuthorityData is invalid: no certificates found", + Message: `spec.tls.certificateAuthorityData is invalid: no base64-encoded PEM certificates found in 28 bytes of data (PEM certificates must begin with "-----BEGIN CERTIFICATE-----")`, ObservedGeneration: 1234, }, }, diff --git a/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher_test.go b/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher_test.go index 15ca58aaf..1486e4cfd 100644 --- a/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher_test.go +++ b/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher_test.go @@ -1394,7 +1394,7 @@ func TestController(t *testing.T) { buildGitHubConnectionValidUnknown(t), buildHostValidTrue(t, *validFilledOutIDP.Spec.GitHubAPI.Host), buildOrganizationsPolicyValidTrue(t, *validFilledOutIDP.Spec.AllowAuthentication.Organizations.Policy), - buildTLSConfigurationValidFalse(t, "spec.githubAPI.tls.certificateAuthorityData is invalid: no certificates found"), + buildTLSConfigurationValidFalse(t, `spec.githubAPI.tls.certificateAuthorityData is invalid: no base64-encoded PEM certificates found in 4 bytes of data (PEM certificates must begin with "-----BEGIN CERTIFICATE-----")`), }, }, }, @@ -1404,7 +1404,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidTrue("some-idp-name"), buildLogForUpdatingOrganizationPolicyValid("some-idp-name", "True", "Success", fmt.Sprintf(`spec.allowAuthentication.organizations.policy (\"%s\") is valid`, string(*validFilledOutIDP.Spec.AllowAuthentication.Organizations.Policy))), buildLogForUpdatingHostValid("some-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is valid`, *validFilledOutIDP.Spec.GitHubAPI.Host), - buildLogForUpdatingTLSConfigurationValid("some-idp-name", "False", "InvalidTLSConfig", "spec.githubAPI.tls.certificateAuthorityData is invalid: no certificates found"), + buildLogForUpdatingTLSConfigurationValid("some-idp-name", "False", "InvalidTLSConfig", `spec.githubAPI.tls.certificateAuthorityData is invalid: no base64-encoded PEM certificates found in 4 bytes of data (PEM certificates must begin with \"-----BEGIN CERTIFICATE-----\")`), buildLogForUpdatingGitHubConnectionValidUnknown("some-idp-name"), buildLogForUpdatingPhase("some-idp-name", "Error"), }, diff --git a/internal/controller/supervisorconfig/ldapupstreamwatcher/ldap_upstream_watcher_test.go b/internal/controller/supervisorconfig/ldapupstreamwatcher/ldap_upstream_watcher_test.go index 3dde9d235..77269be21 100644 --- a/internal/controller/supervisorconfig/ldapupstreamwatcher/ldap_upstream_watcher_test.go +++ b/internal/controller/supervisorconfig/ldapupstreamwatcher/ldap_upstream_watcher_test.go @@ -527,7 +527,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { Status: "False", LastTransitionTime: now, Reason: "InvalidTLSConfig", - Message: "spec.tls.certificateAuthorityData is invalid: no certificates found", + Message: `spec.tls.certificateAuthorityData is invalid: no base64-encoded PEM certificates found in 28 bytes of data (PEM certificates must begin with "-----BEGIN CERTIFICATE-----")`, ObservedGeneration: 1234, }, }, diff --git a/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher_test.go b/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher_test.go index ed5968185..f63d01e28 100644 --- a/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher_test.go +++ b/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher_test.go @@ -431,11 +431,11 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { wantErr: controllerlib.ErrSyntheticRequeue.Error(), wantLogs: []string{ `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"ClientCredentialsSecretValid","status":"True","reason":"Success","message":"loaded client credentials"}`, - `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","status":"False","reason":"InvalidTLSConfig","message":"spec.tls.certificateAuthorityData is invalid: no certificates found"}`, - `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"False","reason":"InvalidTLSConfig","message":"spec.tls.certificateAuthorityData is invalid: no certificates found"}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","status":"False","reason":"InvalidTLSConfig","message":"spec.tls.certificateAuthorityData is invalid: no base64-encoded PEM certificates found in 28 bytes of data (PEM certificates must begin with \"-----BEGIN CERTIFICATE-----\")"}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"False","reason":"InvalidTLSConfig","message":"spec.tls.certificateAuthorityData is invalid: no base64-encoded PEM certificates found in 28 bytes of data (PEM certificates must begin with \"-----BEGIN CERTIFICATE-----\")"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"AdditionalAuthorizeParametersValid","status":"True","reason":"Success","message":"additionalAuthorizeParameters parameter names are allowed"}`, - `{"level":"error","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"oidcupstreamwatcher/oidc_upstream_watcher.go:$oidcupstreamwatcher.(*oidcWatcherController).validateUpstream","message":"found failing condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","reason":"InvalidTLSConfig","message":"spec.tls.certificateAuthorityData is invalid: no certificates found","error":"OIDCIdentityProvider has a failing condition"}`, - `{"level":"error","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"oidcupstreamwatcher/oidc_upstream_watcher.go:$oidcupstreamwatcher.(*oidcWatcherController).validateUpstream","message":"found failing condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","reason":"InvalidTLSConfig","message":"spec.tls.certificateAuthorityData is invalid: no certificates found","error":"OIDCIdentityProvider has a failing condition"}`, + `{"level":"error","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"oidcupstreamwatcher/oidc_upstream_watcher.go:$oidcupstreamwatcher.(*oidcWatcherController).validateUpstream","message":"found failing condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","reason":"InvalidTLSConfig","message":"spec.tls.certificateAuthorityData is invalid: no base64-encoded PEM certificates found in 28 bytes of data (PEM certificates must begin with \"-----BEGIN CERTIFICATE-----\")","error":"OIDCIdentityProvider has a failing condition"}`, + `{"level":"error","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"oidcupstreamwatcher/oidc_upstream_watcher.go:$oidcupstreamwatcher.(*oidcWatcherController).validateUpstream","message":"found failing condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","reason":"InvalidTLSConfig","message":"spec.tls.certificateAuthorityData is invalid: no base64-encoded PEM certificates found in 28 bytes of data (PEM certificates must begin with \"-----BEGIN CERTIFICATE-----\")","error":"OIDCIdentityProvider has a failing condition"}`, }, wantResultingCache: []*oidctestutil.TestUpstreamOIDCIdentityProvider{}, wantResultingUpstreams: []idpv1alpha1.OIDCIdentityProvider{{ @@ -447,9 +447,9 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { {Type: "ClientCredentialsSecretValid", Status: "True", LastTransitionTime: now, Reason: "Success", Message: "loaded client credentials"}, {Type: "OIDCDiscoverySucceeded", Status: "False", LastTransitionTime: now, Reason: "InvalidTLSConfig", - Message: `spec.tls.certificateAuthorityData is invalid: no certificates found`}, + Message: `spec.tls.certificateAuthorityData is invalid: no base64-encoded PEM certificates found in 28 bytes of data (PEM certificates must begin with "-----BEGIN CERTIFICATE-----")`}, {Type: "TLSConfigurationValid", Status: "False", LastTransitionTime: now, Reason: "InvalidTLSConfig", - Message: "spec.tls.certificateAuthorityData is invalid: no certificates found"}, + Message: `spec.tls.certificateAuthorityData is invalid: no base64-encoded PEM certificates found in 28 bytes of data (PEM certificates must begin with "-----BEGIN CERTIFICATE-----")`}, }, }, }}, diff --git a/internal/controller/tlsconfigutil/tls_config_util.go b/internal/controller/tlsconfigutil/tls_config_util.go index 14d1e26dd..f41bb9d7c 100644 --- a/internal/controller/tlsconfigutil/tls_config_util.go +++ b/internal/controller/tlsconfigutil/tls_config_util.go @@ -7,6 +7,7 @@ import ( "crypto/x509" "encoding/base64" "fmt" + "strings" "github.com/pkg/errors" corev1 "k8s.io/api/core/v1" @@ -83,7 +84,7 @@ func TLSSpecForConcierge(source *authenticationv1alpha1.TLSSpec) *TLSSpec { // or as a reference to a kubernetes secret or configmap using the CertificateAuthorityDataSource, and returns // - a condition of type TLSConfigurationValid based on the validity of the ca bundle, // - a pem encoded ca bundle -// - a X509 cert pool with the ca bundle +// - a X509 cert pool with the ca bundle. func ValidateTLSConfig( tlsSpec *TLSSpec, conditionPrefix string, @@ -134,6 +135,7 @@ func getCertPool( var err error caBundle := tlsSpec.CertificateAuthorityData + caBundleLength := len(caBundle) field := fmt.Sprintf("%s.%s", conditionPrefix, "certificateAuthorityData") // the ca data supplied inline in the CRDs is expected to be base64 encoded. // However, the ca data read from kubernetes secrets or config map will not be base64 encoded. @@ -146,6 +148,7 @@ func getCertPool( // this will be used to report in the condition status in case an invalid TLS condition is encountered. field = fmt.Sprintf("%s.%s", conditionPrefix, "certificateAuthorityDataSource") caBundle, err = readCABundleFromSource(tlsSpec.CertificateAuthorityDataSource, namespace, secretInformer, configMapInformer) + caBundleLength = len(caBundle) if err != nil { return nil, nil, fmt.Errorf("%s is invalid: %s", field, err.Error()) } @@ -167,7 +170,14 @@ func getCertPool( ca := x509.NewCertPool() ok := ca.AppendCertsFromPEM(bundleBytes) if !ok { - return nil, nil, fmt.Errorf("%s is invalid: no certificates found", field) + if decodeRequired { + return nil, nil, fmt.Errorf("%s is invalid: no base64-encoded PEM certificates found in %d bytes of data (PEM certificates must begin with \"-----BEGIN CERTIFICATE-----\")", + field, caBundleLength) + } + namespacedName := fmt.Sprintf("%s/%s", namespace, tlsSpec.CertificateAuthorityDataSource.Name) + + return nil, nil, fmt.Errorf(`%s is invalid: key %q with %d bytes of data in %s %q is not a PEM-encoded certificate (PEM certificates must begin with "-----BEGIN CERTIFICATE-----")`, + field, tlsSpec.CertificateAuthorityDataSource.Key, caBundleLength, strings.ToLower(tlsSpec.CertificateAuthorityDataSource.Kind), namespacedName) } return ca, bundleBytes, nil diff --git a/internal/controller/tlsconfigutil/tls_config_util_test.go b/internal/controller/tlsconfigutil/tls_config_util_test.go index cff871cb2..f7ba30ac3 100644 --- a/internal/controller/tlsconfigutil/tls_config_util_test.go +++ b/internal/controller/tlsconfigutil/tls_config_util_test.go @@ -84,7 +84,7 @@ func TestValidateTLSConfig(t *testing.T) { Type: typeTLSConfigurationValid, Status: metav1.ConditionFalse, Reason: ReasonInvalidTLSConfig, - Message: "spec.foo.tls.certificateAuthorityData is invalid: no certificates found", + Message: `spec.foo.tls.certificateAuthorityData is invalid: no base64-encoded PEM certificates found in 88 bytes of data (PEM certificates must begin with "-----BEGIN CERTIFICATE-----")`, }, }, { @@ -265,6 +265,35 @@ func TestValidateTLSConfig(t *testing.T) { Message: `spec.foo.tls.certificateAuthorityDataSource is invalid: key "ca-bundle" has empty value in secret "awesome-namespace/awesome-secret"`, }, }, + { + name: "should return invalid condition when a secret has the configured key but the value is not a cert", + tlsSpec: &TLSSpec{ + CertificateAuthorityDataSource: &caBundleSource{ + Kind: "Secret", + Name: "awesome-secret", + Key: "ca-bundle", + }, + }, + namespace: "awesome-namespace", + k8sObjects: []runtime.Object{ + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "awesome-secret", + Namespace: "awesome-namespace", + }, + Type: corev1.SecretTypeOpaque, + Data: map[string][]byte{ + "ca-bundle": []byte("this is not a certificate"), + }, + }, + }, + expectedCondition: &metav1.Condition{ + Type: typeTLSConfigurationValid, + Status: metav1.ConditionFalse, + Reason: ReasonInvalidTLSConfig, + Message: `spec.foo.tls.certificateAuthorityDataSource is invalid: key "ca-bundle" with 25 bytes of data in secret "awesome-namespace/awesome-secret" is not a PEM-encoded certificate (PEM certificates must begin with "-----BEGIN CERTIFICATE-----")`, + }, + }, { name: "should return invalid condition when a configmap does not have the configured key", tlsSpec: &TLSSpec{ @@ -321,6 +350,34 @@ func TestValidateTLSConfig(t *testing.T) { Message: `spec.foo.tls.certificateAuthorityDataSource is invalid: key "ca-bundle" has empty value in configmap "awesome-namespace/awesome-configmap"`, }, }, + { + name: "should return invalid condition when a configmap has the configured key but its value not a cert", + tlsSpec: &TLSSpec{ + CertificateAuthorityDataSource: &caBundleSource{ + Kind: "ConfigMap", + Name: "awesome-configmap", + Key: "ca-bundle", + }, + }, + namespace: "awesome-namespace", + k8sObjects: []runtime.Object{ + &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "awesome-configmap", + Namespace: "awesome-namespace", + }, + Data: map[string]string{ + "ca-bundle": "this is not a cert", + }, + }, + }, + expectedCondition: &metav1.Condition{ + Type: typeTLSConfigurationValid, + Status: metav1.ConditionFalse, + Reason: ReasonInvalidTLSConfig, + Message: `spec.foo.tls.certificateAuthorityDataSource is invalid: key "ca-bundle" with 18 bytes of data in configmap "awesome-namespace/awesome-configmap" is not a PEM-encoded certificate (PEM certificates must begin with "-----BEGIN CERTIFICATE-----")`, + }, + }, { name: "should return ca bundle from kubernetes configMap", tlsSpec: &TLSSpec{ From 2a62beeb5fb0bd680638950075058fba4b6f31d6 Mon Sep 17 00:00:00 2001 From: Ashish Amarnath Date: Thu, 25 Jul 2024 13:06:52 -0700 Subject: [PATCH 52/99] store ca bundle hash in validated settings cache Signed-off-by: Ashish Amarnath --- .../active_directory_upstream_watcher_test.go | 28 +++++++++++++++++++ .../ldap_upstream_watcher_test.go | 18 ++++++++++++ .../upstreamwatchers/upstream_watchers.go | 13 ++++++--- 3 files changed, 55 insertions(+), 4 deletions(-) diff --git a/internal/controller/supervisorconfig/activedirectoryupstreamwatcher/active_directory_upstream_watcher_test.go b/internal/controller/supervisorconfig/activedirectoryupstreamwatcher/active_directory_upstream_watcher_test.go index c956a9635..1b2d5b242 100644 --- a/internal/controller/supervisorconfig/activedirectoryupstreamwatcher/active_directory_upstream_watcher_test.go +++ b/internal/controller/supervisorconfig/activedirectoryupstreamwatcher/active_directory_upstream_watcher_test.go @@ -5,6 +5,7 @@ package activedirectoryupstreamwatcher import ( "context" + "crypto/sha256" "encoding/base64" "errors" "fmt" @@ -469,6 +470,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, + CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithTLS.CABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(activeDirectoryConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), SearchBaseFoundCondition: condPtr(withoutTime(searchBaseFoundInConfigCondition(0))), @@ -669,6 +671,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, + CABundlePEMSHA256: sha256.Sum256(nil), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(activeDirectoryConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), SearchBaseFoundCondition: condPtr(withoutTime(searchBaseFoundInConfigCondition(0))), @@ -739,6 +742,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, + CABundlePEMSHA256: sha256.Sum256(nil), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(activeDirectoryConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), SearchBaseFoundCondition: condPtr(withoutTime(searchBaseFoundInConfigCondition(0))), @@ -815,6 +819,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, IDPSpecGeneration: 1234, + CABundlePEMSHA256: sha256.Sum256(testCABundle), ConnectionValidCondition: &metav1.Condition{ Type: "LDAPConnectionValid", Status: "True", @@ -949,6 +954,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, + CABundlePEMSHA256: sha256.Sum256(nil), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(activeDirectoryConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), SearchBaseFoundCondition: condPtr(withoutTime(searchBaseFoundInConfigCondition(0))), @@ -1001,6 +1007,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, + CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithTLS.CABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(activeDirectoryConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), SearchBaseFoundCondition: condPtr(withoutTime(searchBaseFoundInConfigCondition(0))), @@ -1162,6 +1169,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, + CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithTLS.CABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(activeDirectoryConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), SearchBaseFoundCondition: condPtr(withoutTime(searchBaseFoundInConfigCondition(0))), @@ -1182,6 +1190,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, + CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithTLS.CABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(activeDirectoryConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), SearchBaseFoundCondition: condPtr(withoutTime(searchBaseFoundInConfigCondition(0))), @@ -1253,6 +1262,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { BindSecretResourceVersion: "4242", LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: exampleDefaultNamingContext, + CABundlePEMSHA256: sha256.Sum256(testCABundle), GroupSearchBase: testGroupSearchBase, IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(activeDirectoryConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), @@ -1275,6 +1285,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: exampleDefaultNamingContext, GroupSearchBase: testGroupSearchBase, + CABundlePEMSHA256: sha256.Sum256(testCABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(activeDirectoryConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), SearchBaseFoundCondition: condPtr(withoutTime(searchBaseFoundInRootDSECondition(0))), @@ -1327,6 +1338,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: exampleDefaultNamingContext, GroupSearchBase: testGroupSearchBase, + CABundlePEMSHA256: sha256.Sum256(testCABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(activeDirectoryConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), SearchBaseFoundCondition: condPtr(withoutTime(searchBaseFoundInRootDSECondition(0))), @@ -1346,6 +1358,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { BindSecretResourceVersion: "4242", LDAPConnectionProtocol: upstreamldap.StartTLS, IDPSpecGeneration: 1234, + CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithStartTLS.CABundle), UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, ConnectionValidCondition: condPtr(activeDirectoryConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), @@ -1367,6 +1380,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.StartTLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, + CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithStartTLS.CABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(activeDirectoryConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), SearchBaseFoundCondition: condPtr(withoutTime(searchBaseFoundInConfigCondition(0))), @@ -1386,6 +1400,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, + CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithTLS.CABundle), IDPSpecGeneration: 1233, ConnectionValidCondition: condPtr(activeDirectoryConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), SearchBaseFoundCondition: condPtr(withoutTime(searchBaseFoundInConfigCondition(0))), @@ -1408,6 +1423,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, + CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithTLS.CABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(activeDirectoryConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), SearchBaseFoundCondition: condPtr(withoutTime(searchBaseFoundInConfigCondition(0))), @@ -1428,6 +1444,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { IDPSpecGeneration: 1234, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, + CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithTLS.CABundle), ConnectionValidCondition: condPtr(activeDirectoryConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), // already previously validated with version 4242 SearchBaseFoundCondition: condPtr(withoutTime(searchBaseFoundInConfigCondition(0))), }}, @@ -1448,6 +1465,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, + CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithTLS.CABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(activeDirectoryConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), SearchBaseFoundCondition: condPtr(withoutTime(searchBaseFoundInConfigCondition(0))), @@ -1487,6 +1505,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, + CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithTLS.CABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(activeDirectoryConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), SearchBaseFoundCondition: condPtr(withoutTime(searchBaseFoundInConfigCondition(0))), @@ -1506,6 +1525,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, + CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithTLS.CABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(activeDirectoryConnectionValidTrueConditionWithoutTimeOrGeneration("4241")), SearchBaseFoundCondition: condPtr(withoutTime(searchBaseFoundInConfigCondition(0))), @@ -1528,6 +1548,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, + CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithTLS.CABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(activeDirectoryConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), SearchBaseFoundCondition: condPtr(withoutTime(searchBaseFoundInConfigCondition(0))), @@ -1589,6 +1610,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, + CABundlePEMSHA256: sha256.Sum256(testCABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(activeDirectoryConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), SearchBaseFoundCondition: condPtr(withoutTime(searchBaseFoundInConfigCondition(0))), @@ -1654,6 +1676,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: exampleDefaultNamingContext, GroupSearchBase: exampleDefaultNamingContext, + CABundlePEMSHA256: sha256.Sum256(testCABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(activeDirectoryConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), SearchBaseFoundCondition: condPtr(withoutTime(searchBaseFoundInRootDSECondition(0))), @@ -1718,6 +1741,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: exampleDefaultNamingContext, GroupSearchBase: testGroupSearchBase, + CABundlePEMSHA256: sha256.Sum256(testCABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(activeDirectoryConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), SearchBaseFoundCondition: condPtr(withoutTime(searchBaseFoundInRootDSECondition(0))), @@ -1782,6 +1806,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: exampleDefaultNamingContext, + CABundlePEMSHA256: sha256.Sum256(testCABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(activeDirectoryConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), SearchBaseFoundCondition: condPtr(withoutTime(searchBaseFoundInRootDSECondition(0))), @@ -1938,6 +1963,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, + CABundlePEMSHA256: sha256.Sum256(testCABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(activeDirectoryConnectionValidTrueConditionWithoutTimeOrGeneration("4241")), SearchBaseFoundCondition: condPtr(withoutTime(searchBaseFoundInRootDSECondition(0))), @@ -1994,6 +2020,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, GroupSearchBase: exampleDefaultNamingContext, UserSearchBase: testUserSearchBase, + CABundlePEMSHA256: sha256.Sum256(testCABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(activeDirectoryConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), SearchBaseFoundCondition: condPtr(withoutTime(searchBaseFoundInRootDSECondition(0))), @@ -2064,6 +2091,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, + CABundlePEMSHA256: sha256.Sum256(testCABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(activeDirectoryConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), SearchBaseFoundCondition: condPtr(withoutTime(searchBaseFoundInConfigCondition(0))), diff --git a/internal/controller/supervisorconfig/ldapupstreamwatcher/ldap_upstream_watcher_test.go b/internal/controller/supervisorconfig/ldapupstreamwatcher/ldap_upstream_watcher_test.go index 77269be21..30a7d48f0 100644 --- a/internal/controller/supervisorconfig/ldapupstreamwatcher/ldap_upstream_watcher_test.go +++ b/internal/controller/supervisorconfig/ldapupstreamwatcher/ldap_upstream_watcher_test.go @@ -5,6 +5,7 @@ package ldapupstreamwatcher import ( "context" + "crypto/sha256" "encoding/base64" "errors" "fmt" @@ -399,6 +400,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, + CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithTLS.CABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(ldapConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), }}, @@ -591,6 +593,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, + CABundlePEMSHA256: sha256.Sum256(nil), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(ldapConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), }}, @@ -658,6 +661,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.StartTLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, + CABundlePEMSHA256: sha256.Sum256(testCABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: &metav1.Condition{ Type: "LDAPConnectionValid", @@ -778,6 +782,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, + CABundlePEMSHA256: sha256.Sum256(nil), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(ldapConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), }}, @@ -829,6 +834,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, + CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithTLS.CABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(ldapConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), }}, @@ -881,6 +887,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, + CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithTLS.CABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(ldapConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), }}, @@ -900,6 +907,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, + CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithTLS.CABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(ldapConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), }}, @@ -918,6 +926,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.StartTLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, + CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithStartTLS.CABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(ldapConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), }}, @@ -937,6 +946,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.StartTLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, + CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithStartTLS.CABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(ldapConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), }}, @@ -954,6 +964,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { BindSecretResourceVersion: "4242", LDAPConnectionProtocol: upstreamldap.TLS, IDPSpecGeneration: 1233, + CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithTLS.CABundle), UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, }}, @@ -975,6 +986,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, + CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithTLS.CABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(ldapConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), }}, @@ -994,6 +1006,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { IDPSpecGeneration: 1234, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, + CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithTLS.CABundle), ConnectionValidCondition: condPtr(ldapConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), // already previously validated with version 4242 }}, setupMocks: func(conn *mockldapconn.MockConn) { @@ -1013,6 +1026,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, + CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithTLS.CABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(ldapConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), }}, @@ -1051,6 +1065,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, + CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithTLS.CABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(ldapConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), }}, @@ -1094,6 +1109,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, + CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithTLS.CABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(ldapConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), }}, @@ -1132,6 +1148,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, + CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithTLS.CABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(ldapConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), }}}, @@ -1193,6 +1210,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, + CABundlePEMSHA256: sha256.Sum256(testCABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(ldapConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), }}, diff --git a/internal/controller/supervisorconfig/upstreamwatchers/upstream_watchers.go b/internal/controller/supervisorconfig/upstreamwatchers/upstream_watchers.go index 81d791d80..7bb9abff6 100644 --- a/internal/controller/supervisorconfig/upstreamwatchers/upstream_watchers.go +++ b/internal/controller/supervisorconfig/upstreamwatchers/upstream_watchers.go @@ -5,6 +5,7 @@ package upstreamwatchers import ( "context" + "crypto/sha256" "fmt" "time" @@ -40,8 +41,9 @@ const ( // ValidatedSettings is the struct which is cached by the ValidatedSettingsCacheI interface. type ValidatedSettings struct { - IDPSpecGeneration int64 // which IDP spec was used during the validation - BindSecretResourceVersion string // which bind secret was used during the validation + IDPSpecGeneration int64 // which IDP spec was used during the validation + BindSecretResourceVersion string // which bind secret was used during the validation + CABundlePEMSHA256 [32]byte // hash of the CA bundle used during the validation // Cache the setting for TLS vs StartTLS. This is always auto-discovered by probing the server. LDAPConnectionProtocol upstreamldap.LDAPConnectionProtocol @@ -277,11 +279,13 @@ func validateAndSetLDAPServerConnectivityAndSearchBase( config *upstreamldap.ProviderConfig, currentSecretVersion string, ) (*metav1.Condition, *metav1.Condition) { - // TODO: if the CA bundle has changed, then we should redo the below connection probes. So maybe this cache should also include the CA bundle (or the hash of the bundle) as part of the lookup? validatedSettings, hasPreviousValidatedSettings := validatedSettingsCache.Get(upstream.Name(), currentSecretVersion, upstream.Generation()) var ldapConnectionValidCondition, searchBaseFoundCondition *metav1.Condition - if hasPreviousValidatedSettings && validatedSettings.UserSearchBase != "" && validatedSettings.GroupSearchBase != "" { + if hasPreviousValidatedSettings && + validatedSettings.UserSearchBase != "" && + validatedSettings.GroupSearchBase != "" && + validatedSettings.CABundlePEMSHA256 == sha256.Sum256(config.CABundle) { // Found previously validated settings in the cache (which is also not missing search base fields), so use them. config.ConnectionProtocol = validatedSettings.LDAPConnectionProtocol config.UserSearch.Base = validatedSettings.UserSearchBase @@ -309,6 +313,7 @@ func validateAndSetLDAPServerConnectivityAndSearchBase( validatedSettingsCache.Set(upstream.Name(), ValidatedSettings{ IDPSpecGeneration: upstream.Generation(), BindSecretResourceVersion: currentSecretVersion, + CABundlePEMSHA256: sha256.Sum256(config.CABundle), LDAPConnectionProtocol: config.ConnectionProtocol, UserSearchBase: config.UserSearch.Base, GroupSearchBase: config.GroupSearch.Base, From a1dcba4731a6c669ec0d301e8606b79358ebb0db Mon Sep 17 00:00:00 2001 From: Ashish Amarnath Date: Thu, 25 Jul 2024 14:10:38 -0700 Subject: [PATCH 53/99] add unit tests for validatedsettings cache storing ca bundle hash Signed-off-by: Ashish Amarnath --- .../active_directory_upstream_watcher_test.go | 205 ++++++++++++++---- .../ldap_upstream_watcher_test.go | 128 +++++++++++ 2 files changed, 295 insertions(+), 38 deletions(-) diff --git a/internal/controller/supervisorconfig/activedirectoryupstreamwatcher/active_directory_upstream_watcher_test.go b/internal/controller/supervisorconfig/activedirectoryupstreamwatcher/active_directory_upstream_watcher_test.go index 1b2d5b242..e4ff38e32 100644 --- a/internal/controller/supervisorconfig/activedirectoryupstreamwatcher/active_directory_upstream_watcher_test.go +++ b/internal/controller/supervisorconfig/activedirectoryupstreamwatcher/active_directory_upstream_watcher_test.go @@ -235,6 +235,9 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { testGroupSearchFilter = "test-group-search-filter" testGroupSearchUserAttributeForFilter = "test-group-search-filter-user-attr-for-filter" testGroupSearchNameAttrName = "test-group-name-attr" + + caBundleConfigMapName = "test-ca-bundle-cm" + caBundleSecretName = "test-ca-bundle-secret" ) testValidSecretData := map[string][]byte{"username": []byte(testBindUsername), "password": []byte(testBindPassword)} @@ -269,6 +272,51 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { }, }, } + + validUpstreamWithConfigMapCABundleSource := validUpstream.DeepCopy() + validUpstreamWithConfigMapCABundleSource.Spec.TLS.CertificateAuthorityData = "" + validUpstreamWithConfigMapCABundleSource.Spec.TLS.CertificateAuthorityDataSource = &idpv1alpha1.CABundleSource{ + Kind: "ConfigMap", + Name: caBundleConfigMapName, + Key: "ca.crt", + } + caBundleConfigMap := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{Name: caBundleConfigMapName, Namespace: testNamespace}, + Data: map[string]string{ + "ca.crt": string(testCABundle), + }, + } + + validUpstreamWithOpaqueSecretCABundleSource := validUpstream.DeepCopy() + validUpstreamWithOpaqueSecretCABundleSource.Spec.TLS.CertificateAuthorityData = "" + validUpstreamWithOpaqueSecretCABundleSource.Spec.TLS.CertificateAuthorityDataSource = &idpv1alpha1.CABundleSource{ + Kind: "Secret", + Name: caBundleSecretName, + Key: "ca.crt", + } + caBundleOpaqueSecret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Name: caBundleSecretName, Namespace: testNamespace}, + Type: corev1.SecretTypeOpaque, + Data: map[string][]byte{ + "ca.crt": testCABundle, + }, + } + + validUpstreamWithTLSSecretCABundleSource := validUpstream.DeepCopy() + validUpstreamWithTLSSecretCABundleSource.Spec.TLS.CertificateAuthorityData = "" + validUpstreamWithTLSSecretCABundleSource.Spec.TLS.CertificateAuthorityDataSource = &idpv1alpha1.CABundleSource{ + Kind: "Secret", + Name: caBundleSecretName, + Key: "ca.crt", + } + caBundleTLSSecret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Name: caBundleSecretName, Namespace: testNamespace}, + Type: corev1.SecretTypeTLS, + Data: map[string][]byte{ + "ca.crt": testCABundle, + }, + } + editedValidUpstream := func(editFunc func(*idpv1alpha1.ActiveDirectoryIdentityProvider)) *idpv1alpha1.ActiveDirectoryIdentityProvider { deepCopy := validUpstream.DeepCopy() editFunc(deepCopy) @@ -436,7 +484,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { name string initialValidatedSettings map[string]upstreamwatchers.ValidatedSettings inputUpstreams []runtime.Object - inputSecrets []runtime.Object + inputK8sObjects []runtime.Object setupMocks func(conn *mockldapconn.MockConn) dialErrors map[string]error wantErr string @@ -449,9 +497,90 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { wantResultingCache: []*upstreamldap.ProviderConfig{}, }, { - name: "one valid upstream updates the cache to include only that upstream", - inputUpstreams: []runtime.Object{validUpstream}, - inputSecrets: []runtime.Object{validBindUserSecret("4242")}, + name: "one valid upstream using a configmap to source ca bundle should include that one upstream", + inputUpstreams: []runtime.Object{validUpstreamWithConfigMapCABundleSource}, + inputK8sObjects: []runtime.Object{validBindUserSecret("4242"), caBundleConfigMap}, + setupMocks: func(conn *mockldapconn.MockConn) { + // Should perform a test dial and bind. + conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1) + conn.EXPECT().Close().Times(1) + }, + wantResultingCache: []*upstreamldap.ProviderConfig{providerConfigForValidUpstreamWithTLS}, + wantResultingUpstreams: []idpv1alpha1.ActiveDirectoryIdentityProvider{{ + ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testName, UID: testResourceUID, Generation: 1234}, + Status: idpv1alpha1.ActiveDirectoryIdentityProviderStatus{ + Phase: "Ready", + Conditions: allConditionsTrue(1234, "4242"), + }, + }}, wantValidatedSettings: map[string]upstreamwatchers.ValidatedSettings{testName: { + BindSecretResourceVersion: "4242", + LDAPConnectionProtocol: upstreamldap.TLS, + UserSearchBase: testUserSearchBase, + GroupSearchBase: testGroupSearchBase, + CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithTLS.CABundle), + IDPSpecGeneration: 1234, + ConnectionValidCondition: condPtr(activeDirectoryConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), + SearchBaseFoundCondition: condPtr(withoutTime(searchBaseFoundInConfigCondition(0))), + }}, + }, + { + name: "one valid upstream using an opaque secret to source ca bundle should include that one upstream", + inputUpstreams: []runtime.Object{validUpstreamWithOpaqueSecretCABundleSource}, + inputK8sObjects: []runtime.Object{validBindUserSecret("4242"), caBundleOpaqueSecret}, + setupMocks: func(conn *mockldapconn.MockConn) { + // Should perform a test dial and bind. + conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1) + conn.EXPECT().Close().Times(1) + }, + wantResultingCache: []*upstreamldap.ProviderConfig{providerConfigForValidUpstreamWithTLS}, + wantResultingUpstreams: []idpv1alpha1.ActiveDirectoryIdentityProvider{{ + ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testName, UID: testResourceUID, Generation: 1234}, + Status: idpv1alpha1.ActiveDirectoryIdentityProviderStatus{ + Phase: "Ready", + Conditions: allConditionsTrue(1234, "4242"), + }, + }}, wantValidatedSettings: map[string]upstreamwatchers.ValidatedSettings{testName: { + BindSecretResourceVersion: "4242", + LDAPConnectionProtocol: upstreamldap.TLS, + UserSearchBase: testUserSearchBase, + GroupSearchBase: testGroupSearchBase, + CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithTLS.CABundle), + IDPSpecGeneration: 1234, + ConnectionValidCondition: condPtr(activeDirectoryConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), + SearchBaseFoundCondition: condPtr(withoutTime(searchBaseFoundInConfigCondition(0))), + }}, + }, + { + name: "one valid upstream using a TLS secret to source ca bundle should include that one upstream", + inputUpstreams: []runtime.Object{validUpstreamWithTLSSecretCABundleSource}, + inputK8sObjects: []runtime.Object{validBindUserSecret("4242"), caBundleTLSSecret}, + setupMocks: func(conn *mockldapconn.MockConn) { + // Should perform a test dial and bind. + conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1) + conn.EXPECT().Close().Times(1) + }, + wantResultingCache: []*upstreamldap.ProviderConfig{providerConfigForValidUpstreamWithTLS}, + wantResultingUpstreams: []idpv1alpha1.ActiveDirectoryIdentityProvider{{ + ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testName, UID: testResourceUID, Generation: 1234}, + Status: idpv1alpha1.ActiveDirectoryIdentityProviderStatus{ + Phase: "Ready", + Conditions: allConditionsTrue(1234, "4242"), + }, + }}, wantValidatedSettings: map[string]upstreamwatchers.ValidatedSettings{testName: { + BindSecretResourceVersion: "4242", + LDAPConnectionProtocol: upstreamldap.TLS, + UserSearchBase: testUserSearchBase, + GroupSearchBase: testGroupSearchBase, + CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithTLS.CABundle), + IDPSpecGeneration: 1234, + ConnectionValidCondition: condPtr(activeDirectoryConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), + SearchBaseFoundCondition: condPtr(withoutTime(searchBaseFoundInConfigCondition(0))), + }}, + }, + { + name: "one valid upstream updates the cache to include only that upstream", + inputUpstreams: []runtime.Object{validUpstream}, + inputK8sObjects: []runtime.Object{validBindUserSecret("4242")}, setupMocks: func(conn *mockldapconn.MockConn) { // Should perform a test dial and bind. conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1) @@ -479,7 +608,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { { name: "missing secret", inputUpstreams: []runtime.Object{validUpstream}, - inputSecrets: []runtime.Object{}, + inputK8sObjects: []runtime.Object{}, wantErr: controllerlib.ErrSyntheticRequeue.Error(), wantResultingCache: []*upstreamldap.ProviderConfig{}, wantResultingUpstreams: []idpv1alpha1.ActiveDirectoryIdentityProvider{{ @@ -503,7 +632,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { { name: "secret has wrong type", inputUpstreams: []runtime.Object{validUpstream}, - inputSecrets: []runtime.Object{&corev1.Secret{ + inputK8sObjects: []runtime.Object{&corev1.Secret{ ObjectMeta: metav1.ObjectMeta{Name: testBindSecretName, Namespace: testNamespace}, Type: "some-other-type", Data: testValidSecretData, @@ -531,7 +660,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { { name: "secret is missing key", inputUpstreams: []runtime.Object{validUpstream}, - inputSecrets: []runtime.Object{&corev1.Secret{ + inputK8sObjects: []runtime.Object{&corev1.Secret{ ObjectMeta: metav1.ObjectMeta{Name: testBindSecretName, Namespace: testNamespace}, Type: corev1.SecretTypeBasicAuth, }}, @@ -560,7 +689,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { inputUpstreams: []runtime.Object{editedValidUpstream(func(upstream *idpv1alpha1.ActiveDirectoryIdentityProvider) { upstream.Spec.TLS.CertificateAuthorityData = "this-is-not-base64-encoded" })}, - inputSecrets: []runtime.Object{validBindUserSecret("")}, + inputK8sObjects: []runtime.Object{validBindUserSecret("")}, wantErr: controllerlib.ErrSyntheticRequeue.Error(), wantResultingCache: []*upstreamldap.ProviderConfig{}, wantResultingUpstreams: []idpv1alpha1.ActiveDirectoryIdentityProvider{{ @@ -586,7 +715,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { inputUpstreams: []runtime.Object{editedValidUpstream(func(upstream *idpv1alpha1.ActiveDirectoryIdentityProvider) { upstream.Spec.TLS.CertificateAuthorityData = base64.StdEncoding.EncodeToString([]byte("this is not pem data")) })}, - inputSecrets: []runtime.Object{validBindUserSecret("")}, + inputK8sObjects: []runtime.Object{validBindUserSecret("")}, wantErr: controllerlib.ErrSyntheticRequeue.Error(), wantResultingCache: []*upstreamldap.ProviderConfig{}, wantResultingUpstreams: []idpv1alpha1.ActiveDirectoryIdentityProvider{{ @@ -612,7 +741,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { inputUpstreams: []runtime.Object{editedValidUpstream(func(upstream *idpv1alpha1.ActiveDirectoryIdentityProvider) { upstream.Spec.TLS = nil })}, - inputSecrets: []runtime.Object{validBindUserSecret("4242")}, + inputK8sObjects: []runtime.Object{validBindUserSecret("4242")}, setupMocks: func(conn *mockldapconn.MockConn) { // Should perform a test dial and bind. conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1) @@ -683,7 +812,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { upstream.Spec.TLS = nil upstream.Spec.GroupSearch.Attributes.GroupName = "sAMAccountName" })}, - inputSecrets: []runtime.Object{validBindUserSecret("4242")}, + inputK8sObjects: []runtime.Object{validBindUserSecret("4242")}, setupMocks: func(conn *mockldapconn.MockConn) { // Should perform a test dial and bind. conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1) @@ -753,7 +882,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { inputUpstreams: []runtime.Object{editedValidUpstream(func(upstream *idpv1alpha1.ActiveDirectoryIdentityProvider) { upstream.Spec.Host = "ldap.example.com" // when the port is not specified, automatically switch ports for StartTLS })}, - inputSecrets: []runtime.Object{validBindUserSecret("4242")}, + inputK8sObjects: []runtime.Object{validBindUserSecret("4242")}, setupMocks: func(conn *mockldapconn.MockConn) { // Should perform a test dial and bind. conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1) @@ -836,7 +965,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { inputUpstreams: []runtime.Object{editedValidUpstream(func(upstream *idpv1alpha1.ActiveDirectoryIdentityProvider) { upstream.Spec.Host = "ldap.example.com:5678" // when the port is specified, do not automatically switch ports for StartTLS })}, - inputSecrets: []runtime.Object{validBindUserSecret("4242")}, + inputK8sObjects: []runtime.Object{validBindUserSecret("4242")}, setupMocks: func(conn *mockldapconn.MockConn) { // Both dials fail, so there should be no bind. }, @@ -902,7 +1031,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { inputUpstreams: []runtime.Object{editedValidUpstream(func(upstream *idpv1alpha1.ActiveDirectoryIdentityProvider) { upstream.Spec.TLS.CertificateAuthorityData = "" })}, - inputSecrets: []runtime.Object{validBindUserSecret("4242")}, + inputK8sObjects: []runtime.Object{validBindUserSecret("4242")}, setupMocks: func(conn *mockldapconn.MockConn) { // Should perform a test dial and bind. conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1) @@ -968,7 +1097,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { upstream.Spec.Bind.SecretName = "non-existent-secret" upstream.UID = "other-uid" })}, - inputSecrets: []runtime.Object{validBindUserSecret("4242")}, + inputK8sObjects: []runtime.Object{validBindUserSecret("4242")}, setupMocks: func(conn *mockldapconn.MockConn) { // Should perform a test dial and bind for the one valid upstream configuration. conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1) @@ -1017,8 +1146,8 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { name: "when testing the connection to the LDAP server fails then the upstream is still added to the cache anyway but not to validatedsettings (treated like a warning)", // If we can't connect, we can still try to allow users to log in, but update the conditions to say that there's a problem // Also don't add anything to the validated settings so that the next time this runs we can try again. - inputUpstreams: []runtime.Object{validUpstream}, - inputSecrets: []runtime.Object{validBindUserSecret("")}, + inputUpstreams: []runtime.Object{validUpstream}, + inputK8sObjects: []runtime.Object{validBindUserSecret("")}, setupMocks: func(conn *mockldapconn.MockConn) { // Should perform a test dial and bind. // Expect two calls to each of these: once for trying TLS and once for trying StartTLS. @@ -1056,7 +1185,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { inputUpstreams: []runtime.Object{editedValidUpstream(func(upstream *idpv1alpha1.ActiveDirectoryIdentityProvider) { upstream.Spec.UserSearch.Base = "" })}, - inputSecrets: []runtime.Object{validBindUserSecret("")}, + inputK8sObjects: []runtime.Object{validBindUserSecret("")}, setupMocks: func(conn *mockldapconn.MockConn) { // Should perform a test dial and bind. // Expect three calls bind: once for trying TLS and once for trying StartTLS (both fail), and one before querying for defaultNamingContext (succeeds) @@ -1123,7 +1252,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { inputUpstreams: []runtime.Object{editedValidUpstream(func(upstream *idpv1alpha1.ActiveDirectoryIdentityProvider) { upstream.Spec.UserSearch.Base = "" })}, - inputSecrets: []runtime.Object{validBindUserSecret("")}, + inputK8sObjects: []runtime.Object{validBindUserSecret("")}, setupMocks: func(conn *mockldapconn.MockConn) { // Should perform a test dial and bind. // Expect 3 calls to each of these: once for trying TLS and once for trying StartTLS, one before querying @@ -1163,7 +1292,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { searchBaseFoundInConfigCondition(1234), } })}, - inputSecrets: []runtime.Object{validBindUserSecret("4242")}, + inputK8sObjects: []runtime.Object{validBindUserSecret("4242")}, initialValidatedSettings: map[string]upstreamwatchers.ValidatedSettings{testName: { BindSecretResourceVersion: "4242", LDAPConnectionProtocol: upstreamldap.TLS, @@ -1207,7 +1336,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { } upstream.Spec.UserSearch.Base = "" })}, - inputSecrets: []runtime.Object{validBindUserSecret("4242")}, + inputK8sObjects: []runtime.Object{validBindUserSecret("4242")}, initialValidatedSettings: map[string]upstreamwatchers.ValidatedSettings{testName: { BindSecretResourceVersion: "4242", LDAPConnectionProtocol: upstreamldap.TLS, @@ -1279,7 +1408,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { } upstream.Spec.UserSearch.Base = "" })}, - inputSecrets: []runtime.Object{validBindUserSecret("4242")}, + inputK8sObjects: []runtime.Object{validBindUserSecret("4242")}, initialValidatedSettings: map[string]upstreamwatchers.ValidatedSettings{testName: { BindSecretResourceVersion: "4242", LDAPConnectionProtocol: upstreamldap.TLS, @@ -1353,7 +1482,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { searchBaseFoundInConfigCondition(1234), } })}, - inputSecrets: []runtime.Object{validBindUserSecret("4242")}, + inputK8sObjects: []runtime.Object{validBindUserSecret("4242")}, initialValidatedSettings: map[string]upstreamwatchers.ValidatedSettings{testName: { BindSecretResourceVersion: "4242", LDAPConnectionProtocol: upstreamldap.StartTLS, @@ -1394,7 +1523,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { activeDirectoryConnectionValidTrueCondition(1233, "4242"), // older spec generation! } })}, - inputSecrets: []runtime.Object{validBindUserSecret("4242")}, + inputK8sObjects: []runtime.Object{validBindUserSecret("4242")}, initialValidatedSettings: map[string]upstreamwatchers.ValidatedSettings{testName: { BindSecretResourceVersion: "4242", LDAPConnectionProtocol: upstreamldap.TLS, @@ -1437,7 +1566,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { activeDirectoryConnectionValidTrueCondition(1234, "4200"), // old version of the condition, as if the previous update of conditions had failed } })}, - inputSecrets: []runtime.Object{validBindUserSecret("4242")}, + inputK8sObjects: []runtime.Object{validBindUserSecret("4242")}, initialValidatedSettings: map[string]upstreamwatchers.ValidatedSettings{testName: { BindSecretResourceVersion: "4242", LDAPConnectionProtocol: upstreamldap.TLS, @@ -1486,7 +1615,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { }, } })}, - inputSecrets: []runtime.Object{validBindUserSecret("4242")}, + inputK8sObjects: []runtime.Object{validBindUserSecret("4242")}, setupMocks: func(conn *mockldapconn.MockConn) { // Should perform a test dial and bind. conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1) @@ -1519,7 +1648,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { activeDirectoryConnectionValidTrueCondition(1234, "4241"), // same spec generation, old secret version } })}, - inputSecrets: []runtime.Object{validBindUserSecret("4242")}, // newer secret version! + inputK8sObjects: []runtime.Object{validBindUserSecret("4242")}, // newer secret version! initialValidatedSettings: map[string]upstreamwatchers.ValidatedSettings{testName: { BindSecretResourceVersion: "4241", LDAPConnectionProtocol: upstreamldap.TLS, @@ -1562,7 +1691,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { upstream.Spec.GroupSearch.Filter = "" upstream.Spec.GroupSearch.Attributes = idpv1alpha1.ActiveDirectoryIdentityProviderGroupSearchAttributes{} })}, - inputSecrets: []runtime.Object{validBindUserSecret("4242")}, + inputK8sObjects: []runtime.Object{validBindUserSecret("4242")}, setupMocks: func(conn *mockldapconn.MockConn) { // Should perform a test dial and bind. conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1) @@ -1623,7 +1752,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { upstream.Spec.UserSearch.Base = "" upstream.Spec.GroupSearch.Base = "" })}, - inputSecrets: []runtime.Object{validBindUserSecret("4242")}, + inputK8sObjects: []runtime.Object{validBindUserSecret("4242")}, setupMocks: func(conn *mockldapconn.MockConn) { // Should perform a test dial and bind. conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(2) @@ -1688,7 +1817,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { upstream.Spec.UserSearch.Attributes = idpv1alpha1.ActiveDirectoryIdentityProviderUserSearchAttributes{} upstream.Spec.UserSearch.Base = "" })}, - inputSecrets: []runtime.Object{validBindUserSecret("4242")}, + inputK8sObjects: []runtime.Object{validBindUserSecret("4242")}, setupMocks: func(conn *mockldapconn.MockConn) { // Should perform a test dial and bind. conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(2) @@ -1753,7 +1882,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { upstream.Spec.UserSearch.Attributes = idpv1alpha1.ActiveDirectoryIdentityProviderUserSearchAttributes{} upstream.Spec.GroupSearch.Base = "" })}, - inputSecrets: []runtime.Object{validBindUserSecret("4242")}, + inputK8sObjects: []runtime.Object{validBindUserSecret("4242")}, setupMocks: func(conn *mockldapconn.MockConn) { // Should perform a test dial and bind. conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(2) @@ -1818,7 +1947,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { upstream.Spec.UserSearch.Attributes = idpv1alpha1.ActiveDirectoryIdentityProviderUserSearchAttributes{} upstream.Spec.GroupSearch.Base = "" })}, - inputSecrets: []runtime.Object{validBindUserSecret("4242")}, + inputK8sObjects: []runtime.Object{validBindUserSecret("4242")}, setupMocks: func(conn *mockldapconn.MockConn) { // Should perform a test dial and bind. conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(2) @@ -1846,7 +1975,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { upstream.Spec.UserSearch.Attributes = idpv1alpha1.ActiveDirectoryIdentityProviderUserSearchAttributes{} upstream.Spec.GroupSearch.Base = "" })}, - inputSecrets: []runtime.Object{validBindUserSecret("4242")}, + inputK8sObjects: []runtime.Object{validBindUserSecret("4242")}, setupMocks: func(conn *mockldapconn.MockConn) { // Should perform a test dial and bind. conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(2) @@ -1882,7 +2011,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { upstream.Spec.UserSearch.Attributes = idpv1alpha1.ActiveDirectoryIdentityProviderUserSearchAttributes{} upstream.Spec.GroupSearch.Base = "" })}, - inputSecrets: []runtime.Object{validBindUserSecret("4242")}, + inputK8sObjects: []runtime.Object{validBindUserSecret("4242")}, setupMocks: func(conn *mockldapconn.MockConn) { // Should perform a test dial and bind. conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(2) @@ -1924,7 +2053,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { upstream.Spec.UserSearch.Attributes = idpv1alpha1.ActiveDirectoryIdentityProviderUserSearchAttributes{} upstream.Spec.GroupSearch.Base = "" })}, - inputSecrets: []runtime.Object{validBindUserSecret("4242")}, + inputK8sObjects: []runtime.Object{validBindUserSecret("4242")}, setupMocks: func(conn *mockldapconn.MockConn) { // Should perform a test dial and bind. conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(2) @@ -1957,7 +2086,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { upstream.Spec.UserSearch.Attributes = idpv1alpha1.ActiveDirectoryIdentityProviderUserSearchAttributes{} upstream.Spec.GroupSearch.Base = "" })}, - inputSecrets: []runtime.Object{validBindUserSecret("4242")}, + inputK8sObjects: []runtime.Object{validBindUserSecret("4242")}, initialValidatedSettings: map[string]upstreamwatchers.ValidatedSettings{testName: { BindSecretResourceVersion: "4241", LDAPConnectionProtocol: upstreamldap.TLS, @@ -2031,7 +2160,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { inputUpstreams: []runtime.Object{editedValidUpstream(func(upstream *idpv1alpha1.ActiveDirectoryIdentityProvider) { upstream.Spec.GroupSearch.SkipGroupRefresh = true })}, - inputSecrets: []runtime.Object{validBindUserSecret("4242")}, + inputK8sObjects: []runtime.Object{validBindUserSecret("4242")}, setupMocks: func(conn *mockldapconn.MockConn) { // Should perform a test dial and bind. conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1) @@ -2105,7 +2234,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { fakePinnipedClient := supervisorfake.NewSimpleClientset(tt.inputUpstreams...) pinnipedInformers := supervisorinformers.NewSharedInformerFactory(fakePinnipedClient, 0) - fakeKubeClient := fake.NewSimpleClientset(tt.inputSecrets...) + fakeKubeClient := fake.NewSimpleClientset(tt.inputK8sObjects...) kubeInformers := informers.NewSharedInformerFactory(fakeKubeClient, 0) cache := dynamicupstreamprovider.NewDynamicUpstreamIDPProvider() cache.SetActiveDirectoryIdentityProviders([]upstreamprovider.UpstreamLDAPIdentityProviderI{ diff --git a/internal/controller/supervisorconfig/ldapupstreamwatcher/ldap_upstream_watcher_test.go b/internal/controller/supervisorconfig/ldapupstreamwatcher/ldap_upstream_watcher_test.go index 30a7d48f0..ed3e6f6d5 100644 --- a/internal/controller/supervisorconfig/ldapupstreamwatcher/ldap_upstream_watcher_test.go +++ b/internal/controller/supervisorconfig/ldapupstreamwatcher/ldap_upstream_watcher_test.go @@ -234,6 +234,9 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { testGroupSearchFilter = "test-group-search-filter" testGroupSearchUserAttributeForFilter = "test-group-search-filter-user-attr-for-filter" testGroupSearchNameAttrName = "test-group-name-attr" + + caBundleConfigMapName = "test-ca-bundle-cm" + caBundleSecretName = "test-ca-bundle-secret" ) testValidSecretData := map[string][]byte{"username": []byte(testBindUsername), "password": []byte(testBindPassword)} @@ -279,6 +282,50 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { return deepCopy } + validUpstreamWithConfigMapCABundleSource := validUpstream.DeepCopy() + validUpstreamWithConfigMapCABundleSource.Spec.TLS.CertificateAuthorityData = "" + validUpstreamWithConfigMapCABundleSource.Spec.TLS.CertificateAuthorityDataSource = &idpv1alpha1.CABundleSource{ + Kind: "ConfigMap", + Name: caBundleConfigMapName, + Key: "ca.crt", + } + caBundleConfigMap := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{Name: caBundleConfigMapName, Namespace: testNamespace}, + Data: map[string]string{ + "ca.crt": string(testCABundle), + }, + } + + validUpstreamWithOpaqueSecretCABundleSource := validUpstream.DeepCopy() + validUpstreamWithOpaqueSecretCABundleSource.Spec.TLS.CertificateAuthorityData = "" + validUpstreamWithOpaqueSecretCABundleSource.Spec.TLS.CertificateAuthorityDataSource = &idpv1alpha1.CABundleSource{ + Kind: "Secret", + Name: caBundleSecretName, + Key: "ca.crt", + } + caBundleOpaqueSecret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Name: caBundleSecretName, Namespace: testNamespace}, + Type: corev1.SecretTypeOpaque, + Data: map[string][]byte{ + "ca.crt": testCABundle, + }, + } + + validUpstreamWithTLSSecretCABundleSource := validUpstream.DeepCopy() + validUpstreamWithTLSSecretCABundleSource.Spec.TLS.CertificateAuthorityData = "" + validUpstreamWithTLSSecretCABundleSource.Spec.TLS.CertificateAuthorityDataSource = &idpv1alpha1.CABundleSource{ + Kind: "Secret", + Name: caBundleSecretName, + Key: "ca.crt", + } + caBundleTLSSecret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Name: caBundleSecretName, Namespace: testNamespace}, + Type: corev1.SecretTypeTLS, + Data: map[string][]byte{ + "ca.crt": testCABundle, + }, + } + providerConfigForValidUpstreamWithTLS := &upstreamldap.ProviderConfig{ Name: testName, ResourceUID: testResourceUID, @@ -378,6 +425,87 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { name: "no LDAPIdentityProvider upstreams clears the cache", wantResultingCache: []*upstreamldap.ProviderConfig{}, }, + { + name: "one valid upstream using a configmap to source CA bundles updates the cache to include only that upstream", + inputUpstreams: []runtime.Object{validUpstreamWithConfigMapCABundleSource}, + inputSecrets: []runtime.Object{validBindUserSecret("4242"), caBundleConfigMap}, + setupMocks: func(conn *mockldapconn.MockConn) { + // Should perform a test dial and bind. + conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1) + conn.EXPECT().Close().Times(1) + }, + wantResultingCache: []*upstreamldap.ProviderConfig{providerConfigForValidUpstreamWithTLS}, + wantResultingUpstreams: []idpv1alpha1.LDAPIdentityProvider{{ + ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testName, Generation: 1234, UID: testResourceUID}, + Status: idpv1alpha1.LDAPIdentityProviderStatus{ + Phase: "Ready", + Conditions: allConditionsTrue(1234, "4242"), + }, + }}, + wantValidatedSettings: map[string]upstreamwatchers.ValidatedSettings{testName: { + BindSecretResourceVersion: "4242", + LDAPConnectionProtocol: upstreamldap.TLS, + UserSearchBase: testUserSearchBase, + GroupSearchBase: testGroupSearchBase, + CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithTLS.CABundle), + IDPSpecGeneration: 1234, + ConnectionValidCondition: condPtr(ldapConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), + }}, + }, + { + name: "one valid upstream using an opaque secret to source CA bundles updates the cache to include only that upstream", + inputUpstreams: []runtime.Object{validUpstreamWithOpaqueSecretCABundleSource}, + inputSecrets: []runtime.Object{validBindUserSecret("4242"), caBundleOpaqueSecret}, + setupMocks: func(conn *mockldapconn.MockConn) { + // Should perform a test dial and bind. + conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1) + conn.EXPECT().Close().Times(1) + }, + wantResultingCache: []*upstreamldap.ProviderConfig{providerConfigForValidUpstreamWithTLS}, + wantResultingUpstreams: []idpv1alpha1.LDAPIdentityProvider{{ + ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testName, Generation: 1234, UID: testResourceUID}, + Status: idpv1alpha1.LDAPIdentityProviderStatus{ + Phase: "Ready", + Conditions: allConditionsTrue(1234, "4242"), + }, + }}, + wantValidatedSettings: map[string]upstreamwatchers.ValidatedSettings{testName: { + BindSecretResourceVersion: "4242", + LDAPConnectionProtocol: upstreamldap.TLS, + UserSearchBase: testUserSearchBase, + GroupSearchBase: testGroupSearchBase, + CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithTLS.CABundle), + IDPSpecGeneration: 1234, + ConnectionValidCondition: condPtr(ldapConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), + }}, + }, + { + name: "one valid upstream using a TLS secret to source CA bundles updates the cache to include only that upstream", + inputUpstreams: []runtime.Object{validUpstreamWithTLSSecretCABundleSource}, + inputSecrets: []runtime.Object{validBindUserSecret("4242"), caBundleTLSSecret}, + setupMocks: func(conn *mockldapconn.MockConn) { + // Should perform a test dial and bind. + conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1) + conn.EXPECT().Close().Times(1) + }, + wantResultingCache: []*upstreamldap.ProviderConfig{providerConfigForValidUpstreamWithTLS}, + wantResultingUpstreams: []idpv1alpha1.LDAPIdentityProvider{{ + ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testName, Generation: 1234, UID: testResourceUID}, + Status: idpv1alpha1.LDAPIdentityProviderStatus{ + Phase: "Ready", + Conditions: allConditionsTrue(1234, "4242"), + }, + }}, + wantValidatedSettings: map[string]upstreamwatchers.ValidatedSettings{testName: { + BindSecretResourceVersion: "4242", + LDAPConnectionProtocol: upstreamldap.TLS, + UserSearchBase: testUserSearchBase, + GroupSearchBase: testGroupSearchBase, + CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithTLS.CABundle), + IDPSpecGeneration: 1234, + ConnectionValidCondition: condPtr(ldapConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), + }}, + }, { name: "one valid upstream updates the cache to include only that upstream", inputUpstreams: []runtime.Object{validUpstream}, From 005dbf3aa86990fe57e5a14d2a60af837ecd35ba Mon Sep 17 00:00:00 2001 From: Ashish Amarnath Date: Thu, 25 Jul 2024 19:20:57 -0700 Subject: [PATCH 54/99] refactor tlsconfigutil to return a caBundle type Signed-off-by: Ashish Amarnath --- .../jwtcachefiller/jwtcachefiller.go | 14 +-- .../webhookcachefiller/webhookcachefiller.go | 4 +- .../github_upstream_watcher.go | 6 +- .../oidc_upstream_watcher.go | 7 +- .../upstreamwatchers/upstream_watchers.go | 4 +- .../tlsconfigutil/tls_config_util.go | 50 +++++++- .../tlsconfigutil/tls_config_util_test.go | 118 ++++++++++++++---- 7 files changed, 155 insertions(+), 48 deletions(-) diff --git a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go index ee11a13e6..2db9bea1e 100644 --- a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go +++ b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go @@ -7,8 +7,6 @@ package jwtcachefiller import ( "context" - "crypto/sha256" - "crypto/x509" "errors" "fmt" "net/http" @@ -223,8 +221,8 @@ func (c *jwtCacheFillerController) syncIndividualJWTAuthenticator(ctx context.Co } conditions := make([]*metav1.Condition, 0) - certPool, caBundlePEM, conditions, tlsBundleOk := c.validateTLSBundle(jwtAuthenticator.Spec.TLS, conditions) - caBundlePEMSHA256 := sha256.Sum256(caBundlePEM) // note that this will always return the same hash for nil input + caBundle, conditions, tlsBundleOk := c.validateTLSBundle(jwtAuthenticator.Spec.TLS, conditions) + caBundlePEMSHA256 := caBundle.GetCABundleHash() // Only revalidate and update the cache if the cached authenticator is different from the desired authenticator. // There is no need to repeat validations for a spec that was already successfully validated. We are making a @@ -252,7 +250,7 @@ func (c *jwtCacheFillerController) syncIndividualJWTAuthenticator(ctx context.Co _, conditions, issuerOk := c.validateIssuer(jwtAuthenticator.Spec.Issuer, conditions) okSoFar := tlsBundleOk && issuerOk - client := phttp.Default(certPool) + client := phttp.Default(caBundle.GetCertPool()) client.Timeout = 30 * time.Second // copied from Kube OIDC code coreOSCtx := coreosoidc.ClientContext(context.Background(), client) @@ -317,8 +315,8 @@ func (c *jwtCacheFillerController) cacheValueAsJWTAuthenticator(value authncache return jwtAuthenticator } -func (c *jwtCacheFillerController) validateTLSBundle(tlsSpec *authenticationv1alpha1.TLSSpec, conditions []*metav1.Condition) (*x509.CertPool, []byte, []*metav1.Condition, bool) { - condition, pemBundle, certPool := tlsconfigutil.ValidateTLSConfig( +func (c *jwtCacheFillerController) validateTLSBundle(tlsSpec *authenticationv1alpha1.TLSSpec, conditions []*metav1.Condition) (*tlsconfigutil.CABundle, []*metav1.Condition, bool) { + condition, caBundle := tlsconfigutil.ValidateTLSConfig( tlsconfigutil.TLSSpecForConcierge(tlsSpec), "spec.tls", c.namespace, @@ -326,7 +324,7 @@ func (c *jwtCacheFillerController) validateTLSBundle(tlsSpec *authenticationv1al c.configMapInformer) conditions = append(conditions, condition) - return certPool, pemBundle, conditions, condition.Status == metav1.ConditionTrue + return caBundle, conditions, condition.Status == metav1.ConditionTrue } func (c *jwtCacheFillerController) validateIssuer(issuer string, conditions []*metav1.Condition) (*url.URL, []*metav1.Condition, bool) { diff --git a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go index fa47f1c74..9c2eddd59 100644 --- a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go +++ b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go @@ -244,7 +244,7 @@ func (c *webhookCacheFillerController) cacheValueAsWebhookAuthenticator(value au } func (c *webhookCacheFillerController) validateTLSBundle(tlsSpec *authenticationv1alpha1.TLSSpec, conditions []*metav1.Condition) (*x509.CertPool, []byte, []*metav1.Condition, bool) { - condition, pemBundle, certPool := tlsconfigutil.ValidateTLSConfig( + condition, caBundle := tlsconfigutil.ValidateTLSConfig( tlsconfigutil.TLSSpecForConcierge(tlsSpec), "spec.tls", c.namespace, @@ -252,7 +252,7 @@ func (c *webhookCacheFillerController) validateTLSBundle(tlsSpec *authentication c.configMapInformer) conditions = append(conditions, condition) - return certPool, pemBundle, conditions, condition.Status == metav1.ConditionTrue + return caBundle.GetCertPool(), caBundle.GetCABundle(), conditions, condition.Status == metav1.ConditionTrue } // newWebhookAuthenticator creates a webhook from the provided API server url and caBundle diff --git a/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher.go b/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher.go index 7d8da94be..db1301376 100644 --- a/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher.go +++ b/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher.go @@ -325,7 +325,7 @@ func (c *gitHubWatcherController) validateUpstreamAndUpdateConditions(ctx contro hostCondition, hostPort := validateHost(upstream.Spec.GitHubAPI) conditions = append(conditions, hostCondition) - tlsConfigCondition, caBundlePEM, certPool := tlsconfigutil.ValidateTLSConfig( + tlsConfigCondition, caBundle := tlsconfigutil.ValidateTLSConfig( tlsconfigutil.TLSSpecForSupervisor(upstream.Spec.GitHubAPI.TLS), "spec.githubAPI.tls", c.namespace, @@ -335,8 +335,8 @@ func (c *gitHubWatcherController) validateUpstreamAndUpdateConditions(ctx contro githubConnectionCondition, hostURL, httpClient, githubConnectionErr := c.validateGitHubConnection( hostPort, - caBundlePEM, - certPool, + caBundle.GetCABundle(), + caBundle.GetCertPool(), hostCondition.Status == metav1.ConditionTrue, tlsConfigCondition.Status == metav1.ConditionTrue, ) diff --git a/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher.go b/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher.go index a2a85f21c..10ad7ad9a 100644 --- a/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher.go +++ b/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher.go @@ -6,7 +6,6 @@ package oidcupstreamwatcher import ( "context" - "crypto/sha256" "crypto/x509" "fmt" "net/http" @@ -334,7 +333,7 @@ func (c *oidcWatcherController) validateSecret(upstream *idpv1alpha1.OIDCIdentit // validateIssuer validates the .spec.issuer field, performs OIDC discovery, and returns the appropriate OIDCDiscoverySucceeded condition. func (c *oidcWatcherController) validateIssuer(ctx context.Context, upstream *idpv1alpha1.OIDCIdentityProvider, result *upstreamoidc.ProviderConfig) []*metav1.Condition { - tlsCondition, caBundlePEM, certPool := tlsconfigutil.ValidateTLSConfig( + tlsCondition, caBundle := tlsconfigutil.ValidateTLSConfig( tlsconfigutil.TLSSpecForSupervisor(upstream.Spec.TLS), "spec.tls", upstream.Namespace, @@ -360,7 +359,7 @@ func (c *oidcWatcherController) validateIssuer(ctx context.Context, upstream *id // Get the discovered provider and HTTP client from cache, if they are found in the cache. cacheKey := oidcDiscoveryCacheKey{ issuer: upstream.Spec.Issuer, - caBundleHash: sha256.Sum256(caBundlePEM), // note that this will always return the same hash for nil input + caBundleHash: caBundle.GetCABundleHash(), // note that this will always return the same hash for nil input } if cacheEntry := c.validatorCache.getProvider(cacheKey); cacheEntry != nil { discoveredProvider = cacheEntry.provider @@ -374,7 +373,7 @@ func (c *oidcWatcherController) validateIssuer(ctx context.Context, upstream *id // If the provider does not exist in the cache, do a fresh discovery lookup and save to the cache. if discoveredProvider == nil { - httpClient = defaultClientShortTimeout(certPool) + httpClient = defaultClientShortTimeout(caBundle.GetCertPool()) _, issuerURLCondition := validateHTTPSURL(upstream.Spec.Issuer, "issuer", reasonUnreachable) if issuerURLCondition != nil { diff --git a/internal/controller/supervisorconfig/upstreamwatchers/upstream_watchers.go b/internal/controller/supervisorconfig/upstreamwatchers/upstream_watchers.go index 7bb9abff6..183203941 100644 --- a/internal/controller/supervisorconfig/upstreamwatchers/upstream_watchers.go +++ b/internal/controller/supervisorconfig/upstreamwatchers/upstream_watchers.go @@ -256,9 +256,9 @@ func ValidateGenericLDAP( conditions.Append(secretValidCondition, true) tlsSpec := tlsconfigutil.TLSSpecForSupervisor(upstream.Spec().TLSSpec()) - tlsValidCondition, caBundle, _ := tlsconfigutil.ValidateTLSConfig(tlsSpec, "spec.tls", upstream.Namespace(), secretInformer, configMapInformer) + tlsValidCondition, caBundle := tlsconfigutil.ValidateTLSConfig(tlsSpec, "spec.tls", upstream.Namespace(), secretInformer, configMapInformer) conditions.Append(tlsValidCondition, true) - config.CABundle = caBundle + config.CABundle = caBundle.GetCABundle() var ldapConnectionValidCondition, searchBaseFoundCondition *metav1.Condition // No point in trying to connect to the server if the config was already determined to be invalid. diff --git a/internal/controller/tlsconfigutil/tls_config_util.go b/internal/controller/tlsconfigutil/tls_config_util.go index f41bb9d7c..a26e7738e 100644 --- a/internal/controller/tlsconfigutil/tls_config_util.go +++ b/internal/controller/tlsconfigutil/tls_config_util.go @@ -4,6 +4,7 @@ package tlsconfigutil import ( + "crypto/sha256" "crypto/x509" "encoding/base64" "fmt" @@ -80,18 +81,54 @@ func TLSSpecForConcierge(source *authenticationv1alpha1.TLSSpec) *TLSSpec { return dest } +// CABundle abstracts the internal representation of CA certificate bundles. +type CABundle struct { + caBundle []byte + caCertPool *x509.CertPool +} + +// GetCABundle returns the CA certificate bundle PEM bytes. +func (c *CABundle) GetCABundle() []byte { + return c.caBundle +} + +// GetCABundlePemString returns the certificate bundle PEM formatted as a string. +func (c *CABundle) GetCABundlePemString() string { + return string(c.caBundle) +} + +// GetCertPool returns a X509 cert pool with the CA certificate bundle. +func (c *CABundle) GetCertPool() *x509.CertPool { + return c.caCertPool +} + +// GetCABundleHash returns a sha256 sum of the CA bundle bytes. +func (c *CABundle) GetCABundleHash() [32]byte { + return sha256.Sum256(c.caBundle) // note that this will always return the same hash for nil input +} + +// IsEqual returns whether a CABundle has the same CA certificate bundle as another. +func (l *CABundle) IsEqual(r *CABundle) bool { + if l == nil && r == nil { + return true + } + if l == nil || r == nil { + return false + } + return sha256.Sum256(l.caBundle) == sha256.Sum256(r.GetCABundle()) +} + // ValidateTLSConfig reads ca bundle in the tlsSpec, supplied either inline using the CertificateAuthorityDate // or as a reference to a kubernetes secret or configmap using the CertificateAuthorityDataSource, and returns // - a condition of type TLSConfigurationValid based on the validity of the ca bundle, -// - a pem encoded ca bundle -// - a X509 cert pool with the ca bundle. +// - a CABundle - an abstraction of internal representation of CA certificate bundles. func ValidateTLSConfig( tlsSpec *TLSSpec, conditionPrefix string, namespace string, secretInformer corev1informers.SecretInformer, configMapInformer corev1informers.ConfigMapInformer, -) (*metav1.Condition, []byte, *x509.CertPool) { +) (*metav1.Condition, *CABundle) { // TODO: This func should return a struct that abstracts away the internals of how a CA bundle is held in memory // and can return the CA bundle as string PEM, []byte base64-encoded, CertPool, hash, etc, as well as compare itself // to either a different struct instance or a hash. @@ -100,13 +137,14 @@ func ValidateTLSConfig( certPool, bundle, err := getCertPool(tlsSpec, conditionPrefix, namespace, secretInformer, configMapInformer) if err != nil { - return invalidTLSCondition(err.Error()), nil, nil + return invalidTLSCondition(err.Error()), &CABundle{} } if bundle == nil { // An empty or nil CA bundle results in a valid TLS condition which indicates that no CA data was supplied. - return validTLSCondition(fmt.Sprintf("%s is valid: %s", conditionPrefix, noTLSConfigurationMessage)), nil, nil + return validTLSCondition(fmt.Sprintf("%s is valid: %s", conditionPrefix, noTLSConfigurationMessage)), nil } - return validTLSCondition(fmt.Sprintf("%s is valid: %s", conditionPrefix, loadedTLSConfigurationMessage)), bundle, certPool + return validTLSCondition(fmt.Sprintf("%s is valid: %s", conditionPrefix, loadedTLSConfigurationMessage)), + &CABundle{bundle, certPool} } // getCertPool reads the unified tlsSpec and returns an X509 cert pool with the CA data that is read either from diff --git a/internal/controller/tlsconfigutil/tls_config_util_test.go b/internal/controller/tlsconfigutil/tls_config_util_test.go index f7ba30ac3..eb626013a 100644 --- a/internal/controller/tlsconfigutil/tls_config_util_test.go +++ b/internal/controller/tlsconfigutil/tls_config_util_test.go @@ -27,18 +27,16 @@ import ( func TestValidateTLSConfig(t *testing.T) { testCA, err := certauthority.New("Test CA", 1*time.Hour) require.NoError(t, err) - bundle := testCA.Bundle() certPool := x509.NewCertPool() - require.True(t, certPool.AppendCertsFromPEM(bundle)) - base64EncodedBundle := base64.StdEncoding.EncodeToString(bundle) + require.True(t, certPool.AppendCertsFromPEM(testCA.Bundle())) + base64EncodedBundle := base64.StdEncoding.EncodeToString(testCA.Bundle()) tests := []struct { name string tlsSpec *TLSSpec namespace string k8sObjects []runtime.Object - expectedBundle []byte - expectedCertPool *x509.CertPool + expectedCABundle *CABundle expectedCondition *metav1.Condition }{ { @@ -66,8 +64,10 @@ func TestValidateTLSConfig(t *testing.T) { tlsSpec: &TLSSpec{ CertificateAuthorityData: base64EncodedBundle, }, - expectedBundle: bundle, - expectedCertPool: certPool, + expectedCABundle: &CABundle{ + caBundle: testCA.Bundle(), + caCertPool: certPool, + }, expectedCondition: &metav1.Condition{ Type: typeTLSConfigurationValid, Status: metav1.ConditionTrue, @@ -134,12 +134,14 @@ func TestValidateTLSConfig(t *testing.T) { }, Type: corev1.SecretTypeTLS, Data: map[string][]byte{ - "ca-bundle": bundle, + "ca-bundle": testCA.Bundle(), }, }, }, - expectedBundle: bundle, - expectedCertPool: certPool, + expectedCABundle: &CABundle{ + caBundle: testCA.Bundle(), + caCertPool: certPool, + }, expectedCondition: &metav1.Condition{ Type: typeTLSConfigurationValid, Status: metav1.ConditionTrue, @@ -165,12 +167,14 @@ func TestValidateTLSConfig(t *testing.T) { }, Type: corev1.SecretTypeOpaque, Data: map[string][]byte{ - "ca-bundle": bundle, + "ca-bundle": testCA.Bundle(), }, }, }, - expectedBundle: bundle, - expectedCertPool: certPool, + expectedCABundle: &CABundle{ + caBundle: testCA.Bundle(), + caCertPool: certPool, + }, expectedCondition: &metav1.Condition{ Type: typeTLSConfigurationValid, Status: metav1.ConditionTrue, @@ -196,7 +200,7 @@ func TestValidateTLSConfig(t *testing.T) { }, Type: corev1.SecretTypeBasicAuth, Data: map[string][]byte{ - "ca-bundle": bundle, + "ca-bundle": testCA.Bundle(), }, }, }, @@ -225,7 +229,7 @@ func TestValidateTLSConfig(t *testing.T) { }, Type: corev1.SecretTypeOpaque, Data: map[string][]byte{ - "wrong-key": bundle, + "wrong-key": testCA.Bundle(), }, }, }, @@ -311,7 +315,7 @@ func TestValidateTLSConfig(t *testing.T) { Namespace: "awesome-namespace", }, Data: map[string]string{ - "wrong-key": string(bundle), + "wrong-key": string(testCA.Bundle()), }, }, }, @@ -395,12 +399,14 @@ func TestValidateTLSConfig(t *testing.T) { Namespace: "awesome-namespace", }, Data: map[string]string{ - "ca-bundle": string(bundle), + "ca-bundle": string(testCA.Bundle()), }, }, }, - expectedBundle: bundle, - expectedCertPool: certPool, + expectedCABundle: &CABundle{ + caBundle: testCA.Bundle(), + caCertPool: certPool, + }, expectedCondition: &metav1.Condition{ Type: typeTLSConfigurationValid, Status: metav1.ConditionTrue, @@ -461,7 +467,7 @@ func TestValidateTLSConfig(t *testing.T) { Namespace: "awesome-namespace", }, Data: map[string]string{ - "ca-bundle": string(bundle), + "ca-bundle": string(testCA.Bundle()), }, }, }, @@ -499,11 +505,12 @@ func TestValidateTLSConfig(t *testing.T) { // which would do this same call for us. sharedInformers.WaitForCacheSync(ctx.Done()) - actualCondition, actualBundle, actualCertPool := ValidateTLSConfig(tt.tlsSpec, "spec.foo.tls", tt.namespace, secretsInformer, configMapInformer) + actualCondition, actualBundle := ValidateTLSConfig(tt.tlsSpec, "spec.foo.tls", tt.namespace, secretsInformer, configMapInformer) require.Equal(t, tt.expectedCondition, actualCondition) - require.Equal(t, tt.expectedBundle, actualBundle) - require.True(t, tt.expectedCertPool.Equal(actualCertPool), "expectedCertPool did not equal actualCertPool") + if tt.expectedCABundle != nil { + require.True(t, tt.expectedCABundle.IsEqual(actualBundle), "expectedCertPool did not equal actualCertPool") + } }) } } @@ -655,3 +662,68 @@ func TestTLSSpecForConcierge(t *testing.T) { }) } } + +func TestCABundleIsEqual(t *testing.T) { + testCA, err := certauthority.New("Test CA", 1*time.Hour) + require.NoError(t, err) + certPool := x509.NewCertPool() + require.True(t, certPool.AppendCertsFromPEM(testCA.Bundle())) + + tests := []struct { + name string + left *CABundle + right *CABundle + expected bool + }{ + { + name: "should return equal when left and right are nil", + left: nil, + right: nil, + expected: true, + }, + { + name: "should return not equal when left is nil and right is not", + left: nil, + right: &CABundle{}, + expected: false, + }, + { + name: "should return not equal when right is nil and left is not", + left: &CABundle{}, + right: nil, + expected: false, + }, + { + name: "should return equal when both left and right have same CA certificate bytes", + left: &CABundle{ + caBundle: testCA.Bundle(), + caCertPool: certPool, + }, + right: &CABundle{ + caBundle: testCA.Bundle(), + caCertPool: certPool, + }, + expected: true, + }, + { + name: "should return not equal when both left and right do not have same CA certificate bytes", + left: &CABundle{ + caBundle: testCA.Bundle(), + caCertPool: certPool, + }, + right: &CABundle{ + caBundle: []byte("something that is not a cert"), + caCertPool: nil, + }, + expected: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + actual := tt.left.IsEqual(tt.right) + require.Equal(t, tt.expected, actual) + }) + } +} From 282b949c24702ede16428c5b4467e69036bc71c5 Mon Sep 17 00:00:00 2001 From: Ashish Amarnath Date: Thu, 25 Jul 2024 19:53:27 -0700 Subject: [PATCH 55/99] update jwtcachefiller to use new tlsconfigutil.CABundle type Signed-off-by: Ashish Amarnath --- .../webhookcachefiller/webhookcachefiller.go | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go index 9c2eddd59..58f17c76c 100644 --- a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go +++ b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go @@ -6,7 +6,6 @@ package webhookcachefiller import ( "context" - "crypto/sha256" "crypto/tls" "crypto/x509" "fmt" @@ -162,8 +161,8 @@ func (c *webhookCacheFillerController) syncIndividualWebhookAuthenticator(ctx co } conditions := make([]*metav1.Condition, 0) - certPool, caBundlePEM, conditions, tlsBundleOk := c.validateTLSBundle(webhookAuthenticator.Spec.TLS, conditions) - caBundlePEMSHA256 := sha256.Sum256(caBundlePEM) // note that this will always return the same hash for nil input + caBundle, conditions, tlsBundleOk := c.validateTLSBundle(webhookAuthenticator.Spec.TLS, conditions) + caBundlePEMSHA256 := caBundle.GetCABundleHash() // Only revalidate and update the cache if the cached authenticator is different from the desired authenticator. // There is no need to repeat validations for a spec that was already successfully validated. We are making a @@ -190,7 +189,7 @@ func (c *webhookCacheFillerController) syncIndividualWebhookAuthenticator(ctx co endpointHostPort, conditions, endpointOk := c.validateEndpoint(webhookAuthenticator.Spec.Endpoint, conditions) okSoFar := tlsBundleOk && endpointOk - conditions, tlsNegotiateErr := c.validateConnection(certPool, endpointHostPort, conditions, okSoFar) + conditions, tlsNegotiateErr := c.validateConnection(caBundle.GetCertPool(), endpointHostPort, conditions, okSoFar) errs = append(errs, tlsNegotiateErr) okSoFar = okSoFar && tlsNegotiateErr == nil @@ -198,7 +197,7 @@ func (c *webhookCacheFillerController) syncIndividualWebhookAuthenticator(ctx co // Note that we use the whole URL when constructing the webhook client, // not just the host and port that we validated above. We need the path, etc. webhookAuthenticator.Spec.Endpoint, - caBundlePEM, + caBundle.GetCABundle(), conditions, okSoFar, ) @@ -243,7 +242,7 @@ func (c *webhookCacheFillerController) cacheValueAsWebhookAuthenticator(value au return webhookAuthenticator } -func (c *webhookCacheFillerController) validateTLSBundle(tlsSpec *authenticationv1alpha1.TLSSpec, conditions []*metav1.Condition) (*x509.CertPool, []byte, []*metav1.Condition, bool) { +func (c *webhookCacheFillerController) validateTLSBundle(tlsSpec *authenticationv1alpha1.TLSSpec, conditions []*metav1.Condition) (*tlsconfigutil.CABundle, []*metav1.Condition, bool) { condition, caBundle := tlsconfigutil.ValidateTLSConfig( tlsconfigutil.TLSSpecForConcierge(tlsSpec), "spec.tls", @@ -252,7 +251,7 @@ func (c *webhookCacheFillerController) validateTLSBundle(tlsSpec *authentication c.configMapInformer) conditions = append(conditions, condition) - return caBundle.GetCertPool(), caBundle.GetCABundle(), conditions, condition.Status == metav1.ConditionTrue + return caBundle, conditions, condition.Status == metav1.ConditionTrue } // newWebhookAuthenticator creates a webhook from the provided API server url and caBundle From 15d00068418105548de9dc81cabc71212aa65c1e Mon Sep 17 00:00:00 2001 From: Joshua Casey Date: Fri, 26 Jul 2024 09:15:47 -0500 Subject: [PATCH 56/99] Pull tlsconfigutil.CABundle into a separate file --- .../jwtcachefiller/jwtcachefiller.go | 5 +- .../github_upstream_watcher.go | 1 + .../controller/tlsconfigutil/ca_bundle.go | 52 ++++++++++++ .../tlsconfigutil/ca_bundle_test.go | 76 +++++++++++++++++ .../tlsconfigutil/tls_config_util.go | 42 +--------- .../tlsconfigutil/tls_config_util_test.go | 81 ++----------------- internal/upstreamldap/upstreamldap.go | 3 +- 7 files changed, 143 insertions(+), 117 deletions(-) create mode 100644 internal/controller/tlsconfigutil/ca_bundle.go create mode 100644 internal/controller/tlsconfigutil/ca_bundle_test.go diff --git a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go index 2db9bea1e..272541c53 100644 --- a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go +++ b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go @@ -222,7 +222,6 @@ func (c *jwtCacheFillerController) syncIndividualJWTAuthenticator(ctx context.Co conditions := make([]*metav1.Condition, 0) caBundle, conditions, tlsBundleOk := c.validateTLSBundle(jwtAuthenticator.Spec.TLS, conditions) - caBundlePEMSHA256 := caBundle.GetCABundleHash() // Only revalidate and update the cache if the cached authenticator is different from the desired authenticator. // There is no need to repeat validations for a spec that was already successfully validated. We are making a @@ -238,7 +237,7 @@ func (c *jwtCacheFillerController) syncIndividualJWTAuthenticator(ctx context.Co if jwtAuthenticatorFromCache != nil && reflect.DeepEqual(jwtAuthenticatorFromCache.spec, &jwtAuthenticator.Spec) && tlsBundleOk && // if there was any error while validating the CA bundle, then run remaining validations and update status - jwtAuthenticatorFromCache.caBundlePEMSHA256 == caBundlePEMSHA256 { + jwtAuthenticatorFromCache.caBundlePEMSHA256 == caBundle.GetCABundleHash() { c.log.WithValues("jwtAuthenticator", klog.KObj(jwtAuthenticator), "issuer", jwtAuthenticator.Spec.Issuer). Info("actual jwt authenticator and desired jwt authenticator are the same") // Stop, no more work to be done. This authenticator is already validated and cached. @@ -270,7 +269,7 @@ func (c *jwtCacheFillerController) syncIndividualJWTAuthenticator(ctx context.Co client, jwtAuthenticator.Spec.DeepCopy(), // deep copy to avoid caching original object keySet, - caBundlePEMSHA256, + caBundle.GetCABundleHash(), conditions, okSoFar) errs = append(errs, err) diff --git a/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher.go b/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher.go index db1301376..fcd700478 100644 --- a/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher.go +++ b/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher.go @@ -425,6 +425,7 @@ func validateHost(gitHubAPIConfig idpv1alpha1.GitHubAPIConfig) (*metav1.Conditio }, &hostPort } +// TODO: this should take in a tlsconfigutil.CABundle func (c *gitHubWatcherController) validateGitHubConnection( hostPort *endpointaddr.HostPort, caBundlePEM []byte, diff --git a/internal/controller/tlsconfigutil/ca_bundle.go b/internal/controller/tlsconfigutil/ca_bundle.go new file mode 100644 index 000000000..c2726f3f3 --- /dev/null +++ b/internal/controller/tlsconfigutil/ca_bundle.go @@ -0,0 +1,52 @@ +package tlsconfigutil + +import ( + "crypto/sha256" + "crypto/x509" +) + +// CABundle abstracts the internal representation of CA certificate bundles. +type CABundle struct { + caBundle []byte + sha256 [32]byte + certPool *x509.CertPool +} + +func NewCABundle(caBundle []byte, certPool *x509.CertPool) *CABundle { + return &CABundle{ + caBundle: caBundle, + sha256: sha256.Sum256(caBundle), + certPool: certPool, + } +} + +// GetCABundle returns the CA certificate bundle PEM bytes. +func (c *CABundle) GetCABundle() []byte { + return c.caBundle +} + +// GetCABundlePemString returns the certificate bundle PEM formatted as a string. +func (c *CABundle) GetCABundlePemString() string { + return string(c.caBundle) +} + +// GetCertPool returns a X509 cert pool with the CA certificate bundle. +func (c *CABundle) GetCertPool() *x509.CertPool { + return c.certPool +} + +// GetCABundleHash returns a sha256 sum of the CA bundle bytes. +func (c *CABundle) GetCABundleHash() [32]byte { + return sha256.Sum256(c.caBundle) // note that this will always return the same hash for nil input +} + +// IsEqual returns whether a CABundle has the same CA certificate bundle as another. +func (c *CABundle) IsEqual(other *CABundle) bool { + if c == nil && other == nil { + return true + } + if c == nil || other == nil { + return false + } + return sha256.Sum256(c.caBundle) == sha256.Sum256(other.GetCABundle()) +} diff --git a/internal/controller/tlsconfigutil/ca_bundle_test.go b/internal/controller/tlsconfigutil/ca_bundle_test.go new file mode 100644 index 000000000..1265bbf37 --- /dev/null +++ b/internal/controller/tlsconfigutil/ca_bundle_test.go @@ -0,0 +1,76 @@ +package tlsconfigutil + +import ( + "crypto/x509" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "go.pinniped.dev/internal/certauthority" +) + +func TestCABundleIsEqual(t *testing.T) { + testCA, err := certauthority.New("Test CA", 1*time.Hour) + require.NoError(t, err) + certPool := x509.NewCertPool() + require.True(t, certPool.AppendCertsFromPEM(testCA.Bundle())) + + tests := []struct { + name string + left *CABundle + right *CABundle + expected bool + }{ + { + name: "should return equal when left and right are nil", + left: nil, + right: nil, + expected: true, + }, + { + name: "should return not equal when left is nil and right is not", + left: nil, + right: &CABundle{}, + expected: false, + }, + { + name: "should return not equal when right is nil and left is not", + left: &CABundle{}, + right: nil, + expected: false, + }, + { + name: "should return equal when both left and right have same CA certificate bytes", + left: &CABundle{ + caBundle: testCA.Bundle(), + certPool: certPool, + }, + right: &CABundle{ + caBundle: testCA.Bundle(), + certPool: certPool, + }, + expected: true, + }, + { + name: "should return not equal when both left and right do not have same CA certificate bytes", + left: &CABundle{ + caBundle: testCA.Bundle(), + certPool: certPool, + }, + right: &CABundle{ + caBundle: []byte("something that is not a cert"), + certPool: nil, + }, + expected: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + actual := tt.left.IsEqual(tt.right) + require.Equal(t, tt.expected, actual) + }) + } +} diff --git a/internal/controller/tlsconfigutil/tls_config_util.go b/internal/controller/tlsconfigutil/tls_config_util.go index a26e7738e..1a3ceeaa5 100644 --- a/internal/controller/tlsconfigutil/tls_config_util.go +++ b/internal/controller/tlsconfigutil/tls_config_util.go @@ -4,7 +4,6 @@ package tlsconfigutil import ( - "crypto/sha256" "crypto/x509" "encoding/base64" "fmt" @@ -81,43 +80,6 @@ func TLSSpecForConcierge(source *authenticationv1alpha1.TLSSpec) *TLSSpec { return dest } -// CABundle abstracts the internal representation of CA certificate bundles. -type CABundle struct { - caBundle []byte - caCertPool *x509.CertPool -} - -// GetCABundle returns the CA certificate bundle PEM bytes. -func (c *CABundle) GetCABundle() []byte { - return c.caBundle -} - -// GetCABundlePemString returns the certificate bundle PEM formatted as a string. -func (c *CABundle) GetCABundlePemString() string { - return string(c.caBundle) -} - -// GetCertPool returns a X509 cert pool with the CA certificate bundle. -func (c *CABundle) GetCertPool() *x509.CertPool { - return c.caCertPool -} - -// GetCABundleHash returns a sha256 sum of the CA bundle bytes. -func (c *CABundle) GetCABundleHash() [32]byte { - return sha256.Sum256(c.caBundle) // note that this will always return the same hash for nil input -} - -// IsEqual returns whether a CABundle has the same CA certificate bundle as another. -func (l *CABundle) IsEqual(r *CABundle) bool { - if l == nil && r == nil { - return true - } - if l == nil || r == nil { - return false - } - return sha256.Sum256(l.caBundle) == sha256.Sum256(r.GetCABundle()) -} - // ValidateTLSConfig reads ca bundle in the tlsSpec, supplied either inline using the CertificateAuthorityDate // or as a reference to a kubernetes secret or configmap using the CertificateAuthorityDataSource, and returns // - a condition of type TLSConfigurationValid based on the validity of the ca bundle, @@ -137,14 +99,14 @@ func ValidateTLSConfig( certPool, bundle, err := getCertPool(tlsSpec, conditionPrefix, namespace, secretInformer, configMapInformer) if err != nil { - return invalidTLSCondition(err.Error()), &CABundle{} + return invalidTLSCondition(err.Error()), nil } if bundle == nil { // An empty or nil CA bundle results in a valid TLS condition which indicates that no CA data was supplied. return validTLSCondition(fmt.Sprintf("%s is valid: %s", conditionPrefix, noTLSConfigurationMessage)), nil } return validTLSCondition(fmt.Sprintf("%s is valid: %s", conditionPrefix, loadedTLSConfigurationMessage)), - &CABundle{bundle, certPool} + NewCABundle(bundle, certPool) } // getCertPool reads the unified tlsSpec and returns an X509 cert pool with the CA data that is read either from diff --git a/internal/controller/tlsconfigutil/tls_config_util_test.go b/internal/controller/tlsconfigutil/tls_config_util_test.go index eb626013a..9c38d7715 100644 --- a/internal/controller/tlsconfigutil/tls_config_util_test.go +++ b/internal/controller/tlsconfigutil/tls_config_util_test.go @@ -65,8 +65,8 @@ func TestValidateTLSConfig(t *testing.T) { CertificateAuthorityData: base64EncodedBundle, }, expectedCABundle: &CABundle{ - caBundle: testCA.Bundle(), - caCertPool: certPool, + caBundle: testCA.Bundle(), + certPool: certPool, }, expectedCondition: &metav1.Condition{ Type: typeTLSConfigurationValid, @@ -139,8 +139,8 @@ func TestValidateTLSConfig(t *testing.T) { }, }, expectedCABundle: &CABundle{ - caBundle: testCA.Bundle(), - caCertPool: certPool, + caBundle: testCA.Bundle(), + certPool: certPool, }, expectedCondition: &metav1.Condition{ Type: typeTLSConfigurationValid, @@ -172,8 +172,8 @@ func TestValidateTLSConfig(t *testing.T) { }, }, expectedCABundle: &CABundle{ - caBundle: testCA.Bundle(), - caCertPool: certPool, + caBundle: testCA.Bundle(), + certPool: certPool, }, expectedCondition: &metav1.Condition{ Type: typeTLSConfigurationValid, @@ -404,8 +404,8 @@ func TestValidateTLSConfig(t *testing.T) { }, }, expectedCABundle: &CABundle{ - caBundle: testCA.Bundle(), - caCertPool: certPool, + caBundle: testCA.Bundle(), + certPool: certPool, }, expectedCondition: &metav1.Condition{ Type: typeTLSConfigurationValid, @@ -662,68 +662,3 @@ func TestTLSSpecForConcierge(t *testing.T) { }) } } - -func TestCABundleIsEqual(t *testing.T) { - testCA, err := certauthority.New("Test CA", 1*time.Hour) - require.NoError(t, err) - certPool := x509.NewCertPool() - require.True(t, certPool.AppendCertsFromPEM(testCA.Bundle())) - - tests := []struct { - name string - left *CABundle - right *CABundle - expected bool - }{ - { - name: "should return equal when left and right are nil", - left: nil, - right: nil, - expected: true, - }, - { - name: "should return not equal when left is nil and right is not", - left: nil, - right: &CABundle{}, - expected: false, - }, - { - name: "should return not equal when right is nil and left is not", - left: &CABundle{}, - right: nil, - expected: false, - }, - { - name: "should return equal when both left and right have same CA certificate bytes", - left: &CABundle{ - caBundle: testCA.Bundle(), - caCertPool: certPool, - }, - right: &CABundle{ - caBundle: testCA.Bundle(), - caCertPool: certPool, - }, - expected: true, - }, - { - name: "should return not equal when both left and right do not have same CA certificate bytes", - left: &CABundle{ - caBundle: testCA.Bundle(), - caCertPool: certPool, - }, - right: &CABundle{ - caBundle: []byte("something that is not a cert"), - caCertPool: nil, - }, - expected: false, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - t.Parallel() - actual := tt.left.IsEqual(tt.right) - require.Equal(t, tt.expected, actual) - }) - } -} diff --git a/internal/upstreamldap/upstreamldap.go b/internal/upstreamldap/upstreamldap.go index 2821a4483..8828ccd9f 100644 --- a/internal/upstreamldap/upstreamldap.go +++ b/internal/upstreamldap/upstreamldap.go @@ -74,7 +74,7 @@ const ( TLS = LDAPConnectionProtocol("TLS") ) -// ProviderConfig includes all of the settings for connection and searching for users and groups in +// ProviderConfig includes all the settings for connection and searching for users and groups in // the upstream LDAP IDP. It also provides methods for testing the connection and performing logins. // The nested structs are not pointer fields to enable deep copy on function params and return values. type ProviderConfig struct { @@ -92,6 +92,7 @@ type ProviderConfig struct { ConnectionProtocol LDAPConnectionProtocol // PEM-encoded CA cert bundle to trust when connecting to the LDAP server. Can be nil. + // TODO: should this be a tlsconfigutil.CABundle? CABundle []byte // BindUsername is the username to use when performing a bind with the upstream LDAP IDP. From 0711093ccd4ae19c668a761f975682bd82762a41 Mon Sep 17 00:00:00 2001 From: Joshua Casey Date: Fri, 26 Jul 2024 09:59:32 -0500 Subject: [PATCH 57/99] Add tests for tlsconfigutil.CABundle and all callers should use the constructor --- .../controller/tlsconfigutil/ca_bundle.go | 29 ++-- .../tlsconfigutil/ca_bundle_test.go | 126 +++++++++++++----- .../tlsconfigutil/tls_config_util_test.go | 20 +-- 3 files changed, 121 insertions(+), 54 deletions(-) diff --git a/internal/controller/tlsconfigutil/ca_bundle.go b/internal/controller/tlsconfigutil/ca_bundle.go index c2726f3f3..48df83c96 100644 --- a/internal/controller/tlsconfigutil/ca_bundle.go +++ b/internal/controller/tlsconfigutil/ca_bundle.go @@ -5,6 +5,9 @@ import ( "crypto/x509" ) +var sHA256OfEmptyData = sha256.Sum256(nil) +var zeroSHA256 = [32]byte{} + // CABundle abstracts the internal representation of CA certificate bundles. type CABundle struct { caBundle []byte @@ -22,31 +25,41 @@ func NewCABundle(caBundle []byte, certPool *x509.CertPool) *CABundle { // GetCABundle returns the CA certificate bundle PEM bytes. func (c *CABundle) GetCABundle() []byte { + if c == nil { + return nil + } return c.caBundle } // GetCABundlePemString returns the certificate bundle PEM formatted as a string. func (c *CABundle) GetCABundlePemString() string { + if c == nil { + return "" + } return string(c.caBundle) } // GetCertPool returns a X509 cert pool with the CA certificate bundle. func (c *CABundle) GetCertPool() *x509.CertPool { + if c == nil { + return nil + } return c.certPool } // GetCABundleHash returns a sha256 sum of the CA bundle bytes. func (c *CABundle) GetCABundleHash() [32]byte { - return sha256.Sum256(c.caBundle) // note that this will always return the same hash for nil input + if c == nil || len(c.caBundle) < 1 { + return sHA256OfEmptyData + } + // This handles improperly initialized receivers + if c.sha256 == zeroSHA256 { + c.sha256 = sha256.Sum256(c.caBundle) + } + return c.sha256 // note that this will always return the same hash for nil input } // IsEqual returns whether a CABundle has the same CA certificate bundle as another. func (c *CABundle) IsEqual(other *CABundle) bool { - if c == nil && other == nil { - return true - } - if c == nil || other == nil { - return false - } - return sha256.Sum256(c.caBundle) == sha256.Sum256(other.GetCABundle()) + return c.GetCABundleHash() == other.GetCABundleHash() } diff --git a/internal/controller/tlsconfigutil/ca_bundle_test.go b/internal/controller/tlsconfigutil/ca_bundle_test.go index 1265bbf37..942a01ef5 100644 --- a/internal/controller/tlsconfigutil/ca_bundle_test.go +++ b/internal/controller/tlsconfigutil/ca_bundle_test.go @@ -10,6 +10,84 @@ import ( "go.pinniped.dev/internal/certauthority" ) +func TestGetCABundle(t *testing.T) { + t.Run("returns the CA bundle", func(t *testing.T) { + caBundle := NewCABundle([]byte("here are some bytes"), nil) + + require.Equal(t, []byte("here are some bytes"), caBundle.GetCABundle()) + }) + + t.Run("handles nil receiver by returning nil", func(t *testing.T) { + var nilCABundle *CABundle + var expected []byte + require.Equal(t, expected, nilCABundle.GetCABundle()) + }) +} + +func TestGetCABundlePemString(t *testing.T) { + t.Run("returns the CA bundle PEM string", func(t *testing.T) { + caBundle := NewCABundle([]byte("here is a string"), nil) + + require.Equal(t, "here is a string", caBundle.GetCABundlePemString()) + }) + + t.Run("handles nil receiver by returning empty sstring", func(t *testing.T) { + var nilCABundle *CABundle + require.Equal(t, "", nilCABundle.GetCABundlePemString()) + }) +} + +func TestGetCertPool(t *testing.T) { + t.Run("returns the cert pool", func(t *testing.T) { + aCertPool := x509.NewCertPool() + caBundle := NewCABundle(nil, aCertPool) + + require.Equal(t, aCertPool, caBundle.GetCertPool()) + }) + + t.Run("handles nil receiver by returning nil", func(t *testing.T) { + var nilCABundle *CABundle + var expected *x509.CertPool + require.Equal(t, expected, nilCABundle.GetCertPool()) + }) +} + +func TestGetCABundleHash(t *testing.T) { + sha256OfNil := [32]uint8{0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55} + + // On the command line, `echo "test" | shasum -a 256` yields "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08", + // which is 32 bytes of data encoded as 64 characters. + // https://stackoverflow.com/a/70565837 + // This is the actual binary data: + sha256OfTest := [32]byte{159, 134, 208, 129, 136, 76, 125, 101, 154, 47, 234, 160, 197, 90, 208, 21, 163, 191, 79, 27, 43, 11, 130, 44, 209, 93, 108, 21, 176, 240, 10, 8} + + t.Run("returns the SHA256", func(t *testing.T) { + caBundle := NewCABundle([]byte("test"), nil) + + require.Equal(t, sha256OfTest, caBundle.GetCABundleHash()) + }) + + t.Run("handles nil receiver by returning the hash of nil", func(t *testing.T) { + var nilCABundle *CABundle + + require.Equal(t, sha256OfNil, nilCABundle.GetCABundleHash()) + }) + + t.Run("handles improperly initialized receiver by returning the hash of nil", func(t *testing.T) { + caBundle := &CABundle{} + + require.Equal(t, sha256OfNil, caBundle.GetCABundleHash()) + }) + + t.Run("handles improperly initialized receiver by computing the hash", func(t *testing.T) { + caBundle := &CABundle{ + caBundle: []byte("test"), + } + + require.Equal(t, sha256OfTest, caBundle.GetCABundleHash()) + }) +} + func TestCABundleIsEqual(t *testing.T) { testCA, err := certauthority.New("Test CA", 1*time.Hour) require.NoError(t, err) @@ -29,39 +107,27 @@ func TestCABundleIsEqual(t *testing.T) { expected: true, }, { - name: "should return not equal when left is nil and right is not", + name: "should return equal when left is nil and right is empty", left: nil, right: &CABundle{}, - expected: false, - }, - { - name: "should return not equal when right is nil and left is not", - left: &CABundle{}, - right: nil, - expected: false, - }, - { - name: "should return equal when both left and right have same CA certificate bytes", - left: &CABundle{ - caBundle: testCA.Bundle(), - certPool: certPool, - }, - right: &CABundle{ - caBundle: testCA.Bundle(), - certPool: certPool, - }, expected: true, }, { - name: "should return not equal when both left and right do not have same CA certificate bytes", - left: &CABundle{ - caBundle: testCA.Bundle(), - certPool: certPool, - }, - right: &CABundle{ - caBundle: []byte("something that is not a cert"), - certPool: nil, - }, + name: "should return equal when right is nil and left is empty", + left: &CABundle{}, + right: nil, + expected: true, + }, + { + name: "should return equal when both left and right have same CA certificate bytes", + left: NewCABundle(testCA.Bundle(), certPool), + right: NewCABundle(testCA.Bundle(), certPool), + expected: true, + }, + { + name: "should return not equal when both left and right do not have same CA certificate bytes", + left: NewCABundle(testCA.Bundle(), certPool), + right: NewCABundle([]byte("something that is not a cert"), certPool), expected: false, }, } @@ -69,8 +135,8 @@ func TestCABundleIsEqual(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { t.Parallel() - actual := tt.left.IsEqual(tt.right) - require.Equal(t, tt.expected, actual) + require.Equal(t, tt.expected, tt.left.IsEqual(tt.right)) + require.Equal(t, tt.expected, tt.right.IsEqual(tt.left)) }) } } diff --git a/internal/controller/tlsconfigutil/tls_config_util_test.go b/internal/controller/tlsconfigutil/tls_config_util_test.go index 9c38d7715..1ffe8b729 100644 --- a/internal/controller/tlsconfigutil/tls_config_util_test.go +++ b/internal/controller/tlsconfigutil/tls_config_util_test.go @@ -64,10 +64,7 @@ func TestValidateTLSConfig(t *testing.T) { tlsSpec: &TLSSpec{ CertificateAuthorityData: base64EncodedBundle, }, - expectedCABundle: &CABundle{ - caBundle: testCA.Bundle(), - certPool: certPool, - }, + expectedCABundle: NewCABundle(testCA.Bundle(), certPool), expectedCondition: &metav1.Condition{ Type: typeTLSConfigurationValid, Status: metav1.ConditionTrue, @@ -138,10 +135,7 @@ func TestValidateTLSConfig(t *testing.T) { }, }, }, - expectedCABundle: &CABundle{ - caBundle: testCA.Bundle(), - certPool: certPool, - }, + expectedCABundle: NewCABundle(testCA.Bundle(), certPool), expectedCondition: &metav1.Condition{ Type: typeTLSConfigurationValid, Status: metav1.ConditionTrue, @@ -171,10 +165,7 @@ func TestValidateTLSConfig(t *testing.T) { }, }, }, - expectedCABundle: &CABundle{ - caBundle: testCA.Bundle(), - certPool: certPool, - }, + expectedCABundle: NewCABundle(testCA.Bundle(), certPool), expectedCondition: &metav1.Condition{ Type: typeTLSConfigurationValid, Status: metav1.ConditionTrue, @@ -403,10 +394,7 @@ func TestValidateTLSConfig(t *testing.T) { }, }, }, - expectedCABundle: &CABundle{ - caBundle: testCA.Bundle(), - certPool: certPool, - }, + expectedCABundle: NewCABundle(testCA.Bundle(), certPool), expectedCondition: &metav1.Condition{ Type: typeTLSConfigurationValid, Status: metav1.ConditionTrue, From e82cb2c7ba81b186a65633af54176cb7ce335181 Mon Sep 17 00:00:00 2001 From: Joshua Casey Date: Fri, 26 Jul 2024 10:03:43 -0500 Subject: [PATCH 58/99] Refactor tlsconfigutil.getCertPool to return a CABundle and change its name to buildCABundle --- .../tlsconfigutil/tls_config_util.go | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/internal/controller/tlsconfigutil/tls_config_util.go b/internal/controller/tlsconfigutil/tls_config_util.go index 1a3ceeaa5..ada0e2b22 100644 --- a/internal/controller/tlsconfigutil/tls_config_util.go +++ b/internal/controller/tlsconfigutil/tls_config_util.go @@ -97,40 +97,40 @@ func ValidateTLSConfig( // // TODO: There could easily be a hash type struct alias for the specific hash value (e.g. "[32]byte") with an Equality function. - certPool, bundle, err := getCertPool(tlsSpec, conditionPrefix, namespace, secretInformer, configMapInformer) + caBundle, err := buildCABundle(tlsSpec, conditionPrefix, namespace, secretInformer, configMapInformer) if err != nil { return invalidTLSCondition(err.Error()), nil } - if bundle == nil { + if len(caBundle.GetCABundle()) < 1 { // An empty or nil CA bundle results in a valid TLS condition which indicates that no CA data was supplied. return validTLSCondition(fmt.Sprintf("%s is valid: %s", conditionPrefix, noTLSConfigurationMessage)), nil } return validTLSCondition(fmt.Sprintf("%s is valid: %s", conditionPrefix, loadedTLSConfigurationMessage)), - NewCABundle(bundle, certPool) + caBundle } -// getCertPool reads the unified tlsSpec and returns an X509 cert pool with the CA data that is read either from +// buildCABundle reads the unified tlsSpec and returns an X509 cert pool with the CA data that is read either from // the inline tls.certificateAuthorityData or from a kubernetes secret or a config map as specified in the // tls.certificateAuthorityDataSource. // If the provided tlsSpec is nil, a nil CA bundle will be returned. // If the provided spec contains a CA bundle that is not properly encoded, an error will be returned. -func getCertPool( +func buildCABundle( tlsSpec *TLSSpec, conditionPrefix string, namespace string, secretInformer corev1informers.SecretInformer, configMapInformer corev1informers.ConfigMapInformer, -) (*x509.CertPool, []byte, error) { +) (*CABundle, error) { // if tlsSpec is nil, we return a nil cert pool and cert bundle. A nil error is also returned to indicate that // a nil tlsSpec is nevertheless a valid one resulting in a valid TLS condition. if tlsSpec == nil { - return nil, nil, nil + return nil, nil } // it is a configuration error to specify a ca bundle inline using the tls.certificateAuthorityDataSource field // and also specifying a kubernetes secret or a config map to serve as the source for the ca bundle. if len(tlsSpec.CertificateAuthorityData) > 0 && tlsSpec.CertificateAuthorityDataSource != nil { - return nil, nil, fmt.Errorf("%s is invalid: both tls.certificateAuthorityDataSource and tls.certificateAuthorityData provided", conditionPrefix) + return nil, fmt.Errorf("%s is invalid: both tls.certificateAuthorityDataSource and tls.certificateAuthorityData provided", conditionPrefix) } var err error @@ -150,37 +150,37 @@ func getCertPool( caBundle, err = readCABundleFromSource(tlsSpec.CertificateAuthorityDataSource, namespace, secretInformer, configMapInformer) caBundleLength = len(caBundle) if err != nil { - return nil, nil, fmt.Errorf("%s is invalid: %s", field, err.Error()) + return nil, fmt.Errorf("%s is invalid: %s", field, err.Error()) } } if len(caBundle) == 0 { - return nil, nil, nil + return nil, nil } bundleBytes := []byte(caBundle) if decodeRequired { bundleBytes, err = base64.StdEncoding.DecodeString(caBundle) if err != nil { - return nil, nil, fmt.Errorf("%s is invalid: %s", field, err.Error()) + return nil, fmt.Errorf("%s is invalid: %s", field, err.Error()) } } // try to create a cert pool with the read ca data to determine validity of the ca bundle read from the tlsSpec. - ca := x509.NewCertPool() - ok := ca.AppendCertsFromPEM(bundleBytes) + certPool := x509.NewCertPool() + ok := certPool.AppendCertsFromPEM(bundleBytes) if !ok { if decodeRequired { - return nil, nil, fmt.Errorf("%s is invalid: no base64-encoded PEM certificates found in %d bytes of data (PEM certificates must begin with \"-----BEGIN CERTIFICATE-----\")", + return nil, fmt.Errorf("%s is invalid: no base64-encoded PEM certificates found in %d bytes of data (PEM certificates must begin with \"-----BEGIN CERTIFICATE-----\")", field, caBundleLength) } namespacedName := fmt.Sprintf("%s/%s", namespace, tlsSpec.CertificateAuthorityDataSource.Name) - return nil, nil, fmt.Errorf(`%s is invalid: key %q with %d bytes of data in %s %q is not a PEM-encoded certificate (PEM certificates must begin with "-----BEGIN CERTIFICATE-----")`, + return nil, fmt.Errorf(`%s is invalid: key %q with %d bytes of data in %s %q is not a PEM-encoded certificate (PEM certificates must begin with "-----BEGIN CERTIFICATE-----")`, field, tlsSpec.CertificateAuthorityDataSource.Key, caBundleLength, strings.ToLower(tlsSpec.CertificateAuthorityDataSource.Kind), namespacedName) } - return ca, bundleBytes, nil + return NewCABundle(bundleBytes, certPool), nil } func readCABundleFromSource(source *caBundleSource, namespace string, secretInformer corev1informers.SecretInformer, configMapInformer corev1informers.ConfigMapInformer) (string, error) { From 34eff2a2f9c1d8c45c76d565809a4d3b7f3e9b75 Mon Sep 17 00:00:00 2001 From: Joshua Casey Date: Fri, 26 Jul 2024 10:27:10 -0500 Subject: [PATCH 59/99] Refactor tlsconfigutil.buildCABundle to make it more clear where the bundle is coming from --- .../tlsconfigutil/tls_config_util.go | 73 +++++++++++-------- 1 file changed, 42 insertions(+), 31 deletions(-) diff --git a/internal/controller/tlsconfigutil/tls_config_util.go b/internal/controller/tlsconfigutil/tls_config_util.go index ada0e2b22..be327535e 100644 --- a/internal/controller/tlsconfigutil/tls_config_util.go +++ b/internal/controller/tlsconfigutil/tls_config_util.go @@ -134,53 +134,64 @@ func buildCABundle( } var err error - caBundle := tlsSpec.CertificateAuthorityData - caBundleLength := len(caBundle) - field := fmt.Sprintf("%s.%s", conditionPrefix, "certificateAuthorityData") - // the ca data supplied inline in the CRDs is expected to be base64 encoded. - // However, the ca data read from kubernetes secrets or config map will not be base64 encoded. - // For kubernetes secrets, secret data read using the client-go code automatically decodes base64 encoded values. - // So a base64 decode is required only when fetching ca bundle from the tls.certificateAuthorityData field. - decodeRequired := true + var caBundleAsBytes []byte + var originalCABundleLength int + + type generateErrorForNoCertsInNonEmptyBundleFunc func() error + var generateErrorForNoCertsInNonEmptyBundle generateErrorForNoCertsInNonEmptyBundleFunc + if tlsSpec.CertificateAuthorityDataSource != nil { - decodeRequired = false + // CA data read from kubernetes secrets or config maps will not be base64 encoded. + // For kubernetes secrets, secret data read using the client-go code automatically decodes base64 encoded values. + // track the path of the field in the tlsSpec from which the CA data is sourced. // this will be used to report in the condition status in case an invalid TLS condition is encountered. - field = fmt.Sprintf("%s.%s", conditionPrefix, "certificateAuthorityDataSource") - caBundle, err = readCABundleFromSource(tlsSpec.CertificateAuthorityDataSource, namespace, secretInformer, configMapInformer) - caBundleLength = len(caBundle) + field := fmt.Sprintf("%s.%s", conditionPrefix, "certificateAuthorityDataSource") + var bundleAsString string + bundleAsString, err = readCABundleFromSource(tlsSpec.CertificateAuthorityDataSource, namespace, secretInformer, configMapInformer) if err != nil { return nil, fmt.Errorf("%s is invalid: %s", field, err.Error()) } + caBundleAsBytes = []byte(bundleAsString) + originalCABundleLength = len(bundleAsString) + + generateErrorForNoCertsInNonEmptyBundle = func() error { + namespacedName := fmt.Sprintf("%s/%s", namespace, tlsSpec.CertificateAuthorityDataSource.Name) + + return fmt.Errorf(`%s is invalid: key %q with %d bytes of data in %s %q is not a PEM-encoded certificate (PEM certificates must begin with "-----BEGIN CERTIFICATE-----")`, + field, tlsSpec.CertificateAuthorityDataSource.Key, originalCABundleLength, strings.ToLower(tlsSpec.CertificateAuthorityDataSource.Kind), namespacedName) + } + } else { + // the ca data supplied inline in the CRDs is expected to be base64 encoded. + field := fmt.Sprintf("%s.%s", conditionPrefix, "certificateAuthorityData") + var decodedBytes []byte + decodedBytes, err = base64.StdEncoding.DecodeString(tlsSpec.CertificateAuthorityData) + if err != nil { + return nil, fmt.Errorf("%s is invalid: %s", field, err.Error()) + } + + caBundleAsBytes = decodedBytes + originalCABundleLength = len(tlsSpec.CertificateAuthorityData) + + generateErrorForNoCertsInNonEmptyBundle = func() error { + return fmt.Errorf("%s is invalid: no base64-encoded PEM certificates found in %d bytes of data (PEM certificates must begin with \"-----BEGIN CERTIFICATE-----\")", + field, originalCABundleLength) + } } - if len(caBundle) == 0 { + // It is perfectly valid to have an empty CA bundle + if originalCABundleLength == 0 { return nil, nil } - bundleBytes := []byte(caBundle) - if decodeRequired { - bundleBytes, err = base64.StdEncoding.DecodeString(caBundle) - if err != nil { - return nil, fmt.Errorf("%s is invalid: %s", field, err.Error()) - } - } - // try to create a cert pool with the read ca data to determine validity of the ca bundle read from the tlsSpec. certPool := x509.NewCertPool() - ok := certPool.AppendCertsFromPEM(bundleBytes) + ok := certPool.AppendCertsFromPEM(caBundleAsBytes) if !ok { - if decodeRequired { - return nil, fmt.Errorf("%s is invalid: no base64-encoded PEM certificates found in %d bytes of data (PEM certificates must begin with \"-----BEGIN CERTIFICATE-----\")", - field, caBundleLength) - } - namespacedName := fmt.Sprintf("%s/%s", namespace, tlsSpec.CertificateAuthorityDataSource.Name) - - return nil, fmt.Errorf(`%s is invalid: key %q with %d bytes of data in %s %q is not a PEM-encoded certificate (PEM certificates must begin with "-----BEGIN CERTIFICATE-----")`, - field, tlsSpec.CertificateAuthorityDataSource.Key, caBundleLength, strings.ToLower(tlsSpec.CertificateAuthorityDataSource.Kind), namespacedName) + return nil, generateErrorForNoCertsInNonEmptyBundle() } - return NewCABundle(bundleBytes, certPool), nil + return NewCABundle(caBundleAsBytes, certPool), nil } func readCABundleFromSource(source *caBundleSource, namespace string, secretInformer corev1informers.SecretInformer, configMapInformer corev1informers.ConfigMapInformer) (string, error) { From 4cf0e46c382f99b3ce2290d4d9dcd81ad3ada49d Mon Sep 17 00:00:00 2001 From: Joshua Casey Date: Fri, 26 Jul 2024 10:46:53 -0500 Subject: [PATCH 60/99] tlsconfigutil.CABundle should generate its own certPool --- .../controller/tlsconfigutil/ca_bundle.go | 5 +- .../tlsconfigutil/ca_bundle_test.go | 63 +++++++++++++++---- .../tlsconfigutil/tls_config_util.go | 7 +-- .../tlsconfigutil/tls_config_util_test.go | 14 ++--- 4 files changed, 62 insertions(+), 27 deletions(-) diff --git a/internal/controller/tlsconfigutil/ca_bundle.go b/internal/controller/tlsconfigutil/ca_bundle.go index 48df83c96..b229fd0fd 100644 --- a/internal/controller/tlsconfigutil/ca_bundle.go +++ b/internal/controller/tlsconfigutil/ca_bundle.go @@ -15,12 +15,13 @@ type CABundle struct { certPool *x509.CertPool } -func NewCABundle(caBundle []byte, certPool *x509.CertPool) *CABundle { +func NewCABundle(caBundle []byte) (*CABundle, bool) { + certPool := x509.NewCertPool() return &CABundle{ caBundle: caBundle, sha256: sha256.Sum256(caBundle), certPool: certPool, - } + }, certPool.AppendCertsFromPEM(caBundle) } // GetCABundle returns the CA certificate bundle PEM bytes. diff --git a/internal/controller/tlsconfigutil/ca_bundle_test.go b/internal/controller/tlsconfigutil/ca_bundle_test.go index 942a01ef5..40022ddf6 100644 --- a/internal/controller/tlsconfigutil/ca_bundle_test.go +++ b/internal/controller/tlsconfigutil/ca_bundle_test.go @@ -1,6 +1,7 @@ package tlsconfigutil import ( + "crypto/sha256" "crypto/x509" "testing" "time" @@ -10,9 +11,34 @@ import ( "go.pinniped.dev/internal/certauthority" ) +func TestNewCABundle(t *testing.T) { + testCA, err := certauthority.New("Test CA", 1*time.Hour) + require.NoError(t, err) + + t.Run("generates the certPool and hash for certificate input", func(t *testing.T) { + caBundle, ok := NewCABundle(testCA.Bundle()) + require.True(t, ok) + + require.Equal(t, testCA.Bundle(), caBundle.GetCABundle()) + require.Equal(t, sha256.Sum256(testCA.Bundle()), caBundle.GetCABundleHash()) + require.Equal(t, string(testCA.Bundle()), caBundle.GetCABundlePemString()) + require.True(t, testCA.Pool().Equal(caBundle.GetCertPool()), "should be the cert pool of the testCA") + }) + + t.Run("returns false for non-certificate input", func(t *testing.T) { + caBundle, ok := NewCABundle([]byte("here are some bytes")) + require.False(t, ok) + + require.Equal(t, []byte("here are some bytes"), caBundle.GetCABundle()) + require.Equal(t, sha256.Sum256([]byte("here are some bytes")), caBundle.GetCABundleHash()) + require.Equal(t, "here are some bytes", caBundle.GetCABundlePemString()) + require.True(t, x509.NewCertPool().Equal(caBundle.GetCertPool()), "should be an empty cert pool") + }) +} + func TestGetCABundle(t *testing.T) { t.Run("returns the CA bundle", func(t *testing.T) { - caBundle := NewCABundle([]byte("here are some bytes"), nil) + caBundle, _ := NewCABundle([]byte("here are some bytes")) require.Equal(t, []byte("here are some bytes"), caBundle.GetCABundle()) }) @@ -26,7 +52,7 @@ func TestGetCABundle(t *testing.T) { func TestGetCABundlePemString(t *testing.T) { t.Run("returns the CA bundle PEM string", func(t *testing.T) { - caBundle := NewCABundle([]byte("here is a string"), nil) + caBundle, _ := NewCABundle([]byte("here is a string")) require.Equal(t, "here is a string", caBundle.GetCABundlePemString()) }) @@ -38,11 +64,10 @@ func TestGetCABundlePemString(t *testing.T) { } func TestGetCertPool(t *testing.T) { - t.Run("returns the cert pool", func(t *testing.T) { - aCertPool := x509.NewCertPool() - caBundle := NewCABundle(nil, aCertPool) + t.Run("returns the generated cert pool", func(t *testing.T) { + caBundle, _ := NewCABundle(nil) - require.Equal(t, aCertPool, caBundle.GetCertPool()) + require.Equal(t, x509.NewCertPool(), caBundle.GetCertPool()) }) t.Run("handles nil receiver by returning nil", func(t *testing.T) { @@ -62,7 +87,7 @@ func TestGetCABundleHash(t *testing.T) { sha256OfTest := [32]byte{159, 134, 208, 129, 136, 76, 125, 101, 154, 47, 234, 160, 197, 90, 208, 21, 163, 191, 79, 27, 43, 11, 130, 44, 209, 93, 108, 21, 176, 240, 10, 8} t.Run("returns the SHA256", func(t *testing.T) { - caBundle := NewCABundle([]byte("test"), nil) + caBundle, _ := NewCABundle([]byte("test")) require.Equal(t, sha256OfTest, caBundle.GetCABundleHash()) }) @@ -119,15 +144,27 @@ func TestCABundleIsEqual(t *testing.T) { expected: true, }, { - name: "should return equal when both left and right have same CA certificate bytes", - left: NewCABundle(testCA.Bundle(), certPool), - right: NewCABundle(testCA.Bundle(), certPool), + name: "should return equal when both left and right have same CA certificate bytes", + left: func() *CABundle { + caBundle, _ := NewCABundle(testCA.Bundle()) + return caBundle + }(), + right: func() *CABundle { + caBundle, _ := NewCABundle(testCA.Bundle()) + return caBundle + }(), expected: true, }, { - name: "should return not equal when both left and right do not have same CA certificate bytes", - left: NewCABundle(testCA.Bundle(), certPool), - right: NewCABundle([]byte("something that is not a cert"), certPool), + name: "should return not equal when both left and right do not have same CA certificate bytes", + left: func() *CABundle { + caBundle, _ := NewCABundle(testCA.Bundle()) + return caBundle + }(), + right: func() *CABundle { + caBundle, _ := NewCABundle([]byte("something that is not a cert")) + return caBundle + }(), expected: false, }, } diff --git a/internal/controller/tlsconfigutil/tls_config_util.go b/internal/controller/tlsconfigutil/tls_config_util.go index be327535e..275fc7114 100644 --- a/internal/controller/tlsconfigutil/tls_config_util.go +++ b/internal/controller/tlsconfigutil/tls_config_util.go @@ -4,7 +4,6 @@ package tlsconfigutil import ( - "crypto/x509" "encoding/base64" "fmt" "strings" @@ -184,14 +183,12 @@ func buildCABundle( return nil, nil } - // try to create a cert pool with the read ca data to determine validity of the ca bundle read from the tlsSpec. - certPool := x509.NewCertPool() - ok := certPool.AppendCertsFromPEM(caBundleAsBytes) + caBundle, ok := NewCABundle(caBundleAsBytes) if !ok { return nil, generateErrorForNoCertsInNonEmptyBundle() } - return NewCABundle(caBundleAsBytes, certPool), nil + return caBundle, nil } func readCABundleFromSource(source *caBundleSource, namespace string, secretInformer corev1informers.SecretInformer, configMapInformer corev1informers.ConfigMapInformer) (string, error) { diff --git a/internal/controller/tlsconfigutil/tls_config_util_test.go b/internal/controller/tlsconfigutil/tls_config_util_test.go index 1ffe8b729..4d528ec7f 100644 --- a/internal/controller/tlsconfigutil/tls_config_util_test.go +++ b/internal/controller/tlsconfigutil/tls_config_util_test.go @@ -5,7 +5,6 @@ package tlsconfigutil import ( "context" - "crypto/x509" "encoding/base64" "testing" "time" @@ -27,10 +26,11 @@ import ( func TestValidateTLSConfig(t *testing.T) { testCA, err := certauthority.New("Test CA", 1*time.Hour) require.NoError(t, err) - certPool := x509.NewCertPool() - require.True(t, certPool.AppendCertsFromPEM(testCA.Bundle())) base64EncodedBundle := base64.StdEncoding.EncodeToString(testCA.Bundle()) + testCABundle, ok := NewCABundle(testCA.Bundle()) + require.True(t, ok) + tests := []struct { name string tlsSpec *TLSSpec @@ -64,7 +64,7 @@ func TestValidateTLSConfig(t *testing.T) { tlsSpec: &TLSSpec{ CertificateAuthorityData: base64EncodedBundle, }, - expectedCABundle: NewCABundle(testCA.Bundle(), certPool), + expectedCABundle: testCABundle, expectedCondition: &metav1.Condition{ Type: typeTLSConfigurationValid, Status: metav1.ConditionTrue, @@ -135,7 +135,7 @@ func TestValidateTLSConfig(t *testing.T) { }, }, }, - expectedCABundle: NewCABundle(testCA.Bundle(), certPool), + expectedCABundle: testCABundle, expectedCondition: &metav1.Condition{ Type: typeTLSConfigurationValid, Status: metav1.ConditionTrue, @@ -165,7 +165,7 @@ func TestValidateTLSConfig(t *testing.T) { }, }, }, - expectedCABundle: NewCABundle(testCA.Bundle(), certPool), + expectedCABundle: testCABundle, expectedCondition: &metav1.Condition{ Type: typeTLSConfigurationValid, Status: metav1.ConditionTrue, @@ -394,7 +394,7 @@ func TestValidateTLSConfig(t *testing.T) { }, }, }, - expectedCABundle: NewCABundle(testCA.Bundle(), certPool), + expectedCABundle: testCABundle, expectedCondition: &metav1.Condition{ Type: typeTLSConfigurationValid, Status: metav1.ConditionTrue, From fcceeed9fa57bee4f2c269c74e49123bd5572f9a Mon Sep 17 00:00:00 2001 From: Joshua Casey Date: Fri, 26 Jul 2024 11:11:03 -0500 Subject: [PATCH 61/99] Refactor tlsconfigutil.CABundle 'getters' to not have 'get' in the name Co-authored-by: Ryan Richard --- .../jwtcachefiller/jwtcachefiller.go | 6 +-- .../webhookcachefiller/webhookcachefiller.go | 6 +-- .../github_upstream_watcher.go | 4 +- .../oidc_upstream_watcher.go | 4 +- .../upstreamwatchers/upstream_watchers.go | 2 +- .../controller/tlsconfigutil/ca_bundle.go | 18 ++++---- .../tlsconfigutil/ca_bundle_test.go | 44 +++++++++---------- .../tlsconfigutil/tls_config_util.go | 2 +- 8 files changed, 43 insertions(+), 43 deletions(-) diff --git a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go index 272541c53..241ef4fe7 100644 --- a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go +++ b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go @@ -237,7 +237,7 @@ func (c *jwtCacheFillerController) syncIndividualJWTAuthenticator(ctx context.Co if jwtAuthenticatorFromCache != nil && reflect.DeepEqual(jwtAuthenticatorFromCache.spec, &jwtAuthenticator.Spec) && tlsBundleOk && // if there was any error while validating the CA bundle, then run remaining validations and update status - jwtAuthenticatorFromCache.caBundlePEMSHA256 == caBundle.GetCABundleHash() { + jwtAuthenticatorFromCache.caBundlePEMSHA256 == caBundle.Hash() { c.log.WithValues("jwtAuthenticator", klog.KObj(jwtAuthenticator), "issuer", jwtAuthenticator.Spec.Issuer). Info("actual jwt authenticator and desired jwt authenticator are the same") // Stop, no more work to be done. This authenticator is already validated and cached. @@ -249,7 +249,7 @@ func (c *jwtCacheFillerController) syncIndividualJWTAuthenticator(ctx context.Co _, conditions, issuerOk := c.validateIssuer(jwtAuthenticator.Spec.Issuer, conditions) okSoFar := tlsBundleOk && issuerOk - client := phttp.Default(caBundle.GetCertPool()) + client := phttp.Default(caBundle.CertPool()) client.Timeout = 30 * time.Second // copied from Kube OIDC code coreOSCtx := coreosoidc.ClientContext(context.Background(), client) @@ -269,7 +269,7 @@ func (c *jwtCacheFillerController) syncIndividualJWTAuthenticator(ctx context.Co client, jwtAuthenticator.Spec.DeepCopy(), // deep copy to avoid caching original object keySet, - caBundle.GetCABundleHash(), + caBundle.Hash(), conditions, okSoFar) errs = append(errs, err) diff --git a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go index 58f17c76c..4da1c09f6 100644 --- a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go +++ b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go @@ -162,7 +162,7 @@ func (c *webhookCacheFillerController) syncIndividualWebhookAuthenticator(ctx co conditions := make([]*metav1.Condition, 0) caBundle, conditions, tlsBundleOk := c.validateTLSBundle(webhookAuthenticator.Spec.TLS, conditions) - caBundlePEMSHA256 := caBundle.GetCABundleHash() + caBundlePEMSHA256 := caBundle.Hash() // Only revalidate and update the cache if the cached authenticator is different from the desired authenticator. // There is no need to repeat validations for a spec that was already successfully validated. We are making a @@ -189,7 +189,7 @@ func (c *webhookCacheFillerController) syncIndividualWebhookAuthenticator(ctx co endpointHostPort, conditions, endpointOk := c.validateEndpoint(webhookAuthenticator.Spec.Endpoint, conditions) okSoFar := tlsBundleOk && endpointOk - conditions, tlsNegotiateErr := c.validateConnection(caBundle.GetCertPool(), endpointHostPort, conditions, okSoFar) + conditions, tlsNegotiateErr := c.validateConnection(caBundle.CertPool(), endpointHostPort, conditions, okSoFar) errs = append(errs, tlsNegotiateErr) okSoFar = okSoFar && tlsNegotiateErr == nil @@ -197,7 +197,7 @@ func (c *webhookCacheFillerController) syncIndividualWebhookAuthenticator(ctx co // Note that we use the whole URL when constructing the webhook client, // not just the host and port that we validated above. We need the path, etc. webhookAuthenticator.Spec.Endpoint, - caBundle.GetCABundle(), + caBundle.PEMBytes(), conditions, okSoFar, ) diff --git a/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher.go b/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher.go index fcd700478..47057c97b 100644 --- a/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher.go +++ b/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher.go @@ -335,8 +335,8 @@ func (c *gitHubWatcherController) validateUpstreamAndUpdateConditions(ctx contro githubConnectionCondition, hostURL, httpClient, githubConnectionErr := c.validateGitHubConnection( hostPort, - caBundle.GetCABundle(), - caBundle.GetCertPool(), + caBundle.PEMBytes(), + caBundle.CertPool(), hostCondition.Status == metav1.ConditionTrue, tlsConfigCondition.Status == metav1.ConditionTrue, ) diff --git a/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher.go b/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher.go index 10ad7ad9a..32cc333f1 100644 --- a/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher.go +++ b/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher.go @@ -359,7 +359,7 @@ func (c *oidcWatcherController) validateIssuer(ctx context.Context, upstream *id // Get the discovered provider and HTTP client from cache, if they are found in the cache. cacheKey := oidcDiscoveryCacheKey{ issuer: upstream.Spec.Issuer, - caBundleHash: caBundle.GetCABundleHash(), // note that this will always return the same hash for nil input + caBundleHash: caBundle.Hash(), // note that this will always return the same hash for nil input } if cacheEntry := c.validatorCache.getProvider(cacheKey); cacheEntry != nil { discoveredProvider = cacheEntry.provider @@ -373,7 +373,7 @@ func (c *oidcWatcherController) validateIssuer(ctx context.Context, upstream *id // If the provider does not exist in the cache, do a fresh discovery lookup and save to the cache. if discoveredProvider == nil { - httpClient = defaultClientShortTimeout(caBundle.GetCertPool()) + httpClient = defaultClientShortTimeout(caBundle.CertPool()) _, issuerURLCondition := validateHTTPSURL(upstream.Spec.Issuer, "issuer", reasonUnreachable) if issuerURLCondition != nil { diff --git a/internal/controller/supervisorconfig/upstreamwatchers/upstream_watchers.go b/internal/controller/supervisorconfig/upstreamwatchers/upstream_watchers.go index 183203941..bb55be45c 100644 --- a/internal/controller/supervisorconfig/upstreamwatchers/upstream_watchers.go +++ b/internal/controller/supervisorconfig/upstreamwatchers/upstream_watchers.go @@ -258,7 +258,7 @@ func ValidateGenericLDAP( tlsSpec := tlsconfigutil.TLSSpecForSupervisor(upstream.Spec().TLSSpec()) tlsValidCondition, caBundle := tlsconfigutil.ValidateTLSConfig(tlsSpec, "spec.tls", upstream.Namespace(), secretInformer, configMapInformer) conditions.Append(tlsValidCondition, true) - config.CABundle = caBundle.GetCABundle() + config.CABundle = caBundle.PEMBytes() var ldapConnectionValidCondition, searchBaseFoundCondition *metav1.Condition // No point in trying to connect to the server if the config was already determined to be invalid. diff --git a/internal/controller/tlsconfigutil/ca_bundle.go b/internal/controller/tlsconfigutil/ca_bundle.go index b229fd0fd..7b53a697f 100644 --- a/internal/controller/tlsconfigutil/ca_bundle.go +++ b/internal/controller/tlsconfigutil/ca_bundle.go @@ -24,32 +24,32 @@ func NewCABundle(caBundle []byte) (*CABundle, bool) { }, certPool.AppendCertsFromPEM(caBundle) } -// GetCABundle returns the CA certificate bundle PEM bytes. -func (c *CABundle) GetCABundle() []byte { +// PEMBytes returns the CA certificate bundle PEM bytes. +func (c *CABundle) PEMBytes() []byte { if c == nil { return nil } return c.caBundle } -// GetCABundlePemString returns the certificate bundle PEM formatted as a string. -func (c *CABundle) GetCABundlePemString() string { +// PEMString returns the certificate bundle PEM formatted as a string. +func (c *CABundle) PEMString() string { if c == nil { return "" } return string(c.caBundle) } -// GetCertPool returns a X509 cert pool with the CA certificate bundle. -func (c *CABundle) GetCertPool() *x509.CertPool { +// CertPool returns a X509 cert pool with the CA certificate bundle. +func (c *CABundle) CertPool() *x509.CertPool { if c == nil { return nil } return c.certPool } -// GetCABundleHash returns a sha256 sum of the CA bundle bytes. -func (c *CABundle) GetCABundleHash() [32]byte { +// Hash returns a sha256 sum of the CA bundle bytes. +func (c *CABundle) Hash() [32]byte { if c == nil || len(c.caBundle) < 1 { return sHA256OfEmptyData } @@ -62,5 +62,5 @@ func (c *CABundle) GetCABundleHash() [32]byte { // IsEqual returns whether a CABundle has the same CA certificate bundle as another. func (c *CABundle) IsEqual(other *CABundle) bool { - return c.GetCABundleHash() == other.GetCABundleHash() + return c.Hash() == other.Hash() } diff --git a/internal/controller/tlsconfigutil/ca_bundle_test.go b/internal/controller/tlsconfigutil/ca_bundle_test.go index 40022ddf6..c11e21e09 100644 --- a/internal/controller/tlsconfigutil/ca_bundle_test.go +++ b/internal/controller/tlsconfigutil/ca_bundle_test.go @@ -19,65 +19,65 @@ func TestNewCABundle(t *testing.T) { caBundle, ok := NewCABundle(testCA.Bundle()) require.True(t, ok) - require.Equal(t, testCA.Bundle(), caBundle.GetCABundle()) - require.Equal(t, sha256.Sum256(testCA.Bundle()), caBundle.GetCABundleHash()) - require.Equal(t, string(testCA.Bundle()), caBundle.GetCABundlePemString()) - require.True(t, testCA.Pool().Equal(caBundle.GetCertPool()), "should be the cert pool of the testCA") + require.Equal(t, testCA.Bundle(), caBundle.PEMBytes()) + require.Equal(t, sha256.Sum256(testCA.Bundle()), caBundle.Hash()) + require.Equal(t, string(testCA.Bundle()), caBundle.PEMString()) + require.True(t, testCA.Pool().Equal(caBundle.CertPool()), "should be the cert pool of the testCA") }) t.Run("returns false for non-certificate input", func(t *testing.T) { caBundle, ok := NewCABundle([]byte("here are some bytes")) require.False(t, ok) - require.Equal(t, []byte("here are some bytes"), caBundle.GetCABundle()) - require.Equal(t, sha256.Sum256([]byte("here are some bytes")), caBundle.GetCABundleHash()) - require.Equal(t, "here are some bytes", caBundle.GetCABundlePemString()) - require.True(t, x509.NewCertPool().Equal(caBundle.GetCertPool()), "should be an empty cert pool") + require.Equal(t, []byte("here are some bytes"), caBundle.PEMBytes()) + require.Equal(t, sha256.Sum256([]byte("here are some bytes")), caBundle.Hash()) + require.Equal(t, "here are some bytes", caBundle.PEMString()) + require.True(t, x509.NewCertPool().Equal(caBundle.CertPool()), "should be an empty cert pool") }) } -func TestGetCABundle(t *testing.T) { +func TestPEMBytes(t *testing.T) { t.Run("returns the CA bundle", func(t *testing.T) { caBundle, _ := NewCABundle([]byte("here are some bytes")) - require.Equal(t, []byte("here are some bytes"), caBundle.GetCABundle()) + require.Equal(t, []byte("here are some bytes"), caBundle.PEMBytes()) }) t.Run("handles nil receiver by returning nil", func(t *testing.T) { var nilCABundle *CABundle var expected []byte - require.Equal(t, expected, nilCABundle.GetCABundle()) + require.Equal(t, expected, nilCABundle.PEMBytes()) }) } -func TestGetCABundlePemString(t *testing.T) { +func TestPEMString(t *testing.T) { t.Run("returns the CA bundle PEM string", func(t *testing.T) { caBundle, _ := NewCABundle([]byte("here is a string")) - require.Equal(t, "here is a string", caBundle.GetCABundlePemString()) + require.Equal(t, "here is a string", caBundle.PEMString()) }) t.Run("handles nil receiver by returning empty sstring", func(t *testing.T) { var nilCABundle *CABundle - require.Equal(t, "", nilCABundle.GetCABundlePemString()) + require.Equal(t, "", nilCABundle.PEMString()) }) } -func TestGetCertPool(t *testing.T) { +func TestCertPool(t *testing.T) { t.Run("returns the generated cert pool", func(t *testing.T) { caBundle, _ := NewCABundle(nil) - require.Equal(t, x509.NewCertPool(), caBundle.GetCertPool()) + require.Equal(t, x509.NewCertPool(), caBundle.CertPool()) }) t.Run("handles nil receiver by returning nil", func(t *testing.T) { var nilCABundle *CABundle var expected *x509.CertPool - require.Equal(t, expected, nilCABundle.GetCertPool()) + require.Equal(t, expected, nilCABundle.CertPool()) }) } -func TestGetCABundleHash(t *testing.T) { +func TestHash(t *testing.T) { sha256OfNil := [32]uint8{0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55} // On the command line, `echo "test" | shasum -a 256` yields "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08", @@ -89,19 +89,19 @@ func TestGetCABundleHash(t *testing.T) { t.Run("returns the SHA256", func(t *testing.T) { caBundle, _ := NewCABundle([]byte("test")) - require.Equal(t, sha256OfTest, caBundle.GetCABundleHash()) + require.Equal(t, sha256OfTest, caBundle.Hash()) }) t.Run("handles nil receiver by returning the hash of nil", func(t *testing.T) { var nilCABundle *CABundle - require.Equal(t, sha256OfNil, nilCABundle.GetCABundleHash()) + require.Equal(t, sha256OfNil, nilCABundle.Hash()) }) t.Run("handles improperly initialized receiver by returning the hash of nil", func(t *testing.T) { caBundle := &CABundle{} - require.Equal(t, sha256OfNil, caBundle.GetCABundleHash()) + require.Equal(t, sha256OfNil, caBundle.Hash()) }) t.Run("handles improperly initialized receiver by computing the hash", func(t *testing.T) { @@ -109,7 +109,7 @@ func TestGetCABundleHash(t *testing.T) { caBundle: []byte("test"), } - require.Equal(t, sha256OfTest, caBundle.GetCABundleHash()) + require.Equal(t, sha256OfTest, caBundle.Hash()) }) } diff --git a/internal/controller/tlsconfigutil/tls_config_util.go b/internal/controller/tlsconfigutil/tls_config_util.go index 275fc7114..986cc237b 100644 --- a/internal/controller/tlsconfigutil/tls_config_util.go +++ b/internal/controller/tlsconfigutil/tls_config_util.go @@ -100,7 +100,7 @@ func ValidateTLSConfig( if err != nil { return invalidTLSCondition(err.Error()), nil } - if len(caBundle.GetCABundle()) < 1 { + if len(caBundle.PEMBytes()) < 1 { // An empty or nil CA bundle results in a valid TLS condition which indicates that no CA data was supplied. return validTLSCondition(fmt.Sprintf("%s is valid: %s", conditionPrefix, noTLSConfigurationMessage)), nil } From 99cfc4fbceaf80bd211bd3ee1ac02c07d8d70cbe Mon Sep 17 00:00:00 2001 From: Joshua Casey Date: Fri, 26 Jul 2024 11:38:42 -0500 Subject: [PATCH 62/99] Remove tlsconfigutil.CABundle.IsEqual and ensure that tlsconfigutil.NewCABundle handles nil/empty input Co-authored-by: Ryan Richard --- .../controller/tlsconfigutil/ca_bundle.go | 16 ++- .../tlsconfigutil/ca_bundle_test.go | 128 ++++++++---------- .../tlsconfigutil/tls_config_util_test.go | 4 +- 3 files changed, 67 insertions(+), 81 deletions(-) diff --git a/internal/controller/tlsconfigutil/ca_bundle.go b/internal/controller/tlsconfigutil/ca_bundle.go index 7b53a697f..7ac7dd412 100644 --- a/internal/controller/tlsconfigutil/ca_bundle.go +++ b/internal/controller/tlsconfigutil/ca_bundle.go @@ -16,12 +16,19 @@ type CABundle struct { } func NewCABundle(caBundle []byte) (*CABundle, bool) { - certPool := x509.NewCertPool() + var certPool *x509.CertPool + ok := true + + if len(caBundle) > 0 { + certPool = x509.NewCertPool() + ok = certPool.AppendCertsFromPEM(caBundle) + } + return &CABundle{ caBundle: caBundle, sha256: sha256.Sum256(caBundle), certPool: certPool, - }, certPool.AppendCertsFromPEM(caBundle) + }, ok } // PEMBytes returns the CA certificate bundle PEM bytes. @@ -59,8 +66,3 @@ func (c *CABundle) Hash() [32]byte { } return c.sha256 // note that this will always return the same hash for nil input } - -// IsEqual returns whether a CABundle has the same CA certificate bundle as another. -func (c *CABundle) IsEqual(other *CABundle) bool { - return c.Hash() == other.Hash() -} diff --git a/internal/controller/tlsconfigutil/ca_bundle_test.go b/internal/controller/tlsconfigutil/ca_bundle_test.go index c11e21e09..d49f9b156 100644 --- a/internal/controller/tlsconfigutil/ca_bundle_test.go +++ b/internal/controller/tlsconfigutil/ca_bundle_test.go @@ -43,10 +43,19 @@ func TestPEMBytes(t *testing.T) { require.Equal(t, []byte("here are some bytes"), caBundle.PEMBytes()) }) + t.Run("handles nil bundle by returning nil", func(t *testing.T) { + caBundle, _ := NewCABundle(nil) + require.Nil(t, caBundle.PEMBytes()) + }) + + t.Run("handles empty bundle by returning empty byte array", func(t *testing.T) { + caBundle, _ := NewCABundle([]byte{}) + require.Equal(t, []byte{}, caBundle.PEMBytes()) + }) + t.Run("handles nil receiver by returning nil", func(t *testing.T) { var nilCABundle *CABundle - var expected []byte - require.Equal(t, expected, nilCABundle.PEMBytes()) + require.Nil(t, nilCABundle.PEMBytes()) }) } @@ -57,23 +66,49 @@ func TestPEMString(t *testing.T) { require.Equal(t, "here is a string", caBundle.PEMString()) }) - t.Run("handles nil receiver by returning empty sstring", func(t *testing.T) { + t.Run("handles nil bundle by returning empty string", func(t *testing.T) { + caBundle, _ := NewCABundle(nil) + + require.Equal(t, "", caBundle.PEMString()) + }) + + t.Run("handles empty bundle by returning empty string", func(t *testing.T) { + caBundle, _ := NewCABundle([]byte{}) + + require.Equal(t, "", caBundle.PEMString()) + }) + + t.Run("handles nil receiver by returning empty string", func(t *testing.T) { var nilCABundle *CABundle - require.Equal(t, "", nilCABundle.PEMString()) + require.Empty(t, nilCABundle.PEMString()) }) } func TestCertPool(t *testing.T) { - t.Run("returns the generated cert pool", func(t *testing.T) { + t.Run("returns the certPool when the caBundle is valid", func(t *testing.T) { + testCA, err := certauthority.New("Test CA", 1*time.Hour) + require.NoError(t, err) + + caBundle, _ := NewCABundle(testCA.Bundle()) + + require.True(t, testCA.Pool().Equal(caBundle.CertPool())) + }) + + t.Run("returns a nil certPool when the caBundle is nil", func(t *testing.T) { caBundle, _ := NewCABundle(nil) - require.Equal(t, x509.NewCertPool(), caBundle.CertPool()) + require.Nil(t, caBundle.CertPool()) + }) + + t.Run("returns a nil certPool when the caBundle is empty", func(t *testing.T) { + caBundle, _ := NewCABundle([]byte{}) + + require.Nil(t, caBundle.CertPool()) }) t.Run("handles nil receiver by returning nil", func(t *testing.T) { var nilCABundle *CABundle - var expected *x509.CertPool - require.Equal(t, expected, nilCABundle.CertPool()) + require.Nil(t, nilCABundle.CertPool()) }) } @@ -92,6 +127,18 @@ func TestHash(t *testing.T) { require.Equal(t, sha256OfTest, caBundle.Hash()) }) + t.Run("returns the SHA256 when the PEM is nil", func(t *testing.T) { + caBundle, _ := NewCABundle(nil) + + require.Equal(t, sha256OfNil, caBundle.Hash()) + }) + + t.Run("returns the SHA256 when the PEM is empty", func(t *testing.T) { + caBundle, _ := NewCABundle([]byte{}) + + require.Equal(t, sha256OfNil, caBundle.Hash()) + }) + t.Run("handles nil receiver by returning the hash of nil", func(t *testing.T) { var nilCABundle *CABundle @@ -112,68 +159,3 @@ func TestHash(t *testing.T) { require.Equal(t, sha256OfTest, caBundle.Hash()) }) } - -func TestCABundleIsEqual(t *testing.T) { - testCA, err := certauthority.New("Test CA", 1*time.Hour) - require.NoError(t, err) - certPool := x509.NewCertPool() - require.True(t, certPool.AppendCertsFromPEM(testCA.Bundle())) - - tests := []struct { - name string - left *CABundle - right *CABundle - expected bool - }{ - { - name: "should return equal when left and right are nil", - left: nil, - right: nil, - expected: true, - }, - { - name: "should return equal when left is nil and right is empty", - left: nil, - right: &CABundle{}, - expected: true, - }, - { - name: "should return equal when right is nil and left is empty", - left: &CABundle{}, - right: nil, - expected: true, - }, - { - name: "should return equal when both left and right have same CA certificate bytes", - left: func() *CABundle { - caBundle, _ := NewCABundle(testCA.Bundle()) - return caBundle - }(), - right: func() *CABundle { - caBundle, _ := NewCABundle(testCA.Bundle()) - return caBundle - }(), - expected: true, - }, - { - name: "should return not equal when both left and right do not have same CA certificate bytes", - left: func() *CABundle { - caBundle, _ := NewCABundle(testCA.Bundle()) - return caBundle - }(), - right: func() *CABundle { - caBundle, _ := NewCABundle([]byte("something that is not a cert")) - return caBundle - }(), - expected: false, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - t.Parallel() - require.Equal(t, tt.expected, tt.left.IsEqual(tt.right)) - require.Equal(t, tt.expected, tt.right.IsEqual(tt.left)) - }) - } -} diff --git a/internal/controller/tlsconfigutil/tls_config_util_test.go b/internal/controller/tlsconfigutil/tls_config_util_test.go index 4d528ec7f..0fae9f05c 100644 --- a/internal/controller/tlsconfigutil/tls_config_util_test.go +++ b/internal/controller/tlsconfigutil/tls_config_util_test.go @@ -497,7 +497,9 @@ func TestValidateTLSConfig(t *testing.T) { require.Equal(t, tt.expectedCondition, actualCondition) if tt.expectedCABundle != nil { - require.True(t, tt.expectedCABundle.IsEqual(actualBundle), "expectedCertPool did not equal actualCertPool") + require.Equal(t, tt.expectedCABundle.Hash(), actualBundle.Hash()) + require.Equal(t, tt.expectedCABundle.PEMBytes(), actualBundle.PEMBytes()) + require.True(t, tt.expectedCABundle.CertPool().Equal(actualBundle.CertPool())) } }) } From a888083c50c97f4b37db2f9757fadd3dadcdc7dd Mon Sep 17 00:00:00 2001 From: Joshua Casey Date: Fri, 26 Jul 2024 12:38:44 -0500 Subject: [PATCH 63/99] Introduce type alias CABundleHash for the hash of a CA bundle ([32]byte) Co-authored-by: Ryan Richard Co-authored-by: Ashish Amarnath --- .../jwtcachefiller/jwtcachefiller.go | 18 ++--- .../jwtcachefiller/jwtcachefiller_test.go | 6 +- .../webhookcachefiller/webhookcachefiller.go | 14 ++-- .../webhookcachefiller_test.go | 6 +- .../active_directory_upstream_watcher_test.go | 64 +++++++-------- .../github_upstream_watcher.go | 37 ++++----- .../github_upstream_watcher_test.go | 34 ++++---- .../ldap_upstream_watcher_test.go | 44 +++++------ .../oidc_upstream_watcher.go | 4 +- .../oidc_upstream_watcher_test.go | 4 +- .../upstreamwatchers/upstream_watchers.go | 11 ++- .../controller/tlsconfigutil/ca_bundle.go | 34 +++++--- .../tlsconfigutil/ca_bundle_test.go | 79 +++++++++++-------- .../tlsconfigutil/tls_config_util.go | 6 -- internal/upstreamldap/upstreamldap.go | 3 +- 15 files changed, 185 insertions(+), 179 deletions(-) diff --git a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go index 241ef4fe7..df6fa5e3c 100644 --- a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go +++ b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go @@ -111,9 +111,9 @@ type tokenAuthenticatorCloser interface { type cachedJWTAuthenticator struct { authenticator.Token - spec *authenticationv1alpha1.JWTAuthenticatorSpec - caBundlePEMSHA256 [32]byte - cancel context.CancelFunc + spec *authenticationv1alpha1.JWTAuthenticatorSpec + caBundleHash tlsconfigutil.CABundleHash + cancel context.CancelFunc } func (c *cachedJWTAuthenticator) Close() { @@ -237,7 +237,7 @@ func (c *jwtCacheFillerController) syncIndividualJWTAuthenticator(ctx context.Co if jwtAuthenticatorFromCache != nil && reflect.DeepEqual(jwtAuthenticatorFromCache.spec, &jwtAuthenticator.Spec) && tlsBundleOk && // if there was any error while validating the CA bundle, then run remaining validations and update status - jwtAuthenticatorFromCache.caBundlePEMSHA256 == caBundle.Hash() { + jwtAuthenticatorFromCache.caBundleHash.Equal(caBundle.Hash()) { c.log.WithValues("jwtAuthenticator", klog.KObj(jwtAuthenticator), "issuer", jwtAuthenticator.Spec.Issuer). Info("actual jwt authenticator and desired jwt authenticator are the same") // Stop, no more work to be done. This authenticator is already validated and cached. @@ -562,7 +562,7 @@ func (c *jwtCacheFillerController) newCachedJWTAuthenticator( client *http.Client, spec *authenticationv1alpha1.JWTAuthenticatorSpec, keySet *coreosoidc.RemoteKeySet, - caBundlePEMSHA256 [32]byte, + caBundleHash tlsconfigutil.CABundleHash, conditions []*metav1.Condition, prereqOk bool, ) (*cachedJWTAuthenticator, []*metav1.Condition, error) { @@ -633,10 +633,10 @@ func (c *jwtCacheFillerController) newCachedJWTAuthenticator( Message: msg, }) return &cachedJWTAuthenticator{ - Token: oidcAuthenticator, - spec: spec, - caBundlePEMSHA256: caBundlePEMSHA256, - cancel: cancel, + Token: oidcAuthenticator, + spec: spec, + caBundleHash: caBundleHash, + cancel: cancel, }, conditions, nil } diff --git a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller_test.go b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller_test.go index 01489c69f..c6aaa0111 100644 --- a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller_test.go +++ b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller_test.go @@ -10,7 +10,6 @@ import ( "crypto/elliptic" "crypto/rand" "crypto/rsa" - "crypto/sha256" "crypto/x509" _ "embed" "encoding/base64" @@ -45,6 +44,7 @@ import ( conciergefake "go.pinniped.dev/generated/latest/client/concierge/clientset/versioned/fake" conciergeinformers "go.pinniped.dev/generated/latest/client/concierge/informers/externalversions" "go.pinniped.dev/internal/controller/authenticator/authncache" + "go.pinniped.dev/internal/controller/tlsconfigutil" "go.pinniped.dev/internal/controllerlib" "go.pinniped.dev/internal/crypto/ptls" "go.pinniped.dev/internal/plog" @@ -2631,8 +2631,8 @@ func newCacheValue(t *testing.T, spec authenticationv1alpha1.JWTAuthenticatorSpe }) return &cachedJWTAuthenticator{ - spec: &spec, - caBundlePEMSHA256: sha256.Sum256([]byte(caBundle)), + spec: &spec, + caBundleHash: tlsconfigutil.NewCABundleHash([]byte(caBundle)), cancel: func() { wasClosed = true }, diff --git a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go index 4da1c09f6..9a62122d3 100644 --- a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go +++ b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go @@ -61,8 +61,8 @@ const ( type cachedWebhookAuthenticator struct { authenticator.Token - spec *authenticationv1alpha1.WebhookAuthenticatorSpec - caBundlePEMSHA256 [32]byte + spec *authenticationv1alpha1.WebhookAuthenticatorSpec + caBundleHash tlsconfigutil.CABundleHash } // New instantiates a new controllerlib.Controller which will populate the provided authncache.Cache. @@ -162,8 +162,6 @@ func (c *webhookCacheFillerController) syncIndividualWebhookAuthenticator(ctx co conditions := make([]*metav1.Condition, 0) caBundle, conditions, tlsBundleOk := c.validateTLSBundle(webhookAuthenticator.Spec.TLS, conditions) - caBundlePEMSHA256 := caBundle.Hash() - // Only revalidate and update the cache if the cached authenticator is different from the desired authenticator. // There is no need to repeat validations for a spec that was already successfully validated. We are making a // design decision to avoid repeating the validation which dials the server, even though the server's TLS @@ -177,7 +175,7 @@ func (c *webhookCacheFillerController) syncIndividualWebhookAuthenticator(ctx co if webhookAuthenticatorFromCache != nil && reflect.DeepEqual(webhookAuthenticatorFromCache.spec, &webhookAuthenticator.Spec) && tlsBundleOk && // if there was any error while validating the CA bundle, then run remaining validations and update status - webhookAuthenticatorFromCache.caBundlePEMSHA256 == caBundlePEMSHA256 { + webhookAuthenticatorFromCache.caBundleHash.Equal(caBundle.Hash()) { c.log.WithValues("webhookAuthenticator", klog.KObj(webhookAuthenticator), "endpoint", webhookAuthenticator.Spec.Endpoint). Info("actual webhook authenticator and desired webhook authenticator are the same") // Stop, no more work to be done. This authenticator is already validated and cached. @@ -210,9 +208,9 @@ func (c *webhookCacheFillerController) syncIndividualWebhookAuthenticator(ctx co c.cache.Delete(cacheKey) } else { c.cache.Store(cacheKey, &cachedWebhookAuthenticator{ - Token: newWebhookAuthenticatorForCache, - spec: webhookAuthenticator.Spec.DeepCopy(), // deep copy to avoid caching original object - caBundlePEMSHA256: caBundlePEMSHA256, + Token: newWebhookAuthenticatorForCache, + spec: webhookAuthenticator.Spec.DeepCopy(), // deep copy to avoid caching original object + caBundleHash: caBundle.Hash(), }) c.log.WithValues("webhook", klog.KObj(webhookAuthenticator), "endpoint", webhookAuthenticator.Spec.Endpoint). Info("added new webhook authenticator") diff --git a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go index 5b4990201..89f2b01b0 100644 --- a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go +++ b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go @@ -6,7 +6,6 @@ package webhookcachefiller import ( "bytes" "context" - "crypto/sha256" "crypto/tls" "encoding/base64" "encoding/json" @@ -39,6 +38,7 @@ import ( conciergeinformers "go.pinniped.dev/generated/latest/client/concierge/informers/externalversions" "go.pinniped.dev/internal/certauthority" "go.pinniped.dev/internal/controller/authenticator/authncache" + "go.pinniped.dev/internal/controller/tlsconfigutil" "go.pinniped.dev/internal/controllerlib" "go.pinniped.dev/internal/crypto/ptls" "go.pinniped.dev/internal/plog" @@ -2030,8 +2030,8 @@ func newCacheValue(t *testing.T, spec authenticationv1alpha1.WebhookAuthenticato t.Helper() return &cachedWebhookAuthenticator{ - spec: &spec, - caBundlePEMSHA256: sha256.Sum256([]byte(caBundle)), + spec: &spec, + caBundleHash: tlsconfigutil.NewCABundleHash([]byte(caBundle)), } } diff --git a/internal/controller/supervisorconfig/activedirectoryupstreamwatcher/active_directory_upstream_watcher_test.go b/internal/controller/supervisorconfig/activedirectoryupstreamwatcher/active_directory_upstream_watcher_test.go index e4ff38e32..04501bd19 100644 --- a/internal/controller/supervisorconfig/activedirectoryupstreamwatcher/active_directory_upstream_watcher_test.go +++ b/internal/controller/supervisorconfig/activedirectoryupstreamwatcher/active_directory_upstream_watcher_test.go @@ -5,7 +5,6 @@ package activedirectoryupstreamwatcher import ( "context" - "crypto/sha256" "encoding/base64" "errors" "fmt" @@ -28,6 +27,7 @@ import ( supervisorinformers "go.pinniped.dev/generated/latest/client/supervisor/informers/externalversions" "go.pinniped.dev/internal/certauthority" "go.pinniped.dev/internal/controller/supervisorconfig/upstreamwatchers" + "go.pinniped.dev/internal/controller/tlsconfigutil" "go.pinniped.dev/internal/controllerlib" "go.pinniped.dev/internal/endpointaddr" "go.pinniped.dev/internal/federationdomain/dynamicupstreamprovider" @@ -237,7 +237,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { testGroupSearchNameAttrName = "test-group-name-attr" caBundleConfigMapName = "test-ca-bundle-cm" - caBundleSecretName = "test-ca-bundle-secret" + caBundleSecretName = "test-ca-bundle-secret" //nolint:gosec // this is not a credential ) testValidSecretData := map[string][]byte{"username": []byte(testBindUsername), "password": []byte(testBindPassword)} @@ -517,7 +517,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, - CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithTLS.CABundle), + CABundleHash: tlsconfigutil.NewCABundleHash(providerConfigForValidUpstreamWithTLS.CABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(activeDirectoryConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), SearchBaseFoundCondition: condPtr(withoutTime(searchBaseFoundInConfigCondition(0))), @@ -544,7 +544,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, - CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithTLS.CABundle), + CABundleHash: tlsconfigutil.NewCABundleHash(providerConfigForValidUpstreamWithTLS.CABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(activeDirectoryConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), SearchBaseFoundCondition: condPtr(withoutTime(searchBaseFoundInConfigCondition(0))), @@ -571,7 +571,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, - CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithTLS.CABundle), + CABundleHash: tlsconfigutil.NewCABundleHash(providerConfigForValidUpstreamWithTLS.CABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(activeDirectoryConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), SearchBaseFoundCondition: condPtr(withoutTime(searchBaseFoundInConfigCondition(0))), @@ -599,7 +599,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, - CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithTLS.CABundle), + CABundleHash: tlsconfigutil.NewCABundleHash(providerConfigForValidUpstreamWithTLS.CABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(activeDirectoryConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), SearchBaseFoundCondition: condPtr(withoutTime(searchBaseFoundInConfigCondition(0))), @@ -800,7 +800,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, - CABundlePEMSHA256: sha256.Sum256(nil), + CABundleHash: tlsconfigutil.NewCABundleHash(nil), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(activeDirectoryConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), SearchBaseFoundCondition: condPtr(withoutTime(searchBaseFoundInConfigCondition(0))), @@ -871,7 +871,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, - CABundlePEMSHA256: sha256.Sum256(nil), + CABundleHash: tlsconfigutil.NewCABundleHash(nil), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(activeDirectoryConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), SearchBaseFoundCondition: condPtr(withoutTime(searchBaseFoundInConfigCondition(0))), @@ -948,7 +948,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, IDPSpecGeneration: 1234, - CABundlePEMSHA256: sha256.Sum256(testCABundle), + CABundleHash: tlsconfigutil.NewCABundleHash(testCABundle), ConnectionValidCondition: &metav1.Condition{ Type: "LDAPConnectionValid", Status: "True", @@ -1083,7 +1083,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, - CABundlePEMSHA256: sha256.Sum256(nil), + CABundleHash: tlsconfigutil.NewCABundleHash(nil), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(activeDirectoryConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), SearchBaseFoundCondition: condPtr(withoutTime(searchBaseFoundInConfigCondition(0))), @@ -1136,7 +1136,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, - CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithTLS.CABundle), + CABundleHash: tlsconfigutil.NewCABundleHash(providerConfigForValidUpstreamWithTLS.CABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(activeDirectoryConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), SearchBaseFoundCondition: condPtr(withoutTime(searchBaseFoundInConfigCondition(0))), @@ -1298,7 +1298,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, - CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithTLS.CABundle), + CABundleHash: tlsconfigutil.NewCABundleHash(providerConfigForValidUpstreamWithTLS.CABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(activeDirectoryConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), SearchBaseFoundCondition: condPtr(withoutTime(searchBaseFoundInConfigCondition(0))), @@ -1319,7 +1319,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, - CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithTLS.CABundle), + CABundleHash: tlsconfigutil.NewCABundleHash(providerConfigForValidUpstreamWithTLS.CABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(activeDirectoryConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), SearchBaseFoundCondition: condPtr(withoutTime(searchBaseFoundInConfigCondition(0))), @@ -1391,7 +1391,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { BindSecretResourceVersion: "4242", LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: exampleDefaultNamingContext, - CABundlePEMSHA256: sha256.Sum256(testCABundle), + CABundleHash: tlsconfigutil.NewCABundleHash(testCABundle), GroupSearchBase: testGroupSearchBase, IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(activeDirectoryConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), @@ -1414,7 +1414,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: exampleDefaultNamingContext, GroupSearchBase: testGroupSearchBase, - CABundlePEMSHA256: sha256.Sum256(testCABundle), + CABundleHash: tlsconfigutil.NewCABundleHash(testCABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(activeDirectoryConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), SearchBaseFoundCondition: condPtr(withoutTime(searchBaseFoundInRootDSECondition(0))), @@ -1467,7 +1467,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: exampleDefaultNamingContext, GroupSearchBase: testGroupSearchBase, - CABundlePEMSHA256: sha256.Sum256(testCABundle), + CABundleHash: tlsconfigutil.NewCABundleHash(testCABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(activeDirectoryConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), SearchBaseFoundCondition: condPtr(withoutTime(searchBaseFoundInRootDSECondition(0))), @@ -1487,7 +1487,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { BindSecretResourceVersion: "4242", LDAPConnectionProtocol: upstreamldap.StartTLS, IDPSpecGeneration: 1234, - CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithStartTLS.CABundle), + CABundleHash: tlsconfigutil.NewCABundleHash(providerConfigForValidUpstreamWithStartTLS.CABundle), UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, ConnectionValidCondition: condPtr(activeDirectoryConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), @@ -1509,7 +1509,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.StartTLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, - CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithStartTLS.CABundle), + CABundleHash: tlsconfigutil.NewCABundleHash(providerConfigForValidUpstreamWithStartTLS.CABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(activeDirectoryConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), SearchBaseFoundCondition: condPtr(withoutTime(searchBaseFoundInConfigCondition(0))), @@ -1529,7 +1529,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, - CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithTLS.CABundle), + CABundleHash: tlsconfigutil.NewCABundleHash(providerConfigForValidUpstreamWithTLS.CABundle), IDPSpecGeneration: 1233, ConnectionValidCondition: condPtr(activeDirectoryConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), SearchBaseFoundCondition: condPtr(withoutTime(searchBaseFoundInConfigCondition(0))), @@ -1552,7 +1552,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, - CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithTLS.CABundle), + CABundleHash: tlsconfigutil.NewCABundleHash(providerConfigForValidUpstreamWithTLS.CABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(activeDirectoryConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), SearchBaseFoundCondition: condPtr(withoutTime(searchBaseFoundInConfigCondition(0))), @@ -1573,7 +1573,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { IDPSpecGeneration: 1234, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, - CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithTLS.CABundle), + CABundleHash: tlsconfigutil.NewCABundleHash(providerConfigForValidUpstreamWithTLS.CABundle), ConnectionValidCondition: condPtr(activeDirectoryConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), // already previously validated with version 4242 SearchBaseFoundCondition: condPtr(withoutTime(searchBaseFoundInConfigCondition(0))), }}, @@ -1594,7 +1594,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, - CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithTLS.CABundle), + CABundleHash: tlsconfigutil.NewCABundleHash(providerConfigForValidUpstreamWithTLS.CABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(activeDirectoryConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), SearchBaseFoundCondition: condPtr(withoutTime(searchBaseFoundInConfigCondition(0))), @@ -1634,7 +1634,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, - CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithTLS.CABundle), + CABundleHash: tlsconfigutil.NewCABundleHash(providerConfigForValidUpstreamWithTLS.CABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(activeDirectoryConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), SearchBaseFoundCondition: condPtr(withoutTime(searchBaseFoundInConfigCondition(0))), @@ -1654,7 +1654,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, - CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithTLS.CABundle), + CABundleHash: tlsconfigutil.NewCABundleHash(providerConfigForValidUpstreamWithTLS.CABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(activeDirectoryConnectionValidTrueConditionWithoutTimeOrGeneration("4241")), SearchBaseFoundCondition: condPtr(withoutTime(searchBaseFoundInConfigCondition(0))), @@ -1677,7 +1677,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, - CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithTLS.CABundle), + CABundleHash: tlsconfigutil.NewCABundleHash(providerConfigForValidUpstreamWithTLS.CABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(activeDirectoryConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), SearchBaseFoundCondition: condPtr(withoutTime(searchBaseFoundInConfigCondition(0))), @@ -1739,7 +1739,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, - CABundlePEMSHA256: sha256.Sum256(testCABundle), + CABundleHash: tlsconfigutil.NewCABundleHash(testCABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(activeDirectoryConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), SearchBaseFoundCondition: condPtr(withoutTime(searchBaseFoundInConfigCondition(0))), @@ -1805,7 +1805,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: exampleDefaultNamingContext, GroupSearchBase: exampleDefaultNamingContext, - CABundlePEMSHA256: sha256.Sum256(testCABundle), + CABundleHash: tlsconfigutil.NewCABundleHash(testCABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(activeDirectoryConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), SearchBaseFoundCondition: condPtr(withoutTime(searchBaseFoundInRootDSECondition(0))), @@ -1870,7 +1870,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: exampleDefaultNamingContext, GroupSearchBase: testGroupSearchBase, - CABundlePEMSHA256: sha256.Sum256(testCABundle), + CABundleHash: tlsconfigutil.NewCABundleHash(testCABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(activeDirectoryConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), SearchBaseFoundCondition: condPtr(withoutTime(searchBaseFoundInRootDSECondition(0))), @@ -1935,7 +1935,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: exampleDefaultNamingContext, - CABundlePEMSHA256: sha256.Sum256(testCABundle), + CABundleHash: tlsconfigutil.NewCABundleHash(testCABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(activeDirectoryConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), SearchBaseFoundCondition: condPtr(withoutTime(searchBaseFoundInRootDSECondition(0))), @@ -2092,7 +2092,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, - CABundlePEMSHA256: sha256.Sum256(testCABundle), + CABundleHash: tlsconfigutil.NewCABundleHash(testCABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(activeDirectoryConnectionValidTrueConditionWithoutTimeOrGeneration("4241")), SearchBaseFoundCondition: condPtr(withoutTime(searchBaseFoundInRootDSECondition(0))), @@ -2149,7 +2149,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, GroupSearchBase: exampleDefaultNamingContext, UserSearchBase: testUserSearchBase, - CABundlePEMSHA256: sha256.Sum256(testCABundle), + CABundleHash: tlsconfigutil.NewCABundleHash(testCABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(activeDirectoryConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), SearchBaseFoundCondition: condPtr(withoutTime(searchBaseFoundInRootDSECondition(0))), @@ -2220,7 +2220,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, - CABundlePEMSHA256: sha256.Sum256(testCABundle), + CABundleHash: tlsconfigutil.NewCABundleHash(testCABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(activeDirectoryConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), SearchBaseFoundCondition: condPtr(withoutTime(searchBaseFoundInConfigCondition(0))), diff --git a/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher.go b/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher.go index 47057c97b..762737235 100644 --- a/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher.go +++ b/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher.go @@ -6,9 +6,7 @@ package githubupstreamwatcher import ( "context" - "crypto/sha256" "crypto/tls" - "crypto/x509" "errors" "fmt" "net" @@ -74,8 +72,8 @@ type UpstreamGitHubIdentityProviderICache interface { } type GitHubValidatedAPICacheI interface { - MarkAsValidated(address string, caBundlePEM []byte) - IsValid(address string, caBundlePEM []byte) bool + MarkAsValidated(address string, caBundleHash tlsconfigutil.CABundleHash) + IsValid(address string, caBundleHash tlsconfigutil.CABundleHash) bool } type GitHubValidatedAPICache struct { @@ -83,24 +81,24 @@ type GitHubValidatedAPICache struct { } type GitHubValidatedAPICacheKey struct { - address string - caBundlePEMSHA256 [32]byte + address string + caBundleHash tlsconfigutil.CABundleHash } -func (g *GitHubValidatedAPICache) MarkAsValidated(address string, caBundlePEM []byte) { +func (g *GitHubValidatedAPICache) MarkAsValidated(address string, caBundleHash tlsconfigutil.CABundleHash) { key := GitHubValidatedAPICacheKey{ - address: address, - caBundlePEMSHA256: sha256.Sum256(caBundlePEM), + address: address, + caBundleHash: caBundleHash, } // Existence in the cache means it has been validated. // The TTL in the cache is not important, it's just a "really long time". g.cache.Set(key, nil, 365*24*time.Hour) } -func (g *GitHubValidatedAPICache) IsValid(address string, caBundlePEM []byte) bool { +func (g *GitHubValidatedAPICache) IsValid(address string, caBundleHash tlsconfigutil.CABundleHash) bool { key := GitHubValidatedAPICacheKey{ - address: address, - caBundlePEMSHA256: sha256.Sum256(caBundlePEM), + address: address, + caBundleHash: caBundleHash, } _, ok := g.cache.Get(key) return ok @@ -335,8 +333,7 @@ func (c *gitHubWatcherController) validateUpstreamAndUpdateConditions(ctx contro githubConnectionCondition, hostURL, httpClient, githubConnectionErr := c.validateGitHubConnection( hostPort, - caBundle.PEMBytes(), - caBundle.CertPool(), + caBundle, hostCondition.Status == metav1.ConditionTrue, tlsConfigCondition.Status == metav1.ConditionTrue, ) @@ -425,11 +422,9 @@ func validateHost(gitHubAPIConfig idpv1alpha1.GitHubAPIConfig) (*metav1.Conditio }, &hostPort } -// TODO: this should take in a tlsconfigutil.CABundle func (c *gitHubWatcherController) validateGitHubConnection( hostPort *endpointaddr.HostPort, - caBundlePEM []byte, - certPool *x509.CertPool, + caBundle *tlsconfigutil.CABundle, hostConditionOk, tlsConfigConditionOk bool, ) (*metav1.Condition, string, *http.Client, error) { if !hostConditionOk || !tlsConfigConditionOk { @@ -443,8 +438,8 @@ func (c *gitHubWatcherController) validateGitHubConnection( address := hostPort.Endpoint() - if !c.validatedCache.IsValid(address, caBundlePEM) { - conn, tlsDialErr := c.dialFunc("tcp", address, ptls.Default(certPool)) + if !c.validatedCache.IsValid(address, caBundle.Hash()) { + conn, tlsDialErr := c.dialFunc("tcp", address, ptls.Default(caBundle.CertPool())) if tlsDialErr != nil { return &metav1.Condition{ Type: GitHubConnectionValid, @@ -457,14 +452,14 @@ func (c *gitHubWatcherController) validateGitHubConnection( _ = conn.Close() } - c.validatedCache.MarkAsValidated(address, caBundlePEM) + c.validatedCache.MarkAsValidated(address, caBundle.Hash()) return &metav1.Condition{ Type: GitHubConnectionValid, Status: metav1.ConditionTrue, Reason: conditionsutil.ReasonSuccess, Message: fmt.Sprintf("spec.githubAPI.host (%q) is reachable and TLS verification succeeds", address), - }, fmt.Sprintf("https://%s", address), phttp.Default(certPool), nil + }, fmt.Sprintf("https://%s", address), phttp.Default(caBundle.CertPool()), nil } // buildDialErrorMessage standardizes DNS error messages that appear differently on different platforms, so that tests and log grepping is uniform. diff --git a/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher_test.go b/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher_test.go index 1486e4cfd..c1fb81400 100644 --- a/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher_test.go +++ b/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher_test.go @@ -6,7 +6,6 @@ package githubupstreamwatcher import ( "bytes" "context" - "crypto/sha256" "crypto/tls" "crypto/x509" "encoding/base64" @@ -40,6 +39,7 @@ import ( "go.pinniped.dev/internal/certauthority" "go.pinniped.dev/internal/controller/conditionsutil" "go.pinniped.dev/internal/controller/supervisorconfig/upstreamwatchers" + "go.pinniped.dev/internal/controller/tlsconfigutil" "go.pinniped.dev/internal/controllerlib" "go.pinniped.dev/internal/federationdomain/dynamicupstreamprovider" "go.pinniped.dev/internal/federationdomain/upstreamprovider" @@ -451,8 +451,8 @@ func TestController(t *testing.T) { }, wantValidatedCache: []GitHubValidatedAPICacheKey{ { - address: goodServerDomain, - caBundlePEMSHA256: sha256.Sum256(goodServerCA), + address: goodServerDomain, + caBundleHash: tlsconfigutil.NewCABundleHash(goodServerCA), }, }, wantResultingUpstreams: []idpv1alpha1.GitHubIdentityProvider{ @@ -513,8 +513,8 @@ func TestController(t *testing.T) { }, wantValidatedCache: []GitHubValidatedAPICacheKey{ { - address: goodServerDomain, - caBundlePEMSHA256: sha256.Sum256(goodServerCA), + address: goodServerDomain, + caBundleHash: tlsconfigutil.NewCABundleHash(goodServerCA), }, }, wantResultingUpstreams: []idpv1alpha1.GitHubIdentityProvider{ @@ -591,8 +591,8 @@ func TestController(t *testing.T) { }, wantValidatedCache: []GitHubValidatedAPICacheKey{ { - address: "github.com:443", - caBundlePEMSHA256: sha256.Sum256(goodServerCA), + address: "github.com:443", + caBundleHash: tlsconfigutil.NewCABundleHash(goodServerCA), }, }, wantResultingUpstreams: []idpv1alpha1.GitHubIdentityProvider{ @@ -665,8 +665,8 @@ func TestController(t *testing.T) { }, wantValidatedCache: []GitHubValidatedAPICacheKey{ { - address: goodServerIPv6Domain, - caBundlePEMSHA256: sha256.Sum256(goodServerIPv6CA), + address: goodServerIPv6Domain, + caBundleHash: tlsconfigutil.NewCABundleHash(goodServerIPv6CA), }, }, wantResultingUpstreams: []idpv1alpha1.GitHubIdentityProvider{ @@ -780,8 +780,8 @@ func TestController(t *testing.T) { }, wantValidatedCache: []GitHubValidatedAPICacheKey{ { - address: goodServerDomain, - caBundlePEMSHA256: sha256.Sum256(goodServerCA), + address: goodServerDomain, + caBundleHash: tlsconfigutil.NewCABundleHash(goodServerCA), }, }, wantResultingUpstreams: []idpv1alpha1.GitHubIdentityProvider{ @@ -961,8 +961,8 @@ func TestController(t *testing.T) { }, wantValidatedCache: []GitHubValidatedAPICacheKey{ { - address: goodServerDomain, - caBundlePEMSHA256: sha256.Sum256(goodServerCA), + address: goodServerDomain, + caBundleHash: tlsconfigutil.NewCABundleHash(goodServerCA), }, }, wantResultingUpstreams: []idpv1alpha1.GitHubIdentityProvider{ @@ -1058,8 +1058,8 @@ func TestController(t *testing.T) { }, preexistingValidatedCache: []GitHubValidatedAPICacheKey{ { - address: goodServerDomain, - caBundlePEMSHA256: sha256.Sum256(goodServerCA), + address: goodServerDomain, + caBundleHash: tlsconfigutil.NewCABundleHash(goodServerCA), }, }, wantResultingCache: []*upstreamgithub.ProviderConfig{ @@ -1087,8 +1087,8 @@ func TestController(t *testing.T) { }, wantValidatedCache: []GitHubValidatedAPICacheKey{ { - address: goodServerDomain, - caBundlePEMSHA256: sha256.Sum256(goodServerCA), + address: goodServerDomain, + caBundleHash: tlsconfigutil.NewCABundleHash(goodServerCA), }, }, wantResultingUpstreams: []idpv1alpha1.GitHubIdentityProvider{ diff --git a/internal/controller/supervisorconfig/ldapupstreamwatcher/ldap_upstream_watcher_test.go b/internal/controller/supervisorconfig/ldapupstreamwatcher/ldap_upstream_watcher_test.go index ed3e6f6d5..653fce090 100644 --- a/internal/controller/supervisorconfig/ldapupstreamwatcher/ldap_upstream_watcher_test.go +++ b/internal/controller/supervisorconfig/ldapupstreamwatcher/ldap_upstream_watcher_test.go @@ -5,7 +5,6 @@ package ldapupstreamwatcher import ( "context" - "crypto/sha256" "encoding/base64" "errors" "fmt" @@ -27,6 +26,7 @@ import ( supervisorinformers "go.pinniped.dev/generated/latest/client/supervisor/informers/externalversions" "go.pinniped.dev/internal/certauthority" "go.pinniped.dev/internal/controller/supervisorconfig/upstreamwatchers" + "go.pinniped.dev/internal/controller/tlsconfigutil" "go.pinniped.dev/internal/controllerlib" "go.pinniped.dev/internal/endpointaddr" "go.pinniped.dev/internal/federationdomain/dynamicupstreamprovider" @@ -236,7 +236,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { testGroupSearchNameAttrName = "test-group-name-attr" caBundleConfigMapName = "test-ca-bundle-cm" - caBundleSecretName = "test-ca-bundle-secret" + caBundleSecretName = "test-ca-bundle-secret" //nolint:gosec // this is not a credential ) testValidSecretData := map[string][]byte{"username": []byte(testBindUsername), "password": []byte(testBindPassword)} @@ -447,7 +447,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, - CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithTLS.CABundle), + CABundleHash: tlsconfigutil.NewCABundleHash(providerConfigForValidUpstreamWithTLS.CABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(ldapConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), }}, @@ -474,7 +474,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, - CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithTLS.CABundle), + CABundleHash: tlsconfigutil.NewCABundleHash(providerConfigForValidUpstreamWithTLS.CABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(ldapConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), }}, @@ -501,7 +501,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, - CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithTLS.CABundle), + CABundleHash: tlsconfigutil.NewCABundleHash(providerConfigForValidUpstreamWithTLS.CABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(ldapConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), }}, @@ -528,7 +528,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, - CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithTLS.CABundle), + CABundleHash: tlsconfigutil.NewCABundleHash(providerConfigForValidUpstreamWithTLS.CABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(ldapConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), }}, @@ -721,7 +721,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, - CABundlePEMSHA256: sha256.Sum256(nil), + CABundleHash: tlsconfigutil.NewCABundleHash(nil), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(ldapConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), }}, @@ -789,7 +789,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.StartTLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, - CABundlePEMSHA256: sha256.Sum256(testCABundle), + CABundleHash: tlsconfigutil.NewCABundleHash(testCABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: &metav1.Condition{ Type: "LDAPConnectionValid", @@ -910,7 +910,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, - CABundlePEMSHA256: sha256.Sum256(nil), + CABundleHash: tlsconfigutil.NewCABundleHash(nil), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(ldapConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), }}, @@ -962,7 +962,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, - CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithTLS.CABundle), + CABundleHash: tlsconfigutil.NewCABundleHash(providerConfigForValidUpstreamWithTLS.CABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(ldapConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), }}, @@ -1015,7 +1015,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, - CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithTLS.CABundle), + CABundleHash: tlsconfigutil.NewCABundleHash(providerConfigForValidUpstreamWithTLS.CABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(ldapConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), }}, @@ -1035,7 +1035,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, - CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithTLS.CABundle), + CABundleHash: tlsconfigutil.NewCABundleHash(providerConfigForValidUpstreamWithTLS.CABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(ldapConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), }}, @@ -1054,7 +1054,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.StartTLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, - CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithStartTLS.CABundle), + CABundleHash: tlsconfigutil.NewCABundleHash(providerConfigForValidUpstreamWithStartTLS.CABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(ldapConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), }}, @@ -1074,7 +1074,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.StartTLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, - CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithStartTLS.CABundle), + CABundleHash: tlsconfigutil.NewCABundleHash(providerConfigForValidUpstreamWithStartTLS.CABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(ldapConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), }}, @@ -1092,7 +1092,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { BindSecretResourceVersion: "4242", LDAPConnectionProtocol: upstreamldap.TLS, IDPSpecGeneration: 1233, - CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithTLS.CABundle), + CABundleHash: tlsconfigutil.NewCABundleHash(providerConfigForValidUpstreamWithTLS.CABundle), UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, }}, @@ -1114,7 +1114,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, - CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithTLS.CABundle), + CABundleHash: tlsconfigutil.NewCABundleHash(providerConfigForValidUpstreamWithTLS.CABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(ldapConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), }}, @@ -1134,7 +1134,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { IDPSpecGeneration: 1234, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, - CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithTLS.CABundle), + CABundleHash: tlsconfigutil.NewCABundleHash(providerConfigForValidUpstreamWithTLS.CABundle), ConnectionValidCondition: condPtr(ldapConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), // already previously validated with version 4242 }}, setupMocks: func(conn *mockldapconn.MockConn) { @@ -1154,7 +1154,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, - CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithTLS.CABundle), + CABundleHash: tlsconfigutil.NewCABundleHash(providerConfigForValidUpstreamWithTLS.CABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(ldapConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), }}, @@ -1193,7 +1193,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, - CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithTLS.CABundle), + CABundleHash: tlsconfigutil.NewCABundleHash(providerConfigForValidUpstreamWithTLS.CABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(ldapConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), }}, @@ -1237,7 +1237,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, - CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithTLS.CABundle), + CABundleHash: tlsconfigutil.NewCABundleHash(providerConfigForValidUpstreamWithTLS.CABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(ldapConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), }}, @@ -1276,7 +1276,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, - CABundlePEMSHA256: sha256.Sum256(providerConfigForValidUpstreamWithTLS.CABundle), + CABundleHash: tlsconfigutil.NewCABundleHash(providerConfigForValidUpstreamWithTLS.CABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(ldapConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), }}}, @@ -1338,7 +1338,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, GroupSearchBase: testGroupSearchBase, - CABundlePEMSHA256: sha256.Sum256(testCABundle), + CABundleHash: tlsconfigutil.NewCABundleHash(testCABundle), IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(ldapConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), }}, diff --git a/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher.go b/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher.go index 32cc333f1..02ef033e2 100644 --- a/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher.go +++ b/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher.go @@ -96,7 +96,7 @@ type UpstreamOIDCIdentityProviderICache interface { // oidcDiscoveryCacheKey is the type of keys in an oidcDiscoveryCache. type oidcDiscoveryCacheKey struct { issuer string - caBundleHash [32]byte + caBundleHash tlsconfigutil.CABundleHash } // oidcDiscoveryCacheValue is the type of cache entries in an oidcDiscoveryCache. @@ -359,7 +359,7 @@ func (c *oidcWatcherController) validateIssuer(ctx context.Context, upstream *id // Get the discovered provider and HTTP client from cache, if they are found in the cache. cacheKey := oidcDiscoveryCacheKey{ issuer: upstream.Spec.Issuer, - caBundleHash: caBundle.Hash(), // note that this will always return the same hash for nil input + caBundleHash: caBundle.Hash(), } if cacheEntry := c.validatorCache.getProvider(cacheKey); cacheEntry != nil { discoveredProvider = cacheEntry.provider diff --git a/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher_test.go b/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher_test.go index f63d01e28..baccac721 100644 --- a/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher_test.go +++ b/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher_test.go @@ -6,7 +6,6 @@ package oidcupstreamwatcher import ( "bytes" "context" - "crypto/sha256" "crypto/x509" "encoding/base64" "encoding/json" @@ -32,6 +31,7 @@ import ( supervisorfake "go.pinniped.dev/generated/latest/client/supervisor/clientset/versioned/fake" supervisorinformers "go.pinniped.dev/generated/latest/client/supervisor/informers/externalversions" "go.pinniped.dev/internal/certauthority" + "go.pinniped.dev/internal/controller/tlsconfigutil" "go.pinniped.dev/internal/controllerlib" "go.pinniped.dev/internal/federationdomain/dynamicupstreamprovider" "go.pinniped.dev/internal/federationdomain/upstreamprovider" @@ -1114,7 +1114,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { // without encountering any errors. cacheKey := oidcDiscoveryCacheKey{ issuer: testIssuerURL + "/this-path-does-not-exist", - caBundleHash: sha256.Sum256([]byte(testIssuerCA)), + caBundleHash: tlsconfigutil.NewCABundleHash([]byte(testIssuerCA)), } // Put it into the initial cache for this test. return map[oidcDiscoveryCacheKey]*oidcDiscoveryCacheValue{ diff --git a/internal/controller/supervisorconfig/upstreamwatchers/upstream_watchers.go b/internal/controller/supervisorconfig/upstreamwatchers/upstream_watchers.go index bb55be45c..ac1ed370e 100644 --- a/internal/controller/supervisorconfig/upstreamwatchers/upstream_watchers.go +++ b/internal/controller/supervisorconfig/upstreamwatchers/upstream_watchers.go @@ -5,7 +5,6 @@ package upstreamwatchers import ( "context" - "crypto/sha256" "fmt" "time" @@ -41,9 +40,9 @@ const ( // ValidatedSettings is the struct which is cached by the ValidatedSettingsCacheI interface. type ValidatedSettings struct { - IDPSpecGeneration int64 // which IDP spec was used during the validation - BindSecretResourceVersion string // which bind secret was used during the validation - CABundlePEMSHA256 [32]byte // hash of the CA bundle used during the validation + IDPSpecGeneration int64 // which IDP spec was used during the validation + BindSecretResourceVersion string // which bind secret was used during the validation + CABundleHash tlsconfigutil.CABundleHash // hash of the CA bundle used during the validation // Cache the setting for TLS vs StartTLS. This is always auto-discovered by probing the server. LDAPConnectionProtocol upstreamldap.LDAPConnectionProtocol @@ -285,7 +284,7 @@ func validateAndSetLDAPServerConnectivityAndSearchBase( if hasPreviousValidatedSettings && validatedSettings.UserSearchBase != "" && validatedSettings.GroupSearchBase != "" && - validatedSettings.CABundlePEMSHA256 == sha256.Sum256(config.CABundle) { + validatedSettings.CABundleHash.Equal(tlsconfigutil.NewCABundleHash(config.CABundle)) { // Found previously validated settings in the cache (which is also not missing search base fields), so use them. config.ConnectionProtocol = validatedSettings.LDAPConnectionProtocol config.UserSearch.Base = validatedSettings.UserSearchBase @@ -313,7 +312,7 @@ func validateAndSetLDAPServerConnectivityAndSearchBase( validatedSettingsCache.Set(upstream.Name(), ValidatedSettings{ IDPSpecGeneration: upstream.Generation(), BindSecretResourceVersion: currentSecretVersion, - CABundlePEMSHA256: sha256.Sum256(config.CABundle), + CABundleHash: tlsconfigutil.NewCABundleHash(config.CABundle), LDAPConnectionProtocol: config.ConnectionProtocol, UserSearchBase: config.UserSearch.Base, GroupSearchBase: config.GroupSearch.Base, diff --git a/internal/controller/tlsconfigutil/ca_bundle.go b/internal/controller/tlsconfigutil/ca_bundle.go index 7ac7dd412..e4c878cc0 100644 --- a/internal/controller/tlsconfigutil/ca_bundle.go +++ b/internal/controller/tlsconfigutil/ca_bundle.go @@ -1,3 +1,6 @@ +// Copyright 2024 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + package tlsconfigutil import ( @@ -5,13 +8,24 @@ import ( "crypto/x509" ) -var sHA256OfEmptyData = sha256.Sum256(nil) -var zeroSHA256 = [32]byte{} +type CABundleHash struct { + hash [32]byte +} + +func NewCABundleHash(bundle []byte) CABundleHash { + return CABundleHash{ + hash: sha256.Sum256(bundle), + } +} + +func (a CABundleHash) Equal(b CABundleHash) bool { + return a == b +} // CABundle abstracts the internal representation of CA certificate bundles. type CABundle struct { caBundle []byte - sha256 [32]byte + sha256 CABundleHash certPool *x509.CertPool } @@ -26,7 +40,7 @@ func NewCABundle(caBundle []byte) (*CABundle, bool) { return &CABundle{ caBundle: caBundle, - sha256: sha256.Sum256(caBundle), + sha256: NewCABundleHash(caBundle), certPool: certPool, }, ok } @@ -56,13 +70,9 @@ func (c *CABundle) CertPool() *x509.CertPool { } // Hash returns a sha256 sum of the CA bundle bytes. -func (c *CABundle) Hash() [32]byte { - if c == nil || len(c.caBundle) < 1 { - return sHA256OfEmptyData +func (c *CABundle) Hash() CABundleHash { + if c == nil { + return NewCABundleHash(nil) } - // This handles improperly initialized receivers - if c.sha256 == zeroSHA256 { - c.sha256 = sha256.Sum256(c.caBundle) - } - return c.sha256 // note that this will always return the same hash for nil input + return c.sha256 } diff --git a/internal/controller/tlsconfigutil/ca_bundle_test.go b/internal/controller/tlsconfigutil/ca_bundle_test.go index d49f9b156..c52d7d932 100644 --- a/internal/controller/tlsconfigutil/ca_bundle_test.go +++ b/internal/controller/tlsconfigutil/ca_bundle_test.go @@ -1,7 +1,9 @@ +// Copyright 2024 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + package tlsconfigutil import ( - "crypto/sha256" "crypto/x509" "testing" "time" @@ -11,6 +13,37 @@ import ( "go.pinniped.dev/internal/certauthority" ) +func TestNewCABundleHash(t *testing.T) { + sha256OfNil := CABundleHash{hash: [32]byte{0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}} + + // On the command line, `echo "test" | shasum -a 256` yields "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08", + // which is 32 bytes of data encoded as 64 characters. + // https://stackoverflow.com/a/70565837 + // This is the actual binary data: + sha256OfTest := CABundleHash{hash: [32]byte{159, 134, 208, 129, 136, 76, 125, 101, 154, 47, 234, 160, 197, 90, 208, 21, 163, 191, 79, 27, 43, 11, 130, 44, 209, 93, 108, 21, 176, 240, 10, 8}} + + t.Run("will hash the data given", func(t *testing.T) { + caBundleHash := NewCABundleHash([]byte("test")) + + require.True(t, sha256OfTest.Equal(caBundleHash)) + require.Equal(t, sha256OfTest, caBundleHash) + }) + + t.Run("will return the hash of nil input", func(t *testing.T) { + caBundleHash := NewCABundleHash(nil) + + require.True(t, sha256OfNil.Equal(caBundleHash)) + require.Equal(t, sha256OfNil, caBundleHash) + }) + + t.Run("will return the hash of empty input", func(t *testing.T) { + caBundleHash := NewCABundleHash([]byte{}) + + require.True(t, sha256OfNil.Equal(caBundleHash)) + require.Equal(t, sha256OfNil, caBundleHash) + }) +} + func TestNewCABundle(t *testing.T) { testCA, err := certauthority.New("Test CA", 1*time.Hour) require.NoError(t, err) @@ -20,7 +53,7 @@ func TestNewCABundle(t *testing.T) { require.True(t, ok) require.Equal(t, testCA.Bundle(), caBundle.PEMBytes()) - require.Equal(t, sha256.Sum256(testCA.Bundle()), caBundle.Hash()) + require.Equal(t, NewCABundleHash(testCA.Bundle()), caBundle.Hash()) require.Equal(t, string(testCA.Bundle()), caBundle.PEMString()) require.True(t, testCA.Pool().Equal(caBundle.CertPool()), "should be the cert pool of the testCA") }) @@ -30,7 +63,7 @@ func TestNewCABundle(t *testing.T) { require.False(t, ok) require.Equal(t, []byte("here are some bytes"), caBundle.PEMBytes()) - require.Equal(t, sha256.Sum256([]byte("here are some bytes")), caBundle.Hash()) + require.Equal(t, NewCABundleHash([]byte("here are some bytes")), caBundle.Hash()) require.Equal(t, "here are some bytes", caBundle.PEMString()) require.True(t, x509.NewCertPool().Equal(caBundle.CertPool()), "should be an empty cert pool") }) @@ -113,49 +146,27 @@ func TestCertPool(t *testing.T) { } func TestHash(t *testing.T) { - sha256OfNil := [32]uint8{0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55} + t.Run("returns the Hash of the given CA bundle", func(t *testing.T) { + caBundle, _ := NewCABundle([]byte("this is a CA bundle")) - // On the command line, `echo "test" | shasum -a 256` yields "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08", - // which is 32 bytes of data encoded as 64 characters. - // https://stackoverflow.com/a/70565837 - // This is the actual binary data: - sha256OfTest := [32]byte{159, 134, 208, 129, 136, 76, 125, 101, 154, 47, 234, 160, 197, 90, 208, 21, 163, 191, 79, 27, 43, 11, 130, 44, 209, 93, 108, 21, 176, 240, 10, 8} - - t.Run("returns the SHA256", func(t *testing.T) { - caBundle, _ := NewCABundle([]byte("test")) - - require.Equal(t, sha256OfTest, caBundle.Hash()) + require.True(t, NewCABundleHash([]byte("this is a CA bundle")).Equal(caBundle.Hash())) }) - t.Run("returns the SHA256 when the PEM is nil", func(t *testing.T) { + t.Run("returns the Hash of nil when the CA bundle is nil", func(t *testing.T) { caBundle, _ := NewCABundle(nil) - require.Equal(t, sha256OfNil, caBundle.Hash()) + require.True(t, NewCABundleHash(nil).Equal(caBundle.Hash())) }) - t.Run("returns the SHA256 when the PEM is empty", func(t *testing.T) { + t.Run("returns the Hash of nil when the CA bundle is empty", func(t *testing.T) { caBundle, _ := NewCABundle([]byte{}) - require.Equal(t, sha256OfNil, caBundle.Hash()) + require.True(t, NewCABundleHash(nil).Equal(caBundle.Hash())) }) - t.Run("handles nil receiver by returning the hash of nil", func(t *testing.T) { + t.Run("returns the Hash of nil when the receiver is nil", func(t *testing.T) { var nilCABundle *CABundle - require.Equal(t, sha256OfNil, nilCABundle.Hash()) - }) - - t.Run("handles improperly initialized receiver by returning the hash of nil", func(t *testing.T) { - caBundle := &CABundle{} - - require.Equal(t, sha256OfNil, caBundle.Hash()) - }) - - t.Run("handles improperly initialized receiver by computing the hash", func(t *testing.T) { - caBundle := &CABundle{ - caBundle: []byte("test"), - } - - require.Equal(t, sha256OfTest, caBundle.Hash()) + require.True(t, NewCABundleHash(nil).Equal(nilCABundle.Hash())) }) } diff --git a/internal/controller/tlsconfigutil/tls_config_util.go b/internal/controller/tlsconfigutil/tls_config_util.go index 986cc237b..42116a288 100644 --- a/internal/controller/tlsconfigutil/tls_config_util.go +++ b/internal/controller/tlsconfigutil/tls_config_util.go @@ -90,12 +90,6 @@ func ValidateTLSConfig( secretInformer corev1informers.SecretInformer, configMapInformer corev1informers.ConfigMapInformer, ) (*metav1.Condition, *CABundle) { - // TODO: This func should return a struct that abstracts away the internals of how a CA bundle is held in memory - // and can return the CA bundle as string PEM, []byte base64-encoded, CertPool, hash, etc, as well as compare itself - // to either a different struct instance or a hash. - // - // TODO: There could easily be a hash type struct alias for the specific hash value (e.g. "[32]byte") with an Equality function. - caBundle, err := buildCABundle(tlsSpec, conditionPrefix, namespace, secretInformer, configMapInformer) if err != nil { return invalidTLSCondition(err.Error()), nil diff --git a/internal/upstreamldap/upstreamldap.go b/internal/upstreamldap/upstreamldap.go index 8828ccd9f..9beee329c 100644 --- a/internal/upstreamldap/upstreamldap.go +++ b/internal/upstreamldap/upstreamldap.go @@ -92,7 +92,6 @@ type ProviderConfig struct { ConnectionProtocol LDAPConnectionProtocol // PEM-encoded CA cert bundle to trust when connecting to the LDAP server. Can be nil. - // TODO: should this be a tlsconfigutil.CABundle? CABundle []byte // BindUsername is the username to use when performing a bind with the upstream LDAP IDP. @@ -373,7 +372,7 @@ func (p *Provider) tlsConfig() (*tls.Config, error) { return ptls.DefaultLDAP(rootCAs), nil } -// GetName returns a name for this upstream provider. +// GetResourceName returns a name for this upstream provider. func (p *Provider) GetResourceName() string { return p.c.Name } From f5da4174508629dc1f3fc0f16c8b2af609780258 Mon Sep 17 00:00:00 2001 From: Ryan Richard Date: Fri, 26 Jul 2024 13:14:23 -0700 Subject: [PATCH 64/99] fix bug in jwtcachefiller caused when status update returns error Co-authored-by: Ashish Amarnath --- .../authenticator/authncache/cache.go | 25 +++- .../authenticator/authncache/cache_test.go | 37 ++++-- .../jwtcachefiller/jwtcachefiller.go | 34 ++--- .../jwtcachefiller/jwtcachefiller_test.go | 125 ++++++++++++------ .../webhookcachefiller/webhookcachefiller.go | 12 +- .../webhookcachefiller_test.go | 39 +++--- internal/mocks/mockcachevalue/generate.go | 6 + .../mocks/mockcachevalue/mockcachevalue.go | 73 ++++++++++ .../mocks/mocktokenauthenticator/generate.go | 6 - .../mocktokenauthenticator.go | 61 --------- 10 files changed, 259 insertions(+), 159 deletions(-) create mode 100644 internal/mocks/mockcachevalue/generate.go create mode 100644 internal/mocks/mockcachevalue/mockcachevalue.go delete mode 100644 internal/mocks/mocktokenauthenticator/generate.go delete mode 100644 internal/mocks/mocktokenauthenticator/mocktokenauthenticator.go diff --git a/internal/controller/authenticator/authncache/cache.go b/internal/controller/authenticator/authncache/cache.go index 9e2b15011..e62605fe3 100644 --- a/internal/controller/authenticator/authncache/cache.go +++ b/internal/controller/authenticator/authncache/cache.go @@ -36,6 +36,7 @@ type Key struct { type Value interface { authenticator.Token + Close() } // New returns an empty cache. @@ -45,21 +46,31 @@ func New() *Cache { // Get an authenticator by key. func (c *Cache) Get(key Key) Value { - res, _ := c.cache.Load(key) - if res == nil { + v, _ := c.cache.Load(key) + if v == nil { return nil } - return res.(Value) + return v.(Value) } -// Store an authenticator into the cache. +// Store an authenticator into the cache. If overwriting a value in the cache, closes the overwritten value. func (c *Cache) Store(key Key, value Value) { - c.cache.Store(key, value) + previousValue, _ := c.cache.Swap(key, value) + // Wait until after it has been overwritten in the cache to close it, to ensure that it is only closed + // after it is not available for cache reads anymore. + if previousValue != nil { + previousValue.(Value).Close() + } } -// Delete an authenticator from the cache. +// Delete an authenticator from the cache. Closes the authenticator after removing it from the cache. func (c *Cache) Delete(key Key) { - c.cache.Delete(key) + deletedValue, _ := c.cache.LoadAndDelete(key) + // Wait until after it has been removed from the cache to close it, to ensure that it is only closed + // after it is not available for cache reads anymore. + if deletedValue != nil { + deletedValue.(Value).Close() + } } // Keys currently stored in the cache. diff --git a/internal/controller/authenticator/authncache/cache_test.go b/internal/controller/authenticator/authncache/cache_test.go index 13c6de76b..68520588a 100644 --- a/internal/controller/authenticator/authncache/cache_test.go +++ b/internal/controller/authenticator/authncache/cache_test.go @@ -19,35 +19,52 @@ import ( authenticationv1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/authentication/v1alpha1" loginapi "go.pinniped.dev/generated/latest/apis/concierge/login" - "go.pinniped.dev/internal/mocks/mocktokenauthenticator" + "go.pinniped.dev/internal/mocks/mockcachevalue" ) func TestCache(t *testing.T) { t.Parallel() ctrl := gomock.NewController(t) - defer ctrl.Finish() + t.Cleanup(func() { + ctrl.Finish() + }) cache := New() require.NotNil(t, cache) key1 := Key{Name: "authenticator-one"} - mockToken1 := mocktokenauthenticator.NewMockToken(ctrl) - cache.Store(key1, mockToken1) - require.Equal(t, mockToken1, cache.Get(key1)) + mockValue1 := mockcachevalue.NewMockValue(ctrl) + require.Nil(t, cache.Get(key1)) + cache.Store(key1, mockValue1) + require.Equal(t, mockValue1, cache.Get(key1)) require.Equal(t, 1, len(cache.Keys())) key2 := Key{Name: "authenticator-two"} - mockToken2 := mocktokenauthenticator.NewMockToken(ctrl) - cache.Store(key2, mockToken2) - require.Equal(t, mockToken2, cache.Get(key2)) + mockValue2 := mockcachevalue.NewMockValue(ctrl) + cache.Store(key2, mockValue2) + require.Equal(t, mockValue2, cache.Get(key2)) require.Equal(t, 2, len(cache.Keys())) + // Assert that Close() has not been called yet, and it should be called by the end of the test. + mockValue1.EXPECT().Close().Times(1) + mockValue2.EXPECT().Close().Times(1) + for _, key := range cache.Keys() { cache.Delete(key) } require.Zero(t, len(cache.Keys())) + key3 := Key{Name: "authenticator-three"} + mockValue3 := mockcachevalue.NewMockValue(ctrl) + cache.Store(key3, mockValue3) + require.Equal(t, mockValue3, cache.Get(key3)) + require.Equal(t, 1, len(cache.Keys())) + mockValue4 := mockcachevalue.NewMockValue(ctrl) + // Assert that Close() has not been called yet, and it should be called by the end of the test. + mockValue3.EXPECT().Close().Times(1) + cache.Store(key3, mockValue4) // overwrite + // Fill the cache back up with a fixed set of keys, but inserted in shuffled order. keysInExpectedOrder := []Key{ {APIGroup: "a", Kind: "a", Name: "a"}, @@ -92,7 +109,7 @@ func TestAuthenticateTokenCredentialRequest(t *testing.T) { mockCache := func(t *testing.T, res *authenticator.Response, authenticated bool, err error) *Cache { ctrl := gomock.NewController(t) t.Cleanup(ctrl.Finish) - m := mocktokenauthenticator.NewMockToken(ctrl) + m := mockcachevalue.NewMockValue(ctrl) m.EXPECT().AuthenticateToken(audienceFreeContext{}, validRequest.Spec.Token).Return(res, authenticated, err) c := New() c.Store(validRequestKey, m) @@ -137,7 +154,7 @@ func TestAuthenticateTokenCredentialRequest(t *testing.T) { t.Run("context is cancelled", func(t *testing.T) { ctrl := gomock.NewController(t) t.Cleanup(ctrl.Finish) - m := mocktokenauthenticator.NewMockToken(ctrl) + m := mockcachevalue.NewMockValue(ctrl) m.EXPECT().AuthenticateToken(gomock.Any(), validRequest.Spec.Token).DoAndReturn( func(ctx context.Context, token string) (*authenticator.Response, bool, error) { select { diff --git a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go index df6fa5e3c..6bd3b885c 100644 --- a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go +++ b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go @@ -231,13 +231,13 @@ func (c *jwtCacheFillerController) syncIndividualJWTAuthenticator(ctx context.Co // rather than trying to show the most up-to-date status possible. These validations are for administrator // convenience at the time of a configuration change, to catch typos and blatant misconfigurations, rather // than to constantly monitor for external issues. - var jwtAuthenticatorFromCache *cachedJWTAuthenticator + var oldJWTAuthenticatorFromCache *cachedJWTAuthenticator if valueFromCache := c.cache.Get(cacheKey); valueFromCache != nil { - jwtAuthenticatorFromCache = c.cacheValueAsJWTAuthenticator(valueFromCache) - if jwtAuthenticatorFromCache != nil && - reflect.DeepEqual(jwtAuthenticatorFromCache.spec, &jwtAuthenticator.Spec) && + oldJWTAuthenticatorFromCache = c.cacheValueAsJWTAuthenticator(valueFromCache) + if oldJWTAuthenticatorFromCache != nil && + reflect.DeepEqual(oldJWTAuthenticatorFromCache.spec, &jwtAuthenticator.Spec) && tlsBundleOk && // if there was any error while validating the CA bundle, then run remaining validations and update status - jwtAuthenticatorFromCache.caBundleHash.Equal(caBundle.Hash()) { + oldJWTAuthenticatorFromCache.caBundleHash.Equal(caBundle.Hash()) { c.log.WithValues("jwtAuthenticator", klog.KObj(jwtAuthenticator), "issuer", jwtAuthenticator.Spec.Issuer). Info("actual jwt authenticator and desired jwt authenticator are the same") // Stop, no more work to be done. This authenticator is already validated and cached. @@ -274,25 +274,29 @@ func (c *jwtCacheFillerController) syncIndividualJWTAuthenticator(ctx context.Co okSoFar) errs = append(errs, err) - if conditionsutil.HadErrorCondition(conditions) { + authenticatorValid := !conditionsutil.HadErrorCondition(conditions) + + // If we calculated a failed status condition, then remove it from the cache even before we try to write + // the status, because writing the status can fail for various reasons. + if !authenticatorValid { // The authenticator was determined to be invalid. Remove it from the cache, in case it was previously // validated and cached. Do not allow an old, previously validated spec of the authenticator to continue // being used for authentication. c.cache.Delete(cacheKey) - } else { + } + + updateErr := c.updateStatus(ctx, jwtAuthenticator, conditions) + errs = append(errs, updateErr) + + // Only add this JWTAuthenticator to the cache if the status update succeeds. + // If it were in the cache after failing to update the status, then the next Sync loop would see it in the cache + // and skip trying to update its status again, which would leave its old status permanently intact. + if authenticatorValid && updateErr == nil { c.cache.Store(cacheKey, newJWTAuthenticatorForCache) c.log.WithValues("jwtAuthenticator", klog.KObj(jwtAuthenticator), "issuer", jwtAuthenticator.Spec.Issuer). Info("added new jwt authenticator") } - // In case we just overwrote or deleted the authenticator from the cache, clean up the old instance - // to avoid leaking goroutines. It's safe to call Close() on nil. We avoid calling Close() until it is - // removed from the cache, because we do not want any end-user authentications to use a closed authenticator. - jwtAuthenticatorFromCache.Close() - - err = c.updateStatus(ctx, jwtAuthenticator, conditions) - errs = append(errs, err) - // Sync loop errors: // - Should not be configuration errors. Config errors a user must correct belong on the .Status // object. The controller simply must wait for a user to correct before running again. diff --git a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller_test.go b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller_test.go index c6aaa0111..0600456f2 100644 --- a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller_test.go +++ b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller_test.go @@ -27,6 +27,7 @@ import ( "github.com/google/go-cmp/cmp" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -47,6 +48,7 @@ import ( "go.pinniped.dev/internal/controller/tlsconfigutil" "go.pinniped.dev/internal/controllerlib" "go.pinniped.dev/internal/crypto/ptls" + "go.pinniped.dev/internal/mocks/mockcachevalue" "go.pinniped.dev/internal/plog" "go.pinniped.dev/internal/testutil" "go.pinniped.dev/internal/testutil/conditionstestutil" @@ -712,7 +714,7 @@ func TestController(t *testing.T) { // Errors such as url.Parse of the issuer are not returned as they imply a user error. // Since these errors trigger a resync, we are careful only to return an error when // something can be automatically corrected on a retry (ie an error that might be networking). - wantSyncLoopErr testutil.RequireErrorStringFunc + wantSyncErr testutil.RequireErrorStringFunc wantLogs []map[string]any wantActions func() []coretesting.Action wantUsernameClaim string @@ -801,7 +803,7 @@ func TestController(t *testing.T) { Spec: *badIssuerJWKSURIJWTAuthenticatorSpec, }, }, - wantSyncLoopErr: testutil.WantExactErrorString("[" + + wantSyncErr: testutil.WantExactErrorString("[" + `error for JWTAuthenticator another-invalid-jwt-authenticator: could not parse provider jwks_uri: parse "https://.café .com/café/café/café/coffee/jwks.json": invalid character " " in host name` + ", " + `error for JWTAuthenticator invalid-jwt-authenticator: could not parse provider jwks_uri: parse "https://.café .com/café/café/café/coffee/jwks.json": invalid character " " in host name` + @@ -1181,7 +1183,6 @@ func TestController(t *testing.T) { newCacheValue(t, *otherJWTAuthenticatorSpec, string(oldCA), wantClose), ) }, - wantClose: true, jwtAuthenticators: []runtime.Object{ &authenticationv1alpha1.JWTAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -1219,6 +1220,62 @@ func TestController(t *testing.T) { } }, wantNamesOfJWTAuthenticatorsInCache: []string{"test-name"}, + wantClose: true, + }, + { + name: "Sync: previously cached JWTAuthenticator gets new spec fields, but status update fails: loop will leave it in the cache and avoid calling close", + cache: func(t *testing.T, cache *authncache.Cache, wantClose bool) { + oldCA, err := base64.StdEncoding.DecodeString(otherJWTAuthenticatorSpec.TLS.CertificateAuthorityData) + require.NoError(t, err) + cache.Store( + authncache.Key{ + Name: "test-name", + Kind: "JWTAuthenticator", + APIGroup: authenticationv1alpha1.SchemeGroupVersion.Group, + }, + newCacheValue(t, *otherJWTAuthenticatorSpec, string(oldCA), wantClose), + ) + }, + configClient: func(client *conciergefake.Clientset) { + client.PrependReactor( + "update", + "jwtauthenticators", + func(action coretesting.Action) (handled bool, ret runtime.Object, err error) { + return true, nil, errors.New("some update error") + }, + ) + }, + jwtAuthenticators: []runtime.Object{ + &authenticationv1alpha1.JWTAuthenticator{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-name", + }, + Spec: *someJWTAuthenticatorSpec, + }, + }, + wantLogs: []map[string]any{}, + wantActions: func() []coretesting.Action { + updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-name", + }, + Spec: *someJWTAuthenticatorSpec, + Status: authenticationv1alpha1.JWTAuthenticatorStatus{ + Conditions: allHappyConditionsSuccess(goodIssuer, frozenMetav1Now, 0), + Phase: "Ready", + }, + }) + updateStatusAction.Subresource = "status" + return []coretesting.Action{ + coretesting.NewListAction(jwtAuthenticatorsGVR, jwtAUthenticatorGVK, "", metav1.ListOptions{}), + coretesting.NewWatchAction(jwtAuthenticatorsGVR, "", metav1.ListOptions{}), + updateStatusAction, + } + }, + skipTestingCachedAuthenticator: true, + wantSyncErr: testutil.WantExactErrorString("error for JWTAuthenticator test-name: some update error"), + wantNamesOfJWTAuthenticatorsInCache: []string{"test-name"}, // keeps the old entry in the cache + wantClose: false, }, { name: "Sync: JWTAuthenticator with external and changed CA bundle: loop will close previous instance of JWTAuthenticator and complete successfully and update status conditions", @@ -1232,7 +1289,6 @@ func TestController(t *testing.T) { newCacheValue(t, *someJWTAuthenticatorSpecWithCAInSecret, "some-stale-ca-bundle-pem-content-from-secret", wantClose), ) }, - wantClose: true, jwtAuthenticators: []runtime.Object{ &authenticationv1alpha1.JWTAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -1272,6 +1328,7 @@ func TestController(t *testing.T) { updateStatusAction, } }, + wantClose: true, wantNamesOfJWTAuthenticatorsInCache: []string{"test-name"}, }, { @@ -1288,7 +1345,6 @@ func TestController(t *testing.T) { newCacheValue(t, *someJWTAuthenticatorSpec, string(oldCA), wantClose), ) }, - wantClose: false, jwtAuthenticators: []runtime.Object{ &authenticationv1alpha1.JWTAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -1313,6 +1369,7 @@ func TestController(t *testing.T) { coretesting.NewWatchAction(jwtAuthenticatorsGVR, "", metav1.ListOptions{}), } }, + wantClose: false, wantNamesOfJWTAuthenticatorsInCache: []string{"test-name"}, // skip the tests because the authenticator pre-loaded into the cache is the mock version that was added above skipTestingCachedAuthenticator: true, @@ -1320,6 +1377,12 @@ func TestController(t *testing.T) { { name: "Sync: authenticator update when cached authenticator is the wrong data type, which should never really happen: loop will complete successfully and update status conditions", cache: func(t *testing.T, cache *authncache.Cache, wantClose bool) { + ctrl := gomock.NewController(t) + t.Cleanup(func() { + ctrl.Finish() + }) + mockCacheValue := mockcachevalue.NewMockValue(ctrl) + mockCacheValue.EXPECT().Close().Times(1) cache.Store( authncache.Key{ Name: "test-name", @@ -1329,7 +1392,7 @@ func TestController(t *testing.T) { // Only entries of type cachedJWTAuthenticator are ever put into the cache, so this should never really happen. // This test is to provide coverage on the production code which reads from the cache and casts those entries to // the appropriate data type. - struct{ authenticator.Token }{}, + mockCacheValue, ) }, jwtAuthenticators: []runtime.Object{ @@ -1346,7 +1409,7 @@ func TestController(t *testing.T) { "timestamp": "2099-08-08T13:57:36.123456Z", "logger": "jwtcachefiller-controller", "message": "wrong JWT authenticator type in cache", - "actualType": "struct { authenticator.Token }", + "actualType": "*mockcachevalue.MockValue", }, { "level": "info", @@ -1419,7 +1482,7 @@ func TestController(t *testing.T) { }, // no explicit logs, this is an issue of config, the user must provide TLS config for the // custom cert provided for this server. - wantSyncLoopErr: testutil.WantSprintfErrorString(`error for JWTAuthenticator test-name: could not perform oidc discovery on provider issuer: Get "%s/.well-known/openid-configuration": tls: failed to verify certificate: x509: certificate signed by unknown authority`, goodIssuer), + wantSyncErr: testutil.WantSprintfErrorString(`error for JWTAuthenticator test-name: could not perform oidc discovery on provider issuer: Get "%s/.well-known/openid-configuration": tls: failed to verify certificate: x509: certificate signed by unknown authority`, goodIssuer), }, { name: "validateTLS: JWTAuthenticator with invalid CA: loop will fail, will write failed and unknown status conditions, but will not enqueue a resync due to user config error", @@ -1461,7 +1524,7 @@ func TestController(t *testing.T) { }, }, { - name: "previously valid cached authenticator (which did not specify a CA bundle) changes and becomes invalid due to any problem with the CA bundle: loop will fail sync, will write failed and unknown status conditions, and will remove authenticator from cache", + name: "previously valid cached authenticator (which did not specify a CA bundle) changes and becomes invalid due to any problem with the CA bundle: loop will fail sync, will write failed and unknown status conditions, and will remove authenticator from cache and close it", cache: func(t *testing.T, cache *authncache.Cache, wantClose bool) { cache.Store( authncache.Key{ @@ -1825,7 +1888,7 @@ func TestController(t *testing.T) { updateStatusAction, } }, - wantSyncLoopErr: testutil.WantExactErrorString(`error for JWTAuthenticator test-name: could not perform oidc discovery on provider issuer: Get "` + goodIssuer + `/foo/bar/baz/shizzle/.well-known/openid-configuration": tls: failed to verify certificate: x509: certificate signed by unknown authority`), + wantSyncErr: testutil.WantExactErrorString(`error for JWTAuthenticator test-name: could not perform oidc discovery on provider issuer: Get "` + goodIssuer + `/foo/bar/baz/shizzle/.well-known/openid-configuration": tls: failed to verify certificate: x509: certificate signed by unknown authority`), }, { name: "validateProviderDiscovery: excessively long errors truncated: loop will fail sync, will write failed and unknown conditions, and will enqueue new sync", @@ -1875,7 +1938,7 @@ func TestController(t *testing.T) { } }, // not currently truncating the logged err - wantSyncLoopErr: testutil.WantExactErrorString("error for JWTAuthenticator test-name: could not perform oidc discovery on provider issuer: 404 Not Found: \n\t\t \t404 not found page\n\t\t\tlots of text that is at least 300 characters long 0123456789 abcdefghijklmnopqrstuvwxyz lots of text that is at least 300 characters long 0123456789 abcdefghijklmnopqrstuvwxyz lots of text that is at least 300 characters long 0123456789 abcdefghijklmnopqrstuvwxyz lots of text that is at least 300 characters long 0123456789 abcdefghijklmnopqrstuvwxyz lots of text that is at least 300 characters long 0123456789 abcdefghijklmnopqrstuvwxyz lots of text that is at least 300 characters long 0123456789 abcdefghijklmnopqrstuvwxyz lots of text that is at least 300 characters long 0123456789 abcdefghijklmnopqrstuvwxyz lots of text that is at least 300 characters long 0123456789 abcdefghijklmnopqrstuvwxyz should not reach end of string\n\t\t"), + wantSyncErr: testutil.WantExactErrorString("error for JWTAuthenticator test-name: could not perform oidc discovery on provider issuer: 404 Not Found: \n\t\t \t404 not found page\n\t\t\tlots of text that is at least 300 characters long 0123456789 abcdefghijklmnopqrstuvwxyz lots of text that is at least 300 characters long 0123456789 abcdefghijklmnopqrstuvwxyz lots of text that is at least 300 characters long 0123456789 abcdefghijklmnopqrstuvwxyz lots of text that is at least 300 characters long 0123456789 abcdefghijklmnopqrstuvwxyz lots of text that is at least 300 characters long 0123456789 abcdefghijklmnopqrstuvwxyz lots of text that is at least 300 characters long 0123456789 abcdefghijklmnopqrstuvwxyz lots of text that is at least 300 characters long 0123456789 abcdefghijklmnopqrstuvwxyz lots of text that is at least 300 characters long 0123456789 abcdefghijklmnopqrstuvwxyz should not reach end of string\n\t\t"), }, // cannot be tested currently the way the coreos lib works. // the constructor requires an issuer in the payload and validates the issuer matches the actual issuer, @@ -1918,7 +1981,7 @@ func TestController(t *testing.T) { updateStatusAction, } }, - wantSyncLoopErr: testutil.WantExactErrorString(`error for JWTAuthenticator test-name: could not parse provider jwks_uri: parse "https://.café .com/café/café/café/coffee/jwks.json": invalid character " " in host name`), + wantSyncErr: testutil.WantExactErrorString(`error for JWTAuthenticator test-name: could not parse provider jwks_uri: parse "https://.café .com/café/café/café/coffee/jwks.json": invalid character " " in host name`), }, { name: "validateProviderJWKSURL: invalid scheme, requires 'https': loop will fail sync, will write failed and unknown conditions, and will enqueue new sync", @@ -1957,7 +2020,7 @@ func TestController(t *testing.T) { updateStatusAction, } }, - wantSyncLoopErr: testutil.WantExactErrorString("error for JWTAuthenticator test-name: jwks_uri http://.café.com/café/café/café/coffee/jwks.json has invalid scheme, require 'https'"), + wantSyncErr: testutil.WantExactErrorString("error for JWTAuthenticator test-name: jwks_uri http://.café.com/café/café/café/coffee/jwks.json has invalid scheme, require 'https'"), }, { name: "validateProviderJWKSURL: remote jwks should not have been able to verify hardcoded test jwt token: loop will fail sync, will write failed and unknown conditions, and will enqueue new sync", @@ -1997,7 +2060,7 @@ func TestController(t *testing.T) { updateStatusAction, } }, - wantSyncLoopErr: testutil.WantExactErrorString("error for JWTAuthenticator test-name: remote jwks should not have been able to verify hardcoded test jwt token"), + wantSyncErr: testutil.WantExactErrorString("error for JWTAuthenticator test-name: remote jwks should not have been able to verify hardcoded test jwt token"), }, { name: "validateJWKSFetch: could not fetch keys: loop will fail sync, will write failed and unknown status conditions, and will enqueue a resync", @@ -2036,7 +2099,7 @@ func TestController(t *testing.T) { updateStatusAction, } }, - wantSyncLoopErr: testutil.WantExactErrorString("error for JWTAuthenticator test-name: could not fetch keys: fetching keys oidc: get keys failed: 404 Not Found 404 page not found\n"), + wantSyncErr: testutil.WantExactErrorString("error for JWTAuthenticator test-name: could not fetch keys: fetching keys oidc: get keys failed: 404 Not Found 404 page not found\n"), }, { name: "updateStatus: called with matching original and updated conditions: will not make request to update conditions", @@ -2120,22 +2183,13 @@ func TestController(t *testing.T) { wantNamesOfJWTAuthenticatorsInCache: []string{"test-name"}, }, { - name: "updateStatus: when update request fails: error will enqueue a resync", + name: "updateStatus: given a valid JWTAuthenticator spec, when update request fails: error will enqueue a resync and the authenticator will not be added to the cache", jwtAuthenticators: []runtime.Object{ &authenticationv1alpha1.JWTAuthenticator{ ObjectMeta: metav1.ObjectMeta{ Name: "test-name", }, Spec: *someJWTAuthenticatorSpec, - Status: authenticationv1alpha1.JWTAuthenticatorStatus{ - Conditions: conditionstestutil.Replace( - allHappyConditionsSuccess(goodIssuer, frozenMetav1Now, 0), - []metav1.Condition{ - sadReadyCondition(frozenMetav1Now, 0), - }, - ), - Phase: "SomethingThatWontUpdate", - }, }, }, configClient: func(client *conciergefake.Clientset) { @@ -2149,7 +2203,7 @@ func TestController(t *testing.T) { }, wantActions: func() []coretesting.Action { // This captures that there was an attempt to update to Ready, allHappyConditions, - // but the wantSyncLoopErr indicates that there is a failure, so the JWTAuthenticator + // but the wantSyncErr indicates that there is a failure, so the JWTAuthenticator // remains with a bad phase and at least 1 sad condition updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -2168,18 +2222,9 @@ func TestController(t *testing.T) { updateStatusAction, } }, - wantLogs: []map[string]any{{ - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "jwtcachefiller-controller", - "message": "added new jwt authenticator", - "issuer": goodIssuer, - "jwtAuthenticator": map[string]any{ - "name": "test-name", - }, - }}, - wantSyncLoopErr: testutil.WantExactErrorString("error for JWTAuthenticator test-name: some update error"), - wantNamesOfJWTAuthenticatorsInCache: []string{"test-name"}, + wantLogs: []map[string]any{}, + wantSyncErr: testutil.WantExactErrorString("error for JWTAuthenticator test-name: some update error"), + wantNamesOfJWTAuthenticatorsInCache: []string{}, // even though the authenticator was valid, do not cache it because the status update failed }, // cannot be tested the way we are invoking oidc.New as we don't provide enough configuration // knobs to actually invoke the code in a broken way. We always give a good client, good keys, and @@ -2227,8 +2272,8 @@ func TestController(t *testing.T) { syncCtx := controllerlib.Context{Context: ctx} - if err := controllerlib.TestSync(t, controller, syncCtx); tt.wantSyncLoopErr != nil { - testutil.RequireErrorStringFromErr(t, err, tt.wantSyncLoopErr) + if err := controllerlib.TestSync(t, controller, syncCtx); tt.wantSyncErr != nil { + testutil.RequireErrorStringFromErr(t, err, tt.wantSyncErr) } else { require.NoError(t, err) } diff --git a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go index 9a62122d3..910976102 100644 --- a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go +++ b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go @@ -65,6 +65,10 @@ type cachedWebhookAuthenticator struct { caBundleHash tlsconfigutil.CABundleHash } +func (*cachedWebhookAuthenticator) Close() { + // no-op, because no cleanup is needed on webhook authenticators +} + // New instantiates a new controllerlib.Controller which will populate the provided authncache.Cache. func New( namespace string, @@ -171,11 +175,11 @@ func (c *webhookCacheFillerController) syncIndividualWebhookAuthenticator(ctx co // convenience at the time of a configuration change, to catch typos and blatant misconfigurations, rather // than to constantly monitor for external issues. if valueFromCache := c.cache.Get(cacheKey); valueFromCache != nil { - webhookAuthenticatorFromCache := c.cacheValueAsWebhookAuthenticator(valueFromCache) - if webhookAuthenticatorFromCache != nil && - reflect.DeepEqual(webhookAuthenticatorFromCache.spec, &webhookAuthenticator.Spec) && + oldWebhookAuthenticatorFromCache := c.cacheValueAsWebhookAuthenticator(valueFromCache) + if oldWebhookAuthenticatorFromCache != nil && + reflect.DeepEqual(oldWebhookAuthenticatorFromCache.spec, &webhookAuthenticator.Spec) && tlsBundleOk && // if there was any error while validating the CA bundle, then run remaining validations and update status - webhookAuthenticatorFromCache.caBundleHash.Equal(caBundle.Hash()) { + oldWebhookAuthenticatorFromCache.caBundleHash.Equal(caBundle.Hash()) { c.log.WithValues("webhookAuthenticator", klog.KObj(webhookAuthenticator), "endpoint", webhookAuthenticator.Spec.Endpoint). Info("actual webhook authenticator and desired webhook authenticator are the same") // Stop, no more work to be done. This authenticator is already validated and cached. diff --git a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go index 89f2b01b0..8fb9eff54 100644 --- a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go +++ b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go @@ -20,12 +20,12 @@ import ( "time" "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" authenticationv1beta1 "k8s.io/api/authentication/v1beta1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apiserver/pkg/authentication/authenticator" k8sinformers "k8s.io/client-go/informers" kubeinformers "k8s.io/client-go/informers" kubernetesfake "k8s.io/client-go/kubernetes/fake" @@ -41,6 +41,7 @@ import ( "go.pinniped.dev/internal/controller/tlsconfigutil" "go.pinniped.dev/internal/controllerlib" "go.pinniped.dev/internal/crypto/ptls" + "go.pinniped.dev/internal/mocks/mockcachevalue" "go.pinniped.dev/internal/plog" "go.pinniped.dev/internal/testutil" "go.pinniped.dev/internal/testutil/conditionstestutil" @@ -410,10 +411,10 @@ func TestController(t *testing.T) { webhookAuthenticators []runtime.Object secretsAndConfigMaps []runtime.Object // for modifying the clients to hack in arbitrary api responses - configClient func(*conciergefake.Clientset) - wantSyncLoopErr testutil.RequireErrorStringFunc - wantLogs []map[string]any - wantActions func() []coretesting.Action + configClient func(*conciergefake.Clientset) + wantSyncErr testutil.RequireErrorStringFunc + wantLogs []map[string]any + wantActions func() []coretesting.Action // random comment so lines above don't have huge indents wantNamesOfWebhookAuthenticatorsInCache []string }{ @@ -833,6 +834,12 @@ func TestController(t *testing.T) { { name: "Sync: authenticator update when cached authenticator is the wrong data type, which should never really happen: loop will complete successfully and update status conditions", cache: func(t *testing.T, cache *authncache.Cache) { + ctrl := gomock.NewController(t) + t.Cleanup(func() { + ctrl.Finish() + }) + mockCacheValue := mockcachevalue.NewMockValue(ctrl) + mockCacheValue.EXPECT().Close().Times(1) cache.Store( authncache.Key{ Name: "test-name", @@ -842,7 +849,7 @@ func TestController(t *testing.T) { // Only entries of type cachedWebhookAuthenticator are ever put into the cache, so this should never really happen. // This test is to provide coverage on the production code which reads from the cache and casts those entries to // the appropriate data type. - struct{ authenticator.Token }{}, + mockCacheValue, ) }, webhookAuthenticators: []runtime.Object{ @@ -859,7 +866,7 @@ func TestController(t *testing.T) { "timestamp": "2099-08-08T13:57:36.123456Z", "logger": "webhookcachefiller-controller", "message": "wrong webhook authenticator type in cache", - "actualType": "struct { authenticator.Token }", + "actualType": "*mockcachevalue.MockValue", }, { "level": "info", @@ -1097,7 +1104,7 @@ func TestController(t *testing.T) { updateStatusAction, } }, - wantSyncLoopErr: testutil.WantExactErrorString(`error for WebhookAuthenticator test-name: cannot dial server: tls: failed to verify certificate: x509: certificate signed by unknown authority`), + wantSyncErr: testutil.WantExactErrorString(`error for WebhookAuthenticator test-name: cannot dial server: tls: failed to verify certificate: x509: certificate signed by unknown authority`), wantNamesOfWebhookAuthenticatorsInCache: []string{}, }, { @@ -1337,7 +1344,7 @@ func TestController(t *testing.T) { Spec: badWebhookAuthenticatorSpecGoodEndpointButUnknownCA, }, }, - wantSyncLoopErr: testutil.WantExactErrorString("error for WebhookAuthenticator test-name: cannot dial server: tls: failed to verify certificate: x509: certificate signed by unknown authority"), + wantSyncErr: testutil.WantExactErrorString("error for WebhookAuthenticator test-name: cannot dial server: tls: failed to verify certificate: x509: certificate signed by unknown authority"), wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -1495,7 +1502,7 @@ func TestController(t *testing.T) { updateStatusAction, } }, - wantSyncLoopErr: testutil.WantExactErrorString(`error for WebhookAuthenticator test-name: cannot dial server: dial tcp [::1]:4242: connect: connection refused`), + wantSyncErr: testutil.WantExactErrorString(`error for WebhookAuthenticator test-name: cannot dial server: dial tcp [::1]:4242: connect: connection refused`), wantNamesOfWebhookAuthenticatorsInCache: []string{}, }, { @@ -1543,7 +1550,7 @@ func TestController(t *testing.T) { updateStatusAction, } }, - wantSyncLoopErr: testutil.WantExactErrorString(`error for WebhookAuthenticator test-name: cannot dial server: dial tcp [::1]:443: connect: connection refused`), + wantSyncErr: testutil.WantExactErrorString(`error for WebhookAuthenticator test-name: cannot dial server: dial tcp [::1]:443: connect: connection refused`), wantNamesOfWebhookAuthenticatorsInCache: []string{}, }, { @@ -1625,7 +1632,7 @@ func TestController(t *testing.T) { } }, wantNamesOfWebhookAuthenticatorsInCache: []string{}, - wantSyncLoopErr: testutil.WantExactErrorString(`error for WebhookAuthenticator test-name: cannot dial server: tls: failed to verify certificate: x509: cannot validate certificate for 127.0.0.1 because it doesn't contain any IP SANs`), + wantSyncErr: testutil.WantExactErrorString(`error for WebhookAuthenticator test-name: cannot dial server: tls: failed to verify certificate: x509: cannot validate certificate for 127.0.0.1 because it doesn't contain any IP SANs`), }, { name: "validateConnection: IPv6 address without port or brackets: should succeed since IPv6 brackets are optional without port", @@ -1672,7 +1679,7 @@ func TestController(t *testing.T) { updateStatusAction, } }, - wantSyncLoopErr: testutil.WantExactErrorString(`error for WebhookAuthenticator test-name: cannot dial server: dial tcp [::1]:443: connect: connection refused`), + wantSyncErr: testutil.WantExactErrorString(`error for WebhookAuthenticator test-name: cannot dial server: dial tcp [::1]:443: connect: connection refused`), wantNamesOfWebhookAuthenticatorsInCache: []string{}, }, { @@ -1818,7 +1825,7 @@ func TestController(t *testing.T) { updateStatusAction, } }, - wantSyncLoopErr: testutil.WantExactErrorString("error for WebhookAuthenticator test-name: some update error"), + wantSyncErr: testutil.WantExactErrorString("error for WebhookAuthenticator test-name: some update error"), wantNamesOfWebhookAuthenticatorsInCache: []string{"test-name"}, }, } @@ -1861,8 +1868,8 @@ func TestController(t *testing.T) { syncCtx := controllerlib.Context{Context: ctx} - if err := controllerlib.TestSync(t, controller, syncCtx); tt.wantSyncLoopErr != nil { - testutil.RequireErrorStringFromErr(t, err, tt.wantSyncLoopErr) + if err := controllerlib.TestSync(t, controller, syncCtx); tt.wantSyncErr != nil { + testutil.RequireErrorStringFromErr(t, err, tt.wantSyncErr) } else { require.NoError(t, err) } diff --git a/internal/mocks/mockcachevalue/generate.go b/internal/mocks/mockcachevalue/generate.go new file mode 100644 index 000000000..47447114a --- /dev/null +++ b/internal/mocks/mockcachevalue/generate.go @@ -0,0 +1,6 @@ +// Copyright 2024 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package mockcachevalue + +//go:generate go run -v go.uber.org/mock/mockgen -destination=mockcachevalue.go -package=mockcachevalue -copyright_file=../../../hack/header.txt go.pinniped.dev/internal/controller/authenticator/authncache Value diff --git a/internal/mocks/mockcachevalue/mockcachevalue.go b/internal/mocks/mockcachevalue/mockcachevalue.go new file mode 100644 index 000000000..6339b35a4 --- /dev/null +++ b/internal/mocks/mockcachevalue/mockcachevalue.go @@ -0,0 +1,73 @@ +// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +// + +// Code generated by MockGen. DO NOT EDIT. +// Source: go.pinniped.dev/internal/controller/authenticator/authncache (interfaces: Value) +// +// Generated by this command: +// +// mockgen -destination=mockcachevalue.go -package=mockcachevalue -copyright_file=../../../hack/header.txt go.pinniped.dev/internal/controller/authenticator/authncache Value +// + +// Package mockcachevalue is a generated GoMock package. +package mockcachevalue + +import ( + context "context" + reflect "reflect" + + gomock "go.uber.org/mock/gomock" + authenticator "k8s.io/apiserver/pkg/authentication/authenticator" +) + +// MockValue is a mock of Value interface. +type MockValue struct { + ctrl *gomock.Controller + recorder *MockValueMockRecorder +} + +// MockValueMockRecorder is the mock recorder for MockValue. +type MockValueMockRecorder struct { + mock *MockValue +} + +// NewMockValue creates a new mock instance. +func NewMockValue(ctrl *gomock.Controller) *MockValue { + mock := &MockValue{ctrl: ctrl} + mock.recorder = &MockValueMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockValue) EXPECT() *MockValueMockRecorder { + return m.recorder +} + +// AuthenticateToken mocks base method. +func (m *MockValue) AuthenticateToken(arg0 context.Context, arg1 string) (*authenticator.Response, bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AuthenticateToken", arg0, arg1) + ret0, _ := ret[0].(*authenticator.Response) + ret1, _ := ret[1].(bool) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// AuthenticateToken indicates an expected call of AuthenticateToken. +func (mr *MockValueMockRecorder) AuthenticateToken(arg0, arg1 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AuthenticateToken", reflect.TypeOf((*MockValue)(nil).AuthenticateToken), arg0, arg1) +} + +// Close mocks base method. +func (m *MockValue) Close() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "Close") +} + +// Close indicates an expected call of Close. +func (mr *MockValueMockRecorder) Close() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockValue)(nil).Close)) +} diff --git a/internal/mocks/mocktokenauthenticator/generate.go b/internal/mocks/mocktokenauthenticator/generate.go deleted file mode 100644 index 13329bfcc..000000000 --- a/internal/mocks/mocktokenauthenticator/generate.go +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -package mocktokenauthenticator - -//go:generate go run -v go.uber.org/mock/mockgen -destination=mocktokenauthenticator.go -package=mocktokenauthenticator -copyright_file=../../../hack/header.txt k8s.io/apiserver/pkg/authentication/authenticator Token diff --git a/internal/mocks/mocktokenauthenticator/mocktokenauthenticator.go b/internal/mocks/mocktokenauthenticator/mocktokenauthenticator.go deleted file mode 100644 index 86a158084..000000000 --- a/internal/mocks/mocktokenauthenticator/mocktokenauthenticator.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -// - -// Code generated by MockGen. DO NOT EDIT. -// Source: k8s.io/apiserver/pkg/authentication/authenticator (interfaces: Token) -// -// Generated by this command: -// -// mockgen -destination=mocktokenauthenticator.go -package=mocktokenauthenticator -copyright_file=../../../hack/header.txt k8s.io/apiserver/pkg/authentication/authenticator Token -// - -// Package mocktokenauthenticator is a generated GoMock package. -package mocktokenauthenticator - -import ( - context "context" - reflect "reflect" - - gomock "go.uber.org/mock/gomock" - authenticator "k8s.io/apiserver/pkg/authentication/authenticator" -) - -// MockToken is a mock of Token interface. -type MockToken struct { - ctrl *gomock.Controller - recorder *MockTokenMockRecorder -} - -// MockTokenMockRecorder is the mock recorder for MockToken. -type MockTokenMockRecorder struct { - mock *MockToken -} - -// NewMockToken creates a new mock instance. -func NewMockToken(ctrl *gomock.Controller) *MockToken { - mock := &MockToken{ctrl: ctrl} - mock.recorder = &MockTokenMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockToken) EXPECT() *MockTokenMockRecorder { - return m.recorder -} - -// AuthenticateToken mocks base method. -func (m *MockToken) AuthenticateToken(arg0 context.Context, arg1 string) (*authenticator.Response, bool, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "AuthenticateToken", arg0, arg1) - ret0, _ := ret[0].(*authenticator.Response) - ret1, _ := ret[1].(bool) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// AuthenticateToken indicates an expected call of AuthenticateToken. -func (mr *MockTokenMockRecorder) AuthenticateToken(arg0, arg1 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AuthenticateToken", reflect.TypeOf((*MockToken)(nil).AuthenticateToken), arg0, arg1) -} From dfef9f470f85469599c389919cd21fbb97041d61 Mon Sep 17 00:00:00 2001 From: Ryan Richard Date: Fri, 26 Jul 2024 15:06:37 -0700 Subject: [PATCH 65/99] fix bug in webhookcachefiller caused when status update returns error Also refactor test assertions regarding log statements in jwtcachefiller_test.go and webhookcachefiller_test.go Co-authored-by: Ashish Amarnath --- .../jwtcachefiller/jwtcachefiller_test.go | 37 +-- .../webhookcachefiller/webhookcachefiller.go | 21 +- .../webhookcachefiller_test.go | 215 +++++++++++++----- 3 files changed, 194 insertions(+), 79 deletions(-) diff --git a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller_test.go b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller_test.go index 0600456f2..748307667 100644 --- a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller_test.go +++ b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller_test.go @@ -32,6 +32,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/apiserver/pkg/authentication/authenticator" "k8s.io/apiserver/pkg/authentication/user" @@ -2290,26 +2291,28 @@ func TestController(t *testing.T) { actualLogLines := testutil.SplitByNewline(log.String()) require.Equal(t, len(tt.wantLogs), len(actualLogLines), "log line count should be correct") - for logLineNum, logLine := range actualLogLines { - require.NotNil(t, tt.wantLogs[logLineNum], "expected log line should never be empty") - var lineStruct map[string]any - err := json.Unmarshal([]byte(logLine), &lineStruct) + for actualLogLineNum, actualLogLine := range actualLogLines { + wantLine := tt.wantLogs[actualLogLineNum] + require.NotNil(t, wantLine, "expected log line should never be empty") + + var actualParsedLine map[string]any + err := json.Unmarshal([]byte(actualLogLine), &actualParsedLine) require.NoError(t, err) - require.Equal(t, tt.wantLogs[logLineNum]["level"], lineStruct["level"], fmt.Sprintf("log line (%d) log level should be correct (in: %s)", logLineNum, lineStruct)) + wantLineAsJSON, err := json.Marshal(wantLine) + require.NoError(t, err) + wantLine["caller"] = "we don't want to actually make equality comparisons about this" + require.Lenf(t, actualParsedLine, len(wantLine), "actual: %s\nwant: %s", actualLogLine, string(wantLineAsJSON)) + require.Equal(t, sets.StringKeySet(actualParsedLine), sets.StringKeySet(wantLine)) - require.Equal(t, tt.wantLogs[logLineNum]["timestamp"], lineStruct["timestamp"], fmt.Sprintf("log line (%d) timestamp should be correct (in: %s)", logLineNum, lineStruct)) - require.Equal(t, tt.wantLogs[logLineNum]["logger"], lineStruct["logger"], fmt.Sprintf("log line (%d) logger should be correct", logLineNum)) - require.NotEmpty(t, lineStruct["caller"], fmt.Sprintf("log line (%d) caller should not be empty", logLineNum)) - require.Equal(t, tt.wantLogs[logLineNum]["message"], lineStruct["message"], fmt.Sprintf("log line (%d) message should be correct", logLineNum)) - if lineStruct["issuer"] != nil { - require.Equal(t, tt.wantLogs[logLineNum]["issuer"], lineStruct["issuer"], fmt.Sprintf("log line (%d) issuer should be correct", logLineNum)) - } - if lineStruct["jwtAuthenticator"] != nil { - require.Equal(t, tt.wantLogs[logLineNum]["jwtAuthenticator"], lineStruct["jwtAuthenticator"], fmt.Sprintf("log line (%d) jwtAuthenticator should be correct", logLineNum)) - } - if lineStruct["actualType"] != nil { - require.Equal(t, tt.wantLogs[logLineNum]["actualType"], lineStruct["actualType"], fmt.Sprintf("log line (%d) actualType should be correct", logLineNum)) + for k := range actualParsedLine { + if k == "caller" { + require.NotEmpty(t, actualParsedLine["caller"]) + } else { + require.Equal(t, wantLine[k], actualParsedLine[k], + fmt.Sprintf("log line (%d) key %q was not equal\nactual: %s\nwant: %s", + actualLogLineNum, k, actualParsedLine[k], wantLine[k])) + } } } diff --git a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go index 910976102..b27293b1c 100644 --- a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go +++ b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go @@ -205,24 +205,33 @@ func (c *webhookCacheFillerController) syncIndividualWebhookAuthenticator(ctx co ) errs = append(errs, err) - if conditionsutil.HadErrorCondition(conditions) { + authenticatorValid := !conditionsutil.HadErrorCondition(conditions) + + // If we calculated a failed status condition, then remove it from the cache even before we try to write + // the status, because writing the status can fail for various reasons. + if !authenticatorValid { // The authenticator was determined to be invalid. Remove it from the cache, in case it was previously // validated and cached. Do not allow an old, previously validated spec of the authenticator to continue // being used for authentication. c.cache.Delete(cacheKey) - } else { + } + + updateErr := c.updateStatus(ctx, webhookAuthenticator, conditions) + errs = append(errs, updateErr) + + // Only add this WebhookAuthenticator to the cache if the status update succeeds. + // If it were in the cache after failing to update the status, then the next Sync loop would see it in the cache + // and skip trying to update its status again, which would leave its old status permanently intact. + if authenticatorValid && updateErr == nil { c.cache.Store(cacheKey, &cachedWebhookAuthenticator{ Token: newWebhookAuthenticatorForCache, spec: webhookAuthenticator.Spec.DeepCopy(), // deep copy to avoid caching original object caBundleHash: caBundle.Hash(), }) - c.log.WithValues("webhook", klog.KObj(webhookAuthenticator), "endpoint", webhookAuthenticator.Spec.Endpoint). + c.log.WithValues("webhookAuthenticator", klog.KObj(webhookAuthenticator), "endpoint", webhookAuthenticator.Spec.Endpoint). Info("added new webhook authenticator") } - err = c.updateStatus(ctx, webhookAuthenticator, conditions) - errs = append(errs, err) - // Sync loop errors: // - Should not be configuration errors. Config errors a user must correct belong on the .Status // object. The controller simply must wait for a user to correct before running again. diff --git a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go index 8fb9eff54..39604af30 100644 --- a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go +++ b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go @@ -26,6 +26,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/sets" k8sinformers "k8s.io/client-go/informers" kubeinformers "k8s.io/client-go/informers" kubernetesfake "k8s.io/client-go/kubernetes/fake" @@ -457,7 +458,7 @@ func TestController(t *testing.T) { "logger": "webhookcachefiller-controller", "message": "added new webhook authenticator", "endpoint": goodWebhookDefaultServingCertEndpoint, - "webhook": map[string]any{ + "webhookAuthenticator": map[string]any{ "name": "test-name", }, }, @@ -509,7 +510,7 @@ func TestController(t *testing.T) { "logger": "webhookcachefiller-controller", "message": "added new webhook authenticator", "endpoint": goodWebhookDefaultServingCertEndpoint, - "webhook": map[string]any{ + "webhookAuthenticator": map[string]any{ "name": "existing-webhook-authenticator", }, }, @@ -519,7 +520,7 @@ func TestController(t *testing.T) { "logger": "webhookcachefiller-controller", "message": "added new webhook authenticator", "endpoint": goodWebhookDefaultServingCertEndpoint, - "webhook": map[string]any{ + "webhookAuthenticator": map[string]any{ "name": "new-webhook-authenticator", }, }, @@ -608,7 +609,7 @@ func TestController(t *testing.T) { "logger": "webhookcachefiller-controller", "message": "added new webhook authenticator", "endpoint": goodWebhookDefaultServingCertEndpoint, - "webhook": map[string]any{ + "webhookAuthenticator": map[string]any{ "name": "test-name", }, }, @@ -653,7 +654,7 @@ func TestController(t *testing.T) { "logger": "webhookcachefiller-controller", "message": "added new webhook authenticator", "endpoint": goodWebhookDefaultServingCertEndpoint, - "webhook": map[string]any{ + "webhookAuthenticator": map[string]any{ "name": "test-name", }, }, @@ -708,7 +709,7 @@ func TestController(t *testing.T) { "logger": "webhookcachefiller-controller", "message": "added new webhook authenticator", "endpoint": goodWebhookDefaultServingCertEndpoint, - "webhook": map[string]any{ + "webhookAuthenticator": map[string]any{ "name": "test-name", }, }, @@ -818,7 +819,7 @@ func TestController(t *testing.T) { "logger": "webhookcachefiller-controller", "message": "actual webhook authenticator and desired webhook authenticator are the same", "endpoint": goodWebhookDefaultServingCertEndpoint, - "webhook": map[string]any{ + "webhookAuthenticator": map[string]any{ "name": "test-name", }, }, @@ -874,7 +875,7 @@ func TestController(t *testing.T) { "logger": "webhookcachefiller-controller", "message": "added new webhook authenticator", "endpoint": goodWebhookDefaultServingCertEndpoint, - "webhook": map[string]any{ + "webhookAuthenticator": map[string]any{ "name": "test-name", }, }, @@ -939,7 +940,7 @@ func TestController(t *testing.T) { "logger": "webhookcachefiller-controller", "message": "added new webhook authenticator", "endpoint": goodWebhookDefaultServingCertEndpoint, - "webhook": map[string]any{ + "webhookAuthenticator": map[string]any{ "name": "test-name", }, }, @@ -970,6 +971,61 @@ func TestController(t *testing.T) { }, wantNamesOfWebhookAuthenticatorsInCache: []string{"test-name"}, }, + { + name: "Sync: previously cached authenticator gets new valid spec fields, but status update fails: loop will leave it in the cache", + cache: func(t *testing.T, cache *authncache.Cache) { + oldCA, err := base64.StdEncoding.DecodeString(goodWebhookAuthenticatorSpecWith404Endpoint.TLS.CertificateAuthorityData) + require.NoError(t, err) + cache.Store( + authncache.Key{ + Name: "test-name", + Kind: "WebhookAuthenticator", + APIGroup: authenticationv1alpha1.SchemeGroupVersion.Group, + }, + newCacheValue(t, goodWebhookAuthenticatorSpecWith404Endpoint, string(oldCA)), + ) + }, + configClient: func(client *conciergefake.Clientset) { + client.PrependReactor( + "update", + "webhookauthenticators", + func(action coretesting.Action) (handled bool, ret runtime.Object, err error) { + return true, nil, errors.New("some update error") + }, + ) + }, + webhookAuthenticators: []runtime.Object{ + &authenticationv1alpha1.WebhookAuthenticator{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-name", + Generation: 1234, + }, + Spec: goodWebhookAuthenticatorSpecWithCA, + }, + }, + wantLogs: []map[string]any{}, + wantActions: func() []coretesting.Action { + updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-name", + Generation: 1234, + }, + Spec: goodWebhookAuthenticatorSpecWithCA, + Status: authenticationv1alpha1.WebhookAuthenticatorStatus{ + Conditions: allHappyConditionsSuccess(goodWebhookDefaultServingCertEndpoint, frozenMetav1Now, 1234), + Phase: "Ready", + }, + }) + updateStatusAction.Subresource = "status" + return []coretesting.Action{ + coretesting.NewListAction(webhookAuthenticatorGVR, webhookAuthenticatorGVK, "", metav1.ListOptions{}), + coretesting.NewWatchAction(webhookAuthenticatorGVR, "", metav1.ListOptions{}), + updateStatusAction, + } + }, + wantSyncErr: testutil.WantExactErrorString("error for WebhookAuthenticator test-name: some update error"), + wantNamesOfWebhookAuthenticatorsInCache: []string{"test-name"}, // keeps the old entry in the cache + }, { name: "Sync: valid WebhookAuthenticator with CA: will complete sync loop successfully with success conditions and ready phase", webhookAuthenticators: []runtime.Object{ @@ -987,7 +1043,7 @@ func TestController(t *testing.T) { "logger": "webhookcachefiller-controller", "message": "added new webhook authenticator", "endpoint": goodWebhookDefaultServingCertEndpoint, - "webhook": map[string]any{ + "webhookAuthenticator": map[string]any{ "name": "test-name", }, }, @@ -1036,7 +1092,7 @@ func TestController(t *testing.T) { "logger": "webhookcachefiller-controller", "message": "added new webhook authenticator", "endpoint": hostLocalIPv6Server.URL, - "webhook": map[string]any{ + "webhookAuthenticator": map[string]any{ "name": "test-name", }, }, @@ -1200,6 +1256,71 @@ func TestController(t *testing.T) { }, wantNamesOfWebhookAuthenticatorsInCache: []string{}, // removed from cache }, + { + name: "previously valid cached authenticator's spec changes and becomes invalid (e.g. spec.issuer URL is invalid): loop will fail sync, will write failed and unknown status conditions, and will remove authenticator from cache even though the status update failed", + cache: func(t *testing.T, cache *authncache.Cache) { + oldCA, err := base64.StdEncoding.DecodeString(goodWebhookAuthenticatorSpecWithCA.TLS.CertificateAuthorityData) + require.NoError(t, err) + cache.Store( + authncache.Key{ + Name: "test-name", + Kind: "WebhookAuthenticator", + APIGroup: authenticationv1alpha1.SchemeGroupVersion.Group, + }, + newCacheValue(t, goodWebhookAuthenticatorSpecWithCA, string(oldCA)), + ) + }, + configClient: func(client *conciergefake.Clientset) { + client.PrependReactor( + "update", + "webhookauthenticators", + func(action coretesting.Action) (handled bool, ret runtime.Object, err error) { + return true, nil, errors.New("some update error") + }, + ) + }, + webhookAuthenticators: []runtime.Object{ + &authenticationv1alpha1.WebhookAuthenticator{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-name", + }, + Spec: authenticationv1alpha1.WebhookAuthenticatorSpec{ + Endpoint: badEndpointInvalidURL, + }, + }, + }, + wantActions: func() []coretesting.Action { + updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-name", + }, + Spec: authenticationv1alpha1.WebhookAuthenticatorSpec{ + Endpoint: badEndpointInvalidURL, + }, + Status: authenticationv1alpha1.WebhookAuthenticatorStatus{ + Conditions: conditionstestutil.Replace( + allHappyConditionsSuccess(goodWebhookDefaultServingCertEndpoint, frozenMetav1Now, 0), + []metav1.Condition{ + happyTLSConfigurationValidNoCA(frozenMetav1Now, 0), + sadEndpointURLValid("https://.café .com/café/café/café/coffee", frozenMetav1Now, 0), + unknownWebhookConnectionValid(frozenMetav1Now, 0), + unknownAuthenticatorValid(frozenMetav1Now, 0), + sadReadyCondition(frozenMetav1Now, 0), + }, + ), + Phase: "Error", + }, + }) + updateStatusAction.Subresource = "status" + return []coretesting.Action{ + coretesting.NewListAction(webhookAuthenticatorGVR, webhookAuthenticatorGVK, "", metav1.ListOptions{}), + coretesting.NewWatchAction(webhookAuthenticatorGVR, "", metav1.ListOptions{}), + updateStatusAction, + } + }, + wantSyncErr: testutil.WantExactErrorString("error for WebhookAuthenticator test-name: some update error"), + wantNamesOfWebhookAuthenticatorsInCache: []string{}, // removed from cache + }, { name: "validateEndpoint: parsing error (spec.endpoint URL is invalid) will fail sync loop and will report failed and unknown conditions and Error phase, but will not enqueue a resync due to user config error", webhookAuthenticators: []runtime.Object{ @@ -1392,7 +1513,7 @@ func TestController(t *testing.T) { "logger": "webhookcachefiller-controller", "message": "added new webhook authenticator", "endpoint": goodWebhookDefaultServingCertEndpointBut404, - "webhook": map[string]any{ + "webhookAuthenticator": map[string]any{ "name": "test-name", }, }, @@ -1444,7 +1565,7 @@ func TestController(t *testing.T) { "logger": "webhookcachefiller-controller", "message": "added new webhook authenticator", "endpoint": fmt.Sprintf("https://localhost:%s", localhostURL.Port()), - "webhook": map[string]any{ + "webhookAuthenticator": map[string]any{ "name": "test-name", }, }, @@ -1579,7 +1700,7 @@ func TestController(t *testing.T) { "logger": "webhookcachefiller-controller", "message": "added new webhook authenticator", "endpoint": hostAs127001WebhookServer.URL, - "webhook": map[string]any{ + "webhookAuthenticator": map[string]any{ "name": "test-name", }, }, @@ -1703,7 +1824,7 @@ func TestController(t *testing.T) { "logger": "webhookcachefiller-controller", "message": "added new webhook authenticator", "endpoint": goodWebhookDefaultServingCertEndpoint, - "webhook": map[string]any{ + "webhookAuthenticator": map[string]any{ "name": "test-name", }, }, @@ -1742,7 +1863,7 @@ func TestController(t *testing.T) { "logger": "webhookcachefiller-controller", "message": "added new webhook authenticator", "endpoint": goodWebhookDefaultServingCertEndpoint, - "webhook": map[string]any{ + "webhookAuthenticator": map[string]any{ "name": "test-name", }, }, @@ -1768,7 +1889,7 @@ func TestController(t *testing.T) { wantNamesOfWebhookAuthenticatorsInCache: []string{"test-name"}, }, { - name: "updateStatus: when update request fails: error will enqueue a resync", + name: "updateStatus: given a valid WebhookAuthenticator spec, when update request fails: error will enqueue a resync and the authenticator will not be added to the cache", configClient: func(client *conciergefake.Clientset) { client.PrependReactor( "update", @@ -1784,29 +1905,9 @@ func TestController(t *testing.T) { Name: "test-name", }, Spec: goodWebhookAuthenticatorSpecWithCA, - Status: authenticationv1alpha1.WebhookAuthenticatorStatus{ - Conditions: conditionstestutil.Replace( - allHappyConditionsSuccess(goodWebhookDefaultServingCertEndpoint, frozenMetav1Now, 0), - []metav1.Condition{ - sadReadyCondition(frozenMetav1Now, 0), - }, - ), - Phase: "SomethingBeforeUpdating", - }, - }, - }, - wantLogs: []map[string]any{ - { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "webhookcachefiller-controller", - "message": "added new webhook authenticator", - "endpoint": goodWebhookDefaultServingCertEndpoint, - "webhook": map[string]any{ - "name": "test-name", - }, }, }, + wantLogs: []map[string]any{}, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -1826,7 +1927,7 @@ func TestController(t *testing.T) { } }, wantSyncErr: testutil.WantExactErrorString("error for WebhookAuthenticator test-name: some update error"), - wantNamesOfWebhookAuthenticatorsInCache: []string{"test-name"}, + wantNamesOfWebhookAuthenticatorsInCache: []string{}, // even though the authenticator was valid, do not cache it because the status update failed }, } for _, tt := range tests { @@ -1881,26 +1982,28 @@ func TestController(t *testing.T) { actualLogLines := testutil.SplitByNewline(log.String()) require.Equal(t, len(tt.wantLogs), len(actualLogLines), "log line count should be correct") - for logLineNum, logLine := range actualLogLines { - require.NotNil(t, tt.wantLogs[logLineNum], "expected log line should never be empty") - var lineStruct map[string]any - err := json.Unmarshal([]byte(logLine), &lineStruct) + for actualLogLineNum, actualLogLine := range actualLogLines { + wantLine := tt.wantLogs[actualLogLineNum] + require.NotNil(t, wantLine, "expected log line should never be empty") + + var actualParsedLine map[string]any + err := json.Unmarshal([]byte(actualLogLine), &actualParsedLine) require.NoError(t, err) - require.Equal(t, tt.wantLogs[logLineNum]["level"], lineStruct["level"], fmt.Sprintf("log line (%d) log level should be correct (in: %s)", logLineNum, lineStruct)) + wantLineAsJSON, err := json.Marshal(wantLine) + require.NoError(t, err) + wantLine["caller"] = "we don't want to actually make equality comparisons about this" + require.Lenf(t, actualParsedLine, len(wantLine), "actual: %s\nwant: %s", actualLogLine, string(wantLineAsJSON)) + require.Equal(t, sets.StringKeySet(actualParsedLine), sets.StringKeySet(wantLine)) - require.Equal(t, tt.wantLogs[logLineNum]["timestamp"], lineStruct["timestamp"], fmt.Sprintf("log line (%d) timestamp should be correct (in: %s)", logLineNum, lineStruct)) - require.Equal(t, lineStruct["logger"], tt.wantLogs[logLineNum]["logger"], fmt.Sprintf("log line (%d) logger should be correct", logLineNum)) - require.NotEmpty(t, lineStruct["caller"], fmt.Sprintf("log line (%d) caller should not be empty", logLineNum)) - require.Equal(t, tt.wantLogs[logLineNum]["message"], lineStruct["message"], fmt.Sprintf("log line (%d) message should be correct", logLineNum)) - if lineStruct["webhook"] != nil { - require.Equal(t, tt.wantLogs[logLineNum]["webhook"], lineStruct["webhook"], fmt.Sprintf("log line (%d) webhook should be correct", logLineNum)) - } - if lineStruct["endpoint"] != nil { - require.Equal(t, tt.wantLogs[logLineNum]["endpoint"], lineStruct["endpoint"], fmt.Sprintf("log line (%d) endpoint should be correct", logLineNum)) - } - if lineStruct["actualType"] != nil { - require.Equal(t, tt.wantLogs[logLineNum]["actualType"], lineStruct["actualType"], fmt.Sprintf("log line (%d) actualType should be correct", logLineNum)) + for k := range actualParsedLine { + if k == "caller" { + require.NotEmpty(t, actualParsedLine["caller"]) + } else { + require.Equal(t, wantLine[k], actualParsedLine[k], + fmt.Sprintf("log line (%d) key %q was not equal\nactual: %s\nwant: %s", + actualLogLineNum, k, actualParsedLine[k], wantLine[k])) + } } } }) From 81d42cb3b94421bbb4c3c7c47b52dccc34a6c1a2 Mon Sep 17 00:00:00 2001 From: Ashish Amarnath Date: Fri, 26 Jul 2024 15:52:50 -0700 Subject: [PATCH 66/99] add unit tests for validatedsettings cache storing ca bundle hash Signed-off-by: Ashish Amarnath Co-authored-by: Ryan Richard --- .../active_directory_upstream_watcher_test.go | 41 ++++++++++++++++++- .../ldap_upstream_watcher_test.go | 36 ++++++++++++++++ 2 files changed, 76 insertions(+), 1 deletion(-) diff --git a/internal/controller/supervisorconfig/activedirectoryupstreamwatcher/active_directory_upstream_watcher_test.go b/internal/controller/supervisorconfig/activedirectoryupstreamwatcher/active_directory_upstream_watcher_test.go index 04501bd19..4355e9a64 100644 --- a/internal/controller/supervisorconfig/activedirectoryupstreamwatcher/active_directory_upstream_watcher_test.go +++ b/internal/controller/supervisorconfig/activedirectoryupstreamwatcher/active_directory_upstream_watcher_test.go @@ -512,7 +512,46 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { Phase: "Ready", Conditions: allConditionsTrue(1234, "4242"), }, - }}, wantValidatedSettings: map[string]upstreamwatchers.ValidatedSettings{testName: { + }}, + wantValidatedSettings: map[string]upstreamwatchers.ValidatedSettings{testName: { + BindSecretResourceVersion: "4242", + LDAPConnectionProtocol: upstreamldap.TLS, + UserSearchBase: testUserSearchBase, + GroupSearchBase: testGroupSearchBase, + CABundleHash: tlsconfigutil.NewCABundleHash(providerConfigForValidUpstreamWithTLS.CABundle), + IDPSpecGeneration: 1234, + ConnectionValidCondition: condPtr(activeDirectoryConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), + SearchBaseFoundCondition: condPtr(withoutTime(searchBaseFoundInConfigCondition(0))), + }}, + }, + { + name: "valid upstream spec using a configmap to source CA bundles that is already in the cache is updated to have a new ca bundle: Sync should now update the cache with the new CA bundle hash", + inputUpstreams: []runtime.Object{validUpstreamWithConfigMapCABundleSource}, + inputK8sObjects: []runtime.Object{validBindUserSecret("4242"), caBundleConfigMap}, + initialValidatedSettings: map[string]upstreamwatchers.ValidatedSettings{testName: { + BindSecretResourceVersion: "4242", + LDAPConnectionProtocol: upstreamldap.TLS, + UserSearchBase: testUserSearchBase, + GroupSearchBase: testGroupSearchBase, + CABundleHash: tlsconfigutil.NewCABundleHash([]byte("this CA bundle should be replaced")), + IDPSpecGeneration: 1234, + ConnectionValidCondition: condPtr(activeDirectoryConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), + SearchBaseFoundCondition: condPtr(withoutTime(searchBaseFoundInConfigCondition(0))), + }}, + setupMocks: func(conn *mockldapconn.MockConn) { + // Should perform a test dial and bind. + conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1) + conn.EXPECT().Close().Times(1) + }, + wantResultingCache: []*upstreamldap.ProviderConfig{providerConfigForValidUpstreamWithTLS}, + wantResultingUpstreams: []idpv1alpha1.ActiveDirectoryIdentityProvider{{ + ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testName, UID: testResourceUID, Generation: 1234}, + Status: idpv1alpha1.ActiveDirectoryIdentityProviderStatus{ + Phase: "Ready", + Conditions: allConditionsTrue(1234, "4242"), + }, + }}, + wantValidatedSettings: map[string]upstreamwatchers.ValidatedSettings{testName: { BindSecretResourceVersion: "4242", LDAPConnectionProtocol: upstreamldap.TLS, UserSearchBase: testUserSearchBase, diff --git a/internal/controller/supervisorconfig/ldapupstreamwatcher/ldap_upstream_watcher_test.go b/internal/controller/supervisorconfig/ldapupstreamwatcher/ldap_upstream_watcher_test.go index 653fce090..1486ed826 100644 --- a/internal/controller/supervisorconfig/ldapupstreamwatcher/ldap_upstream_watcher_test.go +++ b/internal/controller/supervisorconfig/ldapupstreamwatcher/ldap_upstream_watcher_test.go @@ -452,6 +452,42 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { ConnectionValidCondition: condPtr(ldapConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), }}, }, + { + name: "valid upstream spec using a configmap to source CA bundles that is already in the cache is updated to have a new ca bundle: Sync should now update the cache with the new CA bundle hash", + inputUpstreams: []runtime.Object{validUpstreamWithConfigMapCABundleSource}, + inputSecrets: []runtime.Object{validBindUserSecret("4242"), caBundleConfigMap}, + initialValidatedSettings: map[string]upstreamwatchers.ValidatedSettings{testName: { + BindSecretResourceVersion: "4242", + LDAPConnectionProtocol: upstreamldap.TLS, + UserSearchBase: testUserSearchBase, + GroupSearchBase: testGroupSearchBase, + CABundleHash: tlsconfigutil.NewCABundleHash([]byte("this CA bundle should be replaced")), + IDPSpecGeneration: 1234, + ConnectionValidCondition: condPtr(ldapConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), + }}, + setupMocks: func(conn *mockldapconn.MockConn) { + // Should perform a test dial and bind. + conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1) + conn.EXPECT().Close().Times(1) + }, + wantResultingCache: []*upstreamldap.ProviderConfig{providerConfigForValidUpstreamWithTLS}, + wantResultingUpstreams: []idpv1alpha1.LDAPIdentityProvider{{ + ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testName, Generation: 1234, UID: testResourceUID}, + Status: idpv1alpha1.LDAPIdentityProviderStatus{ + Phase: "Ready", + Conditions: allConditionsTrue(1234, "4242"), + }, + }}, + wantValidatedSettings: map[string]upstreamwatchers.ValidatedSettings{testName: { + BindSecretResourceVersion: "4242", + LDAPConnectionProtocol: upstreamldap.TLS, + UserSearchBase: testUserSearchBase, + GroupSearchBase: testGroupSearchBase, + CABundleHash: tlsconfigutil.NewCABundleHash(providerConfigForValidUpstreamWithTLS.CABundle), + IDPSpecGeneration: 1234, + ConnectionValidCondition: condPtr(ldapConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), + }}, + }, { name: "one valid upstream using an opaque secret to source CA bundles updates the cache to include only that upstream", inputUpstreams: []runtime.Object{validUpstreamWithOpaqueSecretCABundleSource}, From 9f17ba5ae46df4f8a3c16e6d161a2160c698998d Mon Sep 17 00:00:00 2001 From: Ryan Richard Date: Mon, 29 Jul 2024 09:13:39 -0700 Subject: [PATCH 67/99] change wording of TLS config loaded success messages --- .../jwtcachefiller/jwtcachefiller_test.go | 4 +- .../webhookcachefiller_test.go | 4 +- .../active_directory_upstream_watcher_test.go | 48 ++++---- .../github_upstream_watcher_test.go | 74 ++++++------- .../ldap_upstream_watcher_test.go | 22 ++-- .../oidc_upstream_watcher_test.go | 104 +++++++++--------- .../tlsconfigutil/tls_config_util.go | 6 +- .../tlsconfigutil/tls_config_util_test.go | 6 +- .../concierge_jwtauthenticator_status_test.go | 4 +- ...cierge_webhookauthenticator_status_test.go | 2 +- .../integration/supervisor_github_idp_test.go | 6 +- test/integration/supervisor_login_test.go | 8 +- test/integration/supervisor_upstream_test.go | 6 +- 13 files changed, 147 insertions(+), 147 deletions(-) diff --git a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller_test.go b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller_test.go index 748307667..a8005650b 100644 --- a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller_test.go +++ b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller_test.go @@ -443,7 +443,7 @@ func TestController(t *testing.T) { ObservedGeneration: observedGeneration, LastTransitionTime: time, Reason: "Success", - Message: "spec.tls is valid: loaded TLS configuration", + Message: "spec.tls is valid: using configured CA bundle", } } happyTLSConfigurationValidNoCA := func(time metav1.Time, observedGeneration int64) metav1.Condition { @@ -453,7 +453,7 @@ func TestController(t *testing.T) { ObservedGeneration: observedGeneration, LastTransitionTime: time, Reason: "Success", - Message: "spec.tls is valid: no TLS configuration provided", + Message: "spec.tls is valid: no TLS configuration provided: using default root CA bundle from container image", } } sadTLSConfigurationValid := func(time metav1.Time, observedGeneration int64) metav1.Condition { diff --git a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go index 39604af30..5a5cbdd41 100644 --- a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go +++ b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go @@ -268,7 +268,7 @@ func TestController(t *testing.T) { ObservedGeneration: observedGeneration, LastTransitionTime: time, Reason: "Success", - Message: "spec.tls is valid: loaded TLS configuration", + Message: "spec.tls is valid: using configured CA bundle", } } happyTLSConfigurationValidNoCA := func(time metav1.Time, observedGeneration int64) metav1.Condition { @@ -278,7 +278,7 @@ func TestController(t *testing.T) { ObservedGeneration: observedGeneration, LastTransitionTime: time, Reason: "Success", - Message: "spec.tls is valid: no TLS configuration provided", + Message: "spec.tls is valid: no TLS configuration provided: using default root CA bundle from container image", } } sadTLSConfigurationValid := func(time metav1.Time, observedGeneration int64) metav1.Condition { diff --git a/internal/controller/supervisorconfig/activedirectoryupstreamwatcher/active_directory_upstream_watcher_test.go b/internal/controller/supervisorconfig/activedirectoryupstreamwatcher/active_directory_upstream_watcher_test.go index 4355e9a64..320ecbb06 100644 --- a/internal/controller/supervisorconfig/activedirectoryupstreamwatcher/active_directory_upstream_watcher_test.go +++ b/internal/controller/supervisorconfig/activedirectoryupstreamwatcher/active_directory_upstream_watcher_test.go @@ -440,7 +440,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { bindSecretValidTrueCondition(gen), activeDirectoryConnectionValidTrueCondition(gen, secretVersion), searchBaseFoundInConfigCondition(gen), - tlsConfigurationValidLoadedTrueCondition(gen, "loaded TLS configuration"), + tlsConfigurationValidLoadedTrueCondition(gen, "using configured CA bundle"), } } @@ -663,7 +663,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { Message: fmt.Sprintf(`secret "%s" not found`, testBindSecretName), ObservedGeneration: 1234, }, - tlsConfigurationValidLoadedTrueCondition(1234, "loaded TLS configuration"), + tlsConfigurationValidLoadedTrueCondition(1234, "using configured CA bundle"), }, }, }}, @@ -691,7 +691,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { Message: fmt.Sprintf(`referenced Secret "%s" has wrong type "some-other-type" (should be "kubernetes.io/basic-auth")`, testBindSecretName), ObservedGeneration: 1234, }, - tlsConfigurationValidLoadedTrueCondition(1234, "loaded TLS configuration"), + tlsConfigurationValidLoadedTrueCondition(1234, "using configured CA bundle"), }, }, }}, @@ -718,7 +718,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { Message: fmt.Sprintf(`referenced Secret "%s" is missing required keys ["username" "password"]`, testBindSecretName), ObservedGeneration: 1234, }, - tlsConfigurationValidLoadedTrueCondition(1234, "loaded TLS configuration"), + tlsConfigurationValidLoadedTrueCondition(1234, "using configured CA bundle"), }, }, }}, @@ -828,7 +828,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { Status: "True", LastTransitionTime: now, Reason: "Success", - Message: "spec.tls is valid: no TLS configuration provided", + Message: "spec.tls is valid: no TLS configuration provided: using default root CA bundle from container image", ObservedGeneration: 1234, }, }, @@ -899,7 +899,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { Status: "True", LastTransitionTime: now, Reason: "Success", - Message: "spec.tls is valid: no TLS configuration provided", + Message: "spec.tls is valid: no TLS configuration provided: using default root CA bundle from container image", ObservedGeneration: 1234, }, }, @@ -977,7 +977,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { ObservedGeneration: 1234, }, searchBaseFoundInConfigCondition(1234), - tlsConfigurationValidLoadedTrueCondition(1234, "loaded TLS configuration"), + tlsConfigurationValidLoadedTrueCondition(1234, "using configured CA bundle"), }, }, }}, @@ -1059,7 +1059,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { ObservedGeneration: 1234, }, searchBaseFoundInConfigCondition(1234), - tlsConfigurationValidLoadedTrueCondition(1234, "loaded TLS configuration"), + tlsConfigurationValidLoadedTrueCondition(1234, "using configured CA bundle"), }, }, }}, @@ -1113,7 +1113,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { bindSecretValidTrueCondition(1234), activeDirectoryConnectionValidTrueCondition(1234, "4242"), searchBaseFoundInConfigCondition(1234), - tlsConfigurationValidLoadedTrueCondition(1234, "no TLS configuration provided"), + tlsConfigurationValidLoadedTrueCondition(1234, "no TLS configuration provided: using default root CA bundle from container image"), }, }, }}, @@ -1158,7 +1158,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { Message: fmt.Sprintf(`secret "%s" not found`, "non-existent-secret"), ObservedGeneration: 42, }, - tlsConfigurationValidLoadedTrueCondition(42, "loaded TLS configuration"), + tlsConfigurationValidLoadedTrueCondition(42, "using configured CA bundle"), }, }, }, @@ -1212,7 +1212,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { ObservedGeneration: 1234, }, searchBaseFoundInConfigCondition(1234), - tlsConfigurationValidLoadedTrueCondition(1234, "loaded TLS configuration"), + tlsConfigurationValidLoadedTrueCondition(1234, "using configured CA bundle"), }, }, }}, @@ -1280,7 +1280,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { ObservedGeneration: 1234, }, searchBaseFoundInRootDSECondition(1234), - tlsConfigurationValidLoadedTrueCondition(1234, "loaded TLS configuration"), + tlsConfigurationValidLoadedTrueCondition(1234, "using configured CA bundle"), }, }, }}, @@ -1316,7 +1316,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { ObservedGeneration: 1234, }, searchBaseFoundErrorCondition(1234, "Error finding search base: error binding as \"test-bind-username\" before querying for defaultNamingContext: some bind error"), - tlsConfigurationValidLoadedTrueCondition(1234, "loaded TLS configuration"), + tlsConfigurationValidLoadedTrueCondition(1234, "using configured CA bundle"), }, }, }}, @@ -1422,7 +1422,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { bindSecretValidTrueCondition(1234), activeDirectoryConnectionValidTrueCondition(1234, "4242"), searchBaseFoundInRootDSECondition(1234), - tlsConfigurationValidLoadedTrueCondition(1234, "loaded TLS configuration"), + tlsConfigurationValidLoadedTrueCondition(1234, "using configured CA bundle"), }, }, }}, @@ -1497,7 +1497,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { bindSecretValidTrueCondition(1234), activeDirectoryConnectionValidTrueCondition(1234, "4242"), searchBaseFoundInRootDSECondition(1234), - tlsConfigurationValidLoadedTrueCondition(1234, "loaded TLS configuration"), + tlsConfigurationValidLoadedTrueCondition(1234, "using configured CA bundle"), }, }, }}, @@ -1835,7 +1835,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { bindSecretValidTrueCondition(1234), activeDirectoryConnectionValidTrueCondition(1234, "4242"), searchBaseFoundInRootDSECondition(1234), - tlsConfigurationValidLoadedTrueCondition(1234, "loaded TLS configuration"), + tlsConfigurationValidLoadedTrueCondition(1234, "using configured CA bundle"), }, }, }}, @@ -1900,7 +1900,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { bindSecretValidTrueCondition(1234), activeDirectoryConnectionValidTrueCondition(1234, "4242"), searchBaseFoundInRootDSECondition(1234), - tlsConfigurationValidLoadedTrueCondition(1234, "loaded TLS configuration"), + tlsConfigurationValidLoadedTrueCondition(1234, "using configured CA bundle"), }, }, }}, @@ -1965,7 +1965,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { bindSecretValidTrueCondition(1234), activeDirectoryConnectionValidTrueCondition(1234, "4242"), searchBaseFoundInRootDSECondition(1234), - tlsConfigurationValidLoadedTrueCondition(1234, "loaded TLS configuration"), + tlsConfigurationValidLoadedTrueCondition(1234, "using configured CA bundle"), }, }, }}, @@ -2002,7 +2002,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { bindSecretValidTrueCondition(1234), activeDirectoryConnectionValidTrueCondition(1234, "4242"), searchBaseFoundErrorCondition(1234, "Error finding search base: error querying RootDSE for defaultNamingContext: some error"), - tlsConfigurationValidLoadedTrueCondition(1234, "loaded TLS configuration"), + tlsConfigurationValidLoadedTrueCondition(1234, "using configured CA bundle"), }, }, }}, @@ -2038,7 +2038,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { bindSecretValidTrueCondition(1234), activeDirectoryConnectionValidTrueCondition(1234, "4242"), searchBaseFoundErrorCondition(1234, "Error finding search base: error querying RootDSE for defaultNamingContext: empty search base DN found"), - tlsConfigurationValidLoadedTrueCondition(1234, "loaded TLS configuration"), + tlsConfigurationValidLoadedTrueCondition(1234, "using configured CA bundle"), }, }, }}, @@ -2080,7 +2080,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { bindSecretValidTrueCondition(1234), activeDirectoryConnectionValidTrueCondition(1234, "4242"), searchBaseFoundErrorCondition(1234, "Error finding search base: error querying RootDSE for defaultNamingContext: expected to find 1 entry but found 2"), - tlsConfigurationValidLoadedTrueCondition(1234, "loaded TLS configuration"), + tlsConfigurationValidLoadedTrueCondition(1234, "using configured CA bundle"), }, }, }}, @@ -2109,7 +2109,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { bindSecretValidTrueCondition(1234), activeDirectoryConnectionValidTrueCondition(1234, "4242"), searchBaseFoundErrorCondition(1234, "Error finding search base: error querying RootDSE for defaultNamingContext: expected to find 1 entry but found 0"), - tlsConfigurationValidLoadedTrueCondition(1234, "loaded TLS configuration"), + tlsConfigurationValidLoadedTrueCondition(1234, "using configured CA bundle"), }, }, }}, @@ -2179,7 +2179,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { bindSecretValidTrueCondition(1234), activeDirectoryConnectionValidTrueCondition(1234, "4242"), searchBaseFoundInRootDSECondition(1234), - tlsConfigurationValidLoadedTrueCondition(1234, "loaded TLS configuration"), + tlsConfigurationValidLoadedTrueCondition(1234, "using configured CA bundle"), }, }, }}, @@ -2248,7 +2248,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { Status: "True", LastTransitionTime: now, Reason: "Success", - Message: "spec.tls is valid: loaded TLS configuration", + Message: "spec.tls is valid: using configured CA bundle", ObservedGeneration: 1234, }, }, diff --git a/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher_test.go b/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher_test.go index c1fb81400..437c5620a 100644 --- a/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher_test.go +++ b/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher_test.go @@ -228,7 +228,7 @@ func TestController(t *testing.T) { buildTLSConfigurationValidTrue := func(t *testing.T) metav1.Condition { t.Helper() - return buildTLSConfigurationValidTrueWithMsg(t, "loaded TLS configuration") + return buildTLSConfigurationValidTrueWithMsg(t, "using configured CA bundle") } buildTLSConfigurationValidFalse := func(t *testing.T, message string) metav1.Condition { @@ -477,7 +477,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidTrue("some-idp-name"), buildLogForUpdatingOrganizationPolicyValid("some-idp-name", "True", "Success", fmt.Sprintf(`spec.allowAuthentication.organizations.policy (\"%s\") is valid`, string(*validFilledOutIDP.Spec.AllowAuthentication.Organizations.Policy))), buildLogForUpdatingHostValid("some-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is valid`, *validFilledOutIDP.Spec.GitHubAPI.Host), - buildLogForUpdatingTLSConfigurationValid("some-idp-name", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), + buildLogForUpdatingTLSConfigurationValid("some-idp-name", "True", "Success", "spec.githubAPI.tls is valid: using configured CA bundle"), buildLogForUpdatingGitHubConnectionValid("some-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is reachable and TLS verification succeeds`, *validFilledOutIDP.Spec.GitHubAPI.Host), buildLogForUpdatingPhase("some-idp-name", "Ready"), }, @@ -539,7 +539,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidTrue("minimal-idp-name"), buildLogForUpdatingOrganizationPolicyValid("minimal-idp-name", "True", "Success", fmt.Sprintf(`spec.allowAuthentication.organizations.policy (\"%s\") is valid`, string(*validMinimalIDP.Spec.AllowAuthentication.Organizations.Policy))), buildLogForUpdatingHostValid("minimal-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is valid`, *validMinimalIDP.Spec.GitHubAPI.Host), - buildLogForUpdatingTLSConfigurationValid("minimal-idp-name", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), + buildLogForUpdatingTLSConfigurationValid("minimal-idp-name", "True", "Success", "spec.githubAPI.tls is valid: using configured CA bundle"), buildLogForUpdatingGitHubConnectionValid("minimal-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is reachable and TLS verification succeeds`, *validMinimalIDP.Spec.GitHubAPI.Host), buildLogForUpdatingPhase("minimal-idp-name", "Ready"), }, @@ -622,7 +622,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidTrue("minimal-idp-name"), buildLogForUpdatingOrganizationPolicyValid("minimal-idp-name", "True", "Success", fmt.Sprintf(`spec.allowAuthentication.organizations.policy (\"%s\") is valid`, string(*validMinimalIDP.Spec.AllowAuthentication.Organizations.Policy))), buildLogForUpdatingHostValid("minimal-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is valid`, "github.com"), - buildLogForUpdatingTLSConfigurationValid("minimal-idp-name", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), + buildLogForUpdatingTLSConfigurationValid("minimal-idp-name", "True", "Success", "spec.githubAPI.tls is valid: using configured CA bundle"), buildLogForUpdatingGitHubConnectionValid("minimal-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is reachable and TLS verification succeeds`, "github.com:443"), buildLogForUpdatingPhase("minimal-idp-name", "Ready"), }, @@ -699,7 +699,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidTrue("minimal-idp-name"), buildLogForUpdatingOrganizationPolicyValid("minimal-idp-name", "True", "Success", fmt.Sprintf(`spec.allowAuthentication.organizations.policy (\"%s\") is valid`, string(*validMinimalIDP.Spec.AllowAuthentication.Organizations.Policy))), buildLogForUpdatingHostValid("minimal-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is valid`, goodServerIPv6Domain), - buildLogForUpdatingTLSConfigurationValid("minimal-idp-name", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), + buildLogForUpdatingTLSConfigurationValid("minimal-idp-name", "True", "Success", "spec.githubAPI.tls is valid: using configured CA bundle"), buildLogForUpdatingGitHubConnectionValid("minimal-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is reachable and TLS verification succeeds`, goodServerIPv6Domain), buildLogForUpdatingPhase("minimal-idp-name", "Ready"), }, @@ -859,7 +859,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidTrue("invalid-idp-name"), buildLogForUpdatingOrganizationPolicyValid("invalid-idp-name", "True", "Success", fmt.Sprintf(`spec.allowAuthentication.organizations.policy (\"%s\") is valid`, string(*validFilledOutIDP.Spec.AllowAuthentication.Organizations.Policy))), buildLogForUpdatingHostValid("invalid-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is valid`, *validFilledOutIDP.Spec.GitHubAPI.Host), - buildLogForUpdatingTLSConfigurationValid("invalid-idp-name", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), + buildLogForUpdatingTLSConfigurationValid("invalid-idp-name", "True", "Success", "spec.githubAPI.tls is valid: using configured CA bundle"), buildLogForUpdatingGitHubConnectionValid("invalid-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is reachable and TLS verification succeeds`, *validFilledOutIDP.Spec.GitHubAPI.Host), buildLogForUpdatingPhase("invalid-idp-name", "Error"), @@ -867,7 +867,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidTrue("other-idp-name"), buildLogForUpdatingOrganizationPolicyValid("other-idp-name", "True", "Success", fmt.Sprintf(`spec.allowAuthentication.organizations.policy (\"%s\") is valid`, string(*validFilledOutIDP.Spec.AllowAuthentication.Organizations.Policy))), buildLogForUpdatingHostValid("other-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is valid`, *validFilledOutIDP.Spec.GitHubAPI.Host), - buildLogForUpdatingTLSConfigurationValid("other-idp-name", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), + buildLogForUpdatingTLSConfigurationValid("other-idp-name", "True", "Success", "spec.githubAPI.tls is valid: using configured CA bundle"), buildLogForUpdatingGitHubConnectionValid("other-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is reachable and TLS verification succeeds`, *validFilledOutIDP.Spec.GitHubAPI.Host), buildLogForUpdatingPhase("other-idp-name", "Ready"), @@ -875,7 +875,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidTrue("some-idp-name"), buildLogForUpdatingOrganizationPolicyValid("some-idp-name", "True", "Success", fmt.Sprintf(`spec.allowAuthentication.organizations.policy (\"%s\") is valid`, string(*validFilledOutIDP.Spec.AllowAuthentication.Organizations.Policy))), buildLogForUpdatingHostValid("some-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is valid`, *validFilledOutIDP.Spec.GitHubAPI.Host), - buildLogForUpdatingTLSConfigurationValid("some-idp-name", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), + buildLogForUpdatingTLSConfigurationValid("some-idp-name", "True", "Success", "spec.githubAPI.tls is valid: using configured CA bundle"), buildLogForUpdatingGitHubConnectionValid("some-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is reachable and TLS verification succeeds`, *validFilledOutIDP.Spec.GitHubAPI.Host), buildLogForUpdatingPhase("some-idp-name", "Ready"), }, @@ -1030,7 +1030,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidTrue("idp-with-tls-in-config-map"), buildLogForUpdatingOrganizationPolicyValid("idp-with-tls-in-config-map", "True", "Success", fmt.Sprintf(`spec.allowAuthentication.organizations.policy (\"%s\") is valid`, string(*validFilledOutIDP.Spec.AllowAuthentication.Organizations.Policy))), buildLogForUpdatingHostValid("idp-with-tls-in-config-map", "True", "Success", `spec.githubAPI.host (\"%s\") is valid`, *validFilledOutIDP.Spec.GitHubAPI.Host), - buildLogForUpdatingTLSConfigurationValid("idp-with-tls-in-config-map", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), + buildLogForUpdatingTLSConfigurationValid("idp-with-tls-in-config-map", "True", "Success", "spec.githubAPI.tls is valid: using configured CA bundle"), buildLogForUpdatingGitHubConnectionValid("idp-with-tls-in-config-map", "True", "Success", `spec.githubAPI.host (\"%s\") is reachable and TLS verification succeeds`, *validFilledOutIDP.Spec.GitHubAPI.Host), buildLogForUpdatingPhase("idp-with-tls-in-config-map", "Ready"), @@ -1038,7 +1038,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidTrue("idp-with-tls-in-secret"), buildLogForUpdatingOrganizationPolicyValid("idp-with-tls-in-secret", "True", "Success", fmt.Sprintf(`spec.allowAuthentication.organizations.policy (\"%s\") is valid`, string(*validFilledOutIDP.Spec.AllowAuthentication.Organizations.Policy))), buildLogForUpdatingHostValid("idp-with-tls-in-secret", "True", "Success", `spec.githubAPI.host (\"%s\") is valid`, *validFilledOutIDP.Spec.GitHubAPI.Host), - buildLogForUpdatingTLSConfigurationValid("idp-with-tls-in-secret", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), + buildLogForUpdatingTLSConfigurationValid("idp-with-tls-in-secret", "True", "Success", "spec.githubAPI.tls is valid: using configured CA bundle"), buildLogForUpdatingGitHubConnectionValid("idp-with-tls-in-secret", "True", "Success", `spec.githubAPI.host (\"%s\") is reachable and TLS verification succeeds`, *validFilledOutIDP.Spec.GitHubAPI.Host), buildLogForUpdatingPhase("idp-with-tls-in-secret", "Ready"), }, @@ -1113,7 +1113,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidTrue("some-idp-name"), buildLogForUpdatingOrganizationPolicyValid("some-idp-name", "True", "Success", fmt.Sprintf(`spec.allowAuthentication.organizations.policy (\"%s\") is valid`, string(*validFilledOutIDP.Spec.AllowAuthentication.Organizations.Policy))), buildLogForUpdatingHostValid("some-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is valid`, *validFilledOutIDP.Spec.GitHubAPI.Host), - buildLogForUpdatingTLSConfigurationValid("some-idp-name", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), + buildLogForUpdatingTLSConfigurationValid("some-idp-name", "True", "Success", "spec.githubAPI.tls is valid: using configured CA bundle"), buildLogForUpdatingGitHubConnectionValid("some-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is reachable and TLS verification succeeds`, *validFilledOutIDP.Spec.GitHubAPI.Host), buildLogForUpdatingPhase("some-idp-name", "Ready"), }, @@ -1154,7 +1154,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidTrue("some-idp-name"), buildLogForUpdatingOrganizationPolicyValid("some-idp-name", "True", "Success", fmt.Sprintf(`spec.allowAuthentication.organizations.policy (\"%s\") is valid`, string(*validFilledOutIDP.Spec.AllowAuthentication.Organizations.Policy))), buildLogForUpdatingHostValid("some-idp-name", "False", "InvalidHost", `spec.githubAPI.host (\"%s\") is not valid: must not be empty`, ""), - buildLogForUpdatingTLSConfigurationValid("some-idp-name", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), + buildLogForUpdatingTLSConfigurationValid("some-idp-name", "True", "Success", "spec.githubAPI.tls is valid: using configured CA bundle"), buildLogForUpdatingGitHubConnectionValidUnknown("some-idp-name"), buildLogForUpdatingPhase("some-idp-name", "Error"), }, @@ -1195,7 +1195,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidTrue("minimal-idp-name"), buildLogForUpdatingOrganizationPolicyValid("minimal-idp-name", "True", "Success", fmt.Sprintf(`spec.allowAuthentication.organizations.policy (\"%s\") is valid`, string(*validMinimalIDP.Spec.AllowAuthentication.Organizations.Policy))), buildLogForUpdatingHostValid("minimal-idp-name", "False", "InvalidHost", `spec.githubAPI.host (\"%s\") is not valid: invalid port \"//example.com\"`, "https://example.com"), - buildLogForUpdatingTLSConfigurationValid("minimal-idp-name", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), + buildLogForUpdatingTLSConfigurationValid("minimal-idp-name", "True", "Success", "spec.githubAPI.tls is valid: using configured CA bundle"), buildLogForUpdatingGitHubConnectionValidUnknown("minimal-idp-name"), buildLogForUpdatingPhase("minimal-idp-name", "Error"), }, @@ -1236,7 +1236,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidTrue("minimal-idp-name"), buildLogForUpdatingOrganizationPolicyValid("minimal-idp-name", "True", "Success", fmt.Sprintf(`spec.allowAuthentication.organizations.policy (\"%s\") is valid`, string(*validMinimalIDP.Spec.AllowAuthentication.Organizations.Policy))), buildLogForUpdatingHostValid("minimal-idp-name", "False", "InvalidHost", `spec.githubAPI.host (\"%s\") is not valid: host \"example.com/foo\" is not a valid hostname or IP address`, "example.com/foo"), - buildLogForUpdatingTLSConfigurationValid("minimal-idp-name", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), + buildLogForUpdatingTLSConfigurationValid("minimal-idp-name", "True", "Success", "spec.githubAPI.tls is valid: using configured CA bundle"), buildLogForUpdatingGitHubConnectionValidUnknown("minimal-idp-name"), buildLogForUpdatingPhase("minimal-idp-name", "Error"), }, @@ -1277,7 +1277,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidTrue("minimal-idp-name"), buildLogForUpdatingOrganizationPolicyValid("minimal-idp-name", "True", "Success", fmt.Sprintf(`spec.allowAuthentication.organizations.policy (\"%s\") is valid`, string(*validMinimalIDP.Spec.AllowAuthentication.Organizations.Policy))), buildLogForUpdatingHostValid("minimal-idp-name", "False", "InvalidHost", `spec.githubAPI.host (\"%s\") is not valid: invalid port \"p@example.com\"`, "u:p@example.com"), - buildLogForUpdatingTLSConfigurationValid("minimal-idp-name", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), + buildLogForUpdatingTLSConfigurationValid("minimal-idp-name", "True", "Success", "spec.githubAPI.tls is valid: using configured CA bundle"), buildLogForUpdatingGitHubConnectionValidUnknown("minimal-idp-name"), buildLogForUpdatingPhase("minimal-idp-name", "Error"), }, @@ -1318,7 +1318,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidTrue("minimal-idp-name"), buildLogForUpdatingOrganizationPolicyValid("minimal-idp-name", "True", "Success", fmt.Sprintf(`spec.allowAuthentication.organizations.policy (\"%s\") is valid`, string(*validMinimalIDP.Spec.AllowAuthentication.Organizations.Policy))), buildLogForUpdatingHostValid("minimal-idp-name", "False", "InvalidHost", `spec.githubAPI.host (\"%s\") is not valid: host \"example.com?a=b\" is not a valid hostname or IP address`, "example.com?a=b"), - buildLogForUpdatingTLSConfigurationValid("minimal-idp-name", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), + buildLogForUpdatingTLSConfigurationValid("minimal-idp-name", "True", "Success", "spec.githubAPI.tls is valid: using configured CA bundle"), buildLogForUpdatingGitHubConnectionValidUnknown("minimal-idp-name"), buildLogForUpdatingPhase("minimal-idp-name", "Error"), }, @@ -1359,7 +1359,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidTrue("minimal-idp-name"), buildLogForUpdatingOrganizationPolicyValid("minimal-idp-name", "True", "Success", fmt.Sprintf(`spec.allowAuthentication.organizations.policy (\"%s\") is valid`, string(*validMinimalIDP.Spec.AllowAuthentication.Organizations.Policy))), buildLogForUpdatingHostValid("minimal-idp-name", "False", "InvalidHost", `spec.githubAPI.host (\"%s\") is not valid: host \"example.com#a\" is not a valid hostname or IP address`, "example.com#a"), - buildLogForUpdatingTLSConfigurationValid("minimal-idp-name", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), + buildLogForUpdatingTLSConfigurationValid("minimal-idp-name", "True", "Success", "spec.githubAPI.tls is valid: using configured CA bundle"), buildLogForUpdatingGitHubConnectionValidUnknown("minimal-idp-name"), buildLogForUpdatingPhase("minimal-idp-name", "Error"), }, @@ -1446,7 +1446,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidTrue("minimal-idp-name"), buildLogForUpdatingOrganizationPolicyValid("minimal-idp-name", "True", "Success", fmt.Sprintf(`spec.allowAuthentication.organizations.policy (\"%s\") is valid`, string(*validMinimalIDP.Spec.AllowAuthentication.Organizations.Policy))), buildLogForUpdatingHostValid("minimal-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is valid`, "nowhere.bad-tld"), - buildLogForUpdatingTLSConfigurationValid("minimal-idp-name", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), + buildLogForUpdatingTLSConfigurationValid("minimal-idp-name", "True", "Success", "spec.githubAPI.tls is valid: using configured CA bundle"), buildLogForUpdatingGitHubConnectionValid("minimal-idp-name", "False", "UnableToDialServer", `cannot dial server spec.githubAPI.host (\"%s\"): dial tcp: lookup nowhere.bad-tld: no such host`, "nowhere.bad-tld:443"), buildLogForUpdatingPhase("minimal-idp-name", "Error"), }, @@ -1487,7 +1487,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidTrue("minimal-idp-name"), buildLogForUpdatingOrganizationPolicyValid("minimal-idp-name", "True", "Success", fmt.Sprintf(`spec.allowAuthentication.organizations.policy (\"%s\") is valid`, string(*validMinimalIDP.Spec.AllowAuthentication.Organizations.Policy))), buildLogForUpdatingHostValid("minimal-idp-name", "False", "InvalidHost", `spec.githubAPI.host (\"0:0:0:0:0:0:0:1:9876\") is not valid: host \"%s\" is not a valid hostname or IP address`, "0:0:0:0:0:0:0:1:9876"), - buildLogForUpdatingTLSConfigurationValid("minimal-idp-name", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), + buildLogForUpdatingTLSConfigurationValid("minimal-idp-name", "True", "Success", "spec.githubAPI.tls is valid: using configured CA bundle"), buildLogForUpdatingGitHubConnectionValidUnknown("minimal-idp-name"), buildLogForUpdatingPhase("minimal-idp-name", "Error"), }, @@ -1519,7 +1519,7 @@ func TestController(t *testing.T) { buildGitHubConnectionValidFalse(t, fmt.Sprintf(`cannot dial server spec.githubAPI.host (%q): tls: failed to verify certificate: x509: certificate signed by unknown authority`, *validFilledOutIDP.Spec.GitHubAPI.Host)), buildHostValidTrue(t, *validFilledOutIDP.Spec.GitHubAPI.Host), buildOrganizationsPolicyValidTrue(t, *validFilledOutIDP.Spec.AllowAuthentication.Organizations.Policy), - buildTLSConfigurationValidTrueWithMsg(t, "no TLS configuration provided"), + buildTLSConfigurationValidTrueWithMsg(t, "no TLS configuration provided: using default root CA bundle from container image"), }, }, }, @@ -1529,7 +1529,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidTrue("some-idp-name"), buildLogForUpdatingOrganizationPolicyValid("some-idp-name", "True", "Success", fmt.Sprintf(`spec.allowAuthentication.organizations.policy (\"%s\") is valid`, string(*validFilledOutIDP.Spec.AllowAuthentication.Organizations.Policy))), buildLogForUpdatingHostValid("some-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is valid`, *validFilledOutIDP.Spec.GitHubAPI.Host), - buildLogForUpdatingTLSConfigurationValid("some-idp-name", "True", "Success", "spec.githubAPI.tls is valid: no TLS configuration provided"), + buildLogForUpdatingTLSConfigurationValid("some-idp-name", "True", "Success", "spec.githubAPI.tls is valid: no TLS configuration provided: using default root CA bundle from container image"), buildLogForUpdatingGitHubConnectionValid("some-idp-name", "False", "UnableToDialServer", `cannot dial server spec.githubAPI.host (\"%s\"): tls: failed to verify certificate: x509: certificate signed by unknown authority`, *validFilledOutIDP.Spec.GitHubAPI.Host), buildLogForUpdatingPhase("some-idp-name", "Error"), }, @@ -1575,7 +1575,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidTrue("some-idp-name"), buildLogForUpdatingOrganizationPolicyValid("some-idp-name", "True", "Success", fmt.Sprintf(`spec.allowAuthentication.organizations.policy (\"%s\") is valid`, string(*validFilledOutIDP.Spec.AllowAuthentication.Organizations.Policy))), buildLogForUpdatingHostValid("some-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is valid`, *validFilledOutIDP.Spec.GitHubAPI.Host), - buildLogForUpdatingTLSConfigurationValid("some-idp-name", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), + buildLogForUpdatingTLSConfigurationValid("some-idp-name", "True", "Success", "spec.githubAPI.tls is valid: using configured CA bundle"), buildLogForUpdatingGitHubConnectionValid("some-idp-name", "False", "UnableToDialServer", `cannot dial server spec.githubAPI.host (\"%s\"): tls: failed to verify certificate: x509: certificate signed by unknown authority`, *validFilledOutIDP.Spec.GitHubAPI.Host), buildLogForUpdatingPhase("some-idp-name", "Error"), }, @@ -1616,7 +1616,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidTrue("some-idp-name"), buildLogForUpdatingOrganizationPolicyValid("some-idp-name", "False", "Invalid", "spec.allowAuthentication.organizations.policy must be 'OnlyUsersFromAllowedOrganizations' when spec.allowAuthentication.organizations.allowed has organizations listed"), buildLogForUpdatingHostValid("some-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is valid`, *validFilledOutIDP.Spec.GitHubAPI.Host), - buildLogForUpdatingTLSConfigurationValid("some-idp-name", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), + buildLogForUpdatingTLSConfigurationValid("some-idp-name", "True", "Success", "spec.githubAPI.tls is valid: using configured CA bundle"), buildLogForUpdatingGitHubConnectionValid("some-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is reachable and TLS verification succeeds`, *validFilledOutIDP.Spec.GitHubAPI.Host), buildLogForUpdatingPhase("some-idp-name", "Error"), }, @@ -1657,7 +1657,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidTrue("some-idp-name"), buildLogForUpdatingOrganizationPolicyValid("some-idp-name", "False", "Invalid", "spec.allowAuthentication.organizations.policy must be 'OnlyUsersFromAllowedOrganizations' when spec.allowAuthentication.organizations.allowed has organizations listed"), buildLogForUpdatingHostValid("some-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is valid`, *validFilledOutIDP.Spec.GitHubAPI.Host), - buildLogForUpdatingTLSConfigurationValid("some-idp-name", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), + buildLogForUpdatingTLSConfigurationValid("some-idp-name", "True", "Success", "spec.githubAPI.tls is valid: using configured CA bundle"), buildLogForUpdatingGitHubConnectionValid("some-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is reachable and TLS verification succeeds`, *validFilledOutIDP.Spec.GitHubAPI.Host), buildLogForUpdatingPhase("some-idp-name", "Error"), }, @@ -1698,7 +1698,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidTrue("some-idp-name"), buildLogForUpdatingOrganizationPolicyValid("some-idp-name", "False", "Invalid", "spec.allowAuthentication.organizations.policy must be 'OnlyUsersFromAllowedOrganizations' when spec.allowAuthentication.organizations.allowed has organizations listed"), buildLogForUpdatingHostValid("some-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is valid`, *validFilledOutIDP.Spec.GitHubAPI.Host), - buildLogForUpdatingTLSConfigurationValid("some-idp-name", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), + buildLogForUpdatingTLSConfigurationValid("some-idp-name", "True", "Success", "spec.githubAPI.tls is valid: using configured CA bundle"), buildLogForUpdatingGitHubConnectionValid("some-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is reachable and TLS verification succeeds`, *validFilledOutIDP.Spec.GitHubAPI.Host), buildLogForUpdatingPhase("some-idp-name", "Error"), }, @@ -1739,7 +1739,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidTrue("some-idp-name"), buildLogForUpdatingOrganizationPolicyValid("some-idp-name", "False", "Invalid", "spec.allowAuthentication.organizations.policy must be 'AllGitHubUsers' when spec.allowAuthentication.organizations.allowed is empty"), buildLogForUpdatingHostValid("some-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is valid`, *validFilledOutIDP.Spec.GitHubAPI.Host), - buildLogForUpdatingTLSConfigurationValid("some-idp-name", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), + buildLogForUpdatingTLSConfigurationValid("some-idp-name", "True", "Success", "spec.githubAPI.tls is valid: using configured CA bundle"), buildLogForUpdatingGitHubConnectionValid("some-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is reachable and TLS verification succeeds`, *validFilledOutIDP.Spec.GitHubAPI.Host), buildLogForUpdatingPhase("some-idp-name", "Error"), }, @@ -1780,7 +1780,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidFalse("some-idp-name", "spec.claims.username is required"), buildLogForUpdatingOrganizationPolicyValid("some-idp-name", "True", "Success", fmt.Sprintf(`spec.allowAuthentication.organizations.policy (\"%s\") is valid`, string(*validFilledOutIDP.Spec.AllowAuthentication.Organizations.Policy))), buildLogForUpdatingHostValid("some-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is valid`, *validFilledOutIDP.Spec.GitHubAPI.Host), - buildLogForUpdatingTLSConfigurationValid("some-idp-name", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), + buildLogForUpdatingTLSConfigurationValid("some-idp-name", "True", "Success", "spec.githubAPI.tls is valid: using configured CA bundle"), buildLogForUpdatingGitHubConnectionValid("some-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is reachable and TLS verification succeeds`, *validFilledOutIDP.Spec.GitHubAPI.Host), buildLogForUpdatingPhase("some-idp-name", "Error"), }, @@ -1821,7 +1821,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidFalse("some-idp-name", `spec.claims.username (\"a\") is not valid`), buildLogForUpdatingOrganizationPolicyValid("some-idp-name", "True", "Success", fmt.Sprintf(`spec.allowAuthentication.organizations.policy (\"%s\") is valid`, string(*validFilledOutIDP.Spec.AllowAuthentication.Organizations.Policy))), buildLogForUpdatingHostValid("some-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is valid`, *validFilledOutIDP.Spec.GitHubAPI.Host), - buildLogForUpdatingTLSConfigurationValid("some-idp-name", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), + buildLogForUpdatingTLSConfigurationValid("some-idp-name", "True", "Success", "spec.githubAPI.tls is valid: using configured CA bundle"), buildLogForUpdatingGitHubConnectionValid("some-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is reachable and TLS verification succeeds`, *validFilledOutIDP.Spec.GitHubAPI.Host), buildLogForUpdatingPhase("some-idp-name", "Error"), }, @@ -1862,7 +1862,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidFalse("some-idp-name", "spec.claims.groups is required"), buildLogForUpdatingOrganizationPolicyValid("some-idp-name", "True", "Success", fmt.Sprintf(`spec.allowAuthentication.organizations.policy (\"%s\") is valid`, string(*validFilledOutIDP.Spec.AllowAuthentication.Organizations.Policy))), buildLogForUpdatingHostValid("some-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is valid`, *validFilledOutIDP.Spec.GitHubAPI.Host), - buildLogForUpdatingTLSConfigurationValid("some-idp-name", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), + buildLogForUpdatingTLSConfigurationValid("some-idp-name", "True", "Success", "spec.githubAPI.tls is valid: using configured CA bundle"), buildLogForUpdatingGitHubConnectionValid("some-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is reachable and TLS verification succeeds`, *validFilledOutIDP.Spec.GitHubAPI.Host), buildLogForUpdatingPhase("some-idp-name", "Error"), }, @@ -1903,7 +1903,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidFalse("some-idp-name", `spec.claims.groups (\"b\") is not valid`), buildLogForUpdatingOrganizationPolicyValid("some-idp-name", "True", "Success", fmt.Sprintf(`spec.allowAuthentication.organizations.policy (\"%s\") is valid`, string(*validFilledOutIDP.Spec.AllowAuthentication.Organizations.Policy))), buildLogForUpdatingHostValid("some-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is valid`, *validFilledOutIDP.Spec.GitHubAPI.Host), - buildLogForUpdatingTLSConfigurationValid("some-idp-name", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), + buildLogForUpdatingTLSConfigurationValid("some-idp-name", "True", "Success", "spec.githubAPI.tls is valid: using configured CA bundle"), buildLogForUpdatingGitHubConnectionValid("some-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is reachable and TLS verification succeeds`, *validFilledOutIDP.Spec.GitHubAPI.Host), buildLogForUpdatingPhase("some-idp-name", "Error"), }, @@ -1946,7 +1946,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidTrue("minimal-idp-name"), buildLogForUpdatingOrganizationPolicyValid("minimal-idp-name", "True", "Success", fmt.Sprintf(`spec.allowAuthentication.organizations.policy (\"%s\") is valid`, string(*validMinimalIDP.Spec.AllowAuthentication.Organizations.Policy))), buildLogForUpdatingHostValid("minimal-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is valid`, *validFilledOutIDP.Spec.GitHubAPI.Host), - buildLogForUpdatingTLSConfigurationValid("minimal-idp-name", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), + buildLogForUpdatingTLSConfigurationValid("minimal-idp-name", "True", "Success", "spec.githubAPI.tls is valid: using configured CA bundle"), buildLogForUpdatingGitHubConnectionValid("minimal-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is reachable and TLS verification succeeds`, *validFilledOutIDP.Spec.GitHubAPI.Host), buildLogForUpdatingPhase("minimal-idp-name", "Error"), }, @@ -1989,7 +1989,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidTrue("minimal-idp-name"), buildLogForUpdatingOrganizationPolicyValid("minimal-idp-name", "True", "Success", fmt.Sprintf(`spec.allowAuthentication.organizations.policy (\"%s\") is valid`, string(*validMinimalIDP.Spec.AllowAuthentication.Organizations.Policy))), buildLogForUpdatingHostValid("minimal-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is valid`, *validMinimalIDP.Spec.GitHubAPI.Host), - buildLogForUpdatingTLSConfigurationValid("minimal-idp-name", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), + buildLogForUpdatingTLSConfigurationValid("minimal-idp-name", "True", "Success", "spec.githubAPI.tls is valid: using configured CA bundle"), buildLogForUpdatingGitHubConnectionValid("minimal-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is reachable and TLS verification succeeds`, *validMinimalIDP.Spec.GitHubAPI.Host), buildLogForUpdatingPhase("minimal-idp-name", "Error"), }, @@ -2032,7 +2032,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidTrue("minimal-idp-name"), buildLogForUpdatingOrganizationPolicyValid("minimal-idp-name", "True", "Success", fmt.Sprintf(`spec.allowAuthentication.organizations.policy (\"%s\") is valid`, string(*validMinimalIDP.Spec.AllowAuthentication.Organizations.Policy))), buildLogForUpdatingHostValid("minimal-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is valid`, *validFilledOutIDP.Spec.GitHubAPI.Host), - buildLogForUpdatingTLSConfigurationValid("minimal-idp-name", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), + buildLogForUpdatingTLSConfigurationValid("minimal-idp-name", "True", "Success", "spec.githubAPI.tls is valid: using configured CA bundle"), buildLogForUpdatingGitHubConnectionValid("minimal-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is reachable and TLS verification succeeds`, *validFilledOutIDP.Spec.GitHubAPI.Host), buildLogForUpdatingPhase("minimal-idp-name", "Error"), }, @@ -2075,7 +2075,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidTrue("minimal-idp-name"), buildLogForUpdatingOrganizationPolicyValid("minimal-idp-name", "True", "Success", fmt.Sprintf(`spec.allowAuthentication.organizations.policy (\"%s\") is valid`, string(*validMinimalIDP.Spec.AllowAuthentication.Organizations.Policy))), buildLogForUpdatingHostValid("minimal-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is valid`, *validMinimalIDP.Spec.GitHubAPI.Host), - buildLogForUpdatingTLSConfigurationValid("minimal-idp-name", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), + buildLogForUpdatingTLSConfigurationValid("minimal-idp-name", "True", "Success", "spec.githubAPI.tls is valid: using configured CA bundle"), buildLogForUpdatingGitHubConnectionValid("minimal-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is reachable and TLS verification succeeds`, *validMinimalIDP.Spec.GitHubAPI.Host), buildLogForUpdatingPhase("minimal-idp-name", "Error"), }, @@ -2118,7 +2118,7 @@ func TestController(t *testing.T) { buildLogForUpdatingClaimsValidTrue("minimal-idp-name"), buildLogForUpdatingOrganizationPolicyValid("minimal-idp-name", "True", "Success", fmt.Sprintf(`spec.allowAuthentication.organizations.policy (\"%s\") is valid`, string(*validMinimalIDP.Spec.AllowAuthentication.Organizations.Policy))), buildLogForUpdatingHostValid("minimal-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is valid`, *validFilledOutIDP.Spec.GitHubAPI.Host), - buildLogForUpdatingTLSConfigurationValid("minimal-idp-name", "True", "Success", "spec.githubAPI.tls is valid: loaded TLS configuration"), + buildLogForUpdatingTLSConfigurationValid("minimal-idp-name", "True", "Success", "spec.githubAPI.tls is valid: using configured CA bundle"), buildLogForUpdatingGitHubConnectionValid("minimal-idp-name", "True", "Success", `spec.githubAPI.host (\"%s\") is reachable and TLS verification succeeds`, *validFilledOutIDP.Spec.GitHubAPI.Host), buildLogForUpdatingPhase("minimal-idp-name", "Error"), }, @@ -2402,7 +2402,7 @@ func TestController_OnlyWantActions(t *testing.T) { ObservedGeneration: 333, LastTransitionTime: oneHourAgo, Reason: conditionsutil.ReasonSuccess, - Message: "spec.githubAPI.tls is valid: loaded TLS configuration", + Message: "spec.githubAPI.tls is valid: using configured CA bundle", }, }, }, @@ -2514,7 +2514,7 @@ func TestController_OnlyWantActions(t *testing.T) { ObservedGeneration: 1234, LastTransitionTime: wantLastTransitionTime, Reason: conditionsutil.ReasonSuccess, - Message: "spec.githubAPI.tls is valid: loaded TLS configuration", + Message: "spec.githubAPI.tls is valid: using configured CA bundle", }, }, } diff --git a/internal/controller/supervisorconfig/ldapupstreamwatcher/ldap_upstream_watcher_test.go b/internal/controller/supervisorconfig/ldapupstreamwatcher/ldap_upstream_watcher_test.go index 1486ed826..6fba51ab9 100644 --- a/internal/controller/supervisorconfig/ldapupstreamwatcher/ldap_upstream_watcher_test.go +++ b/internal/controller/supervisorconfig/ldapupstreamwatcher/ldap_upstream_watcher_test.go @@ -397,7 +397,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { return []metav1.Condition{ bindSecretValidTrueCondition(gen), ldapConnectionValidTrueCondition(gen, secretVersion), - tlsConfigurationValidLoadedTrueCondition(gen, "loaded TLS configuration"), + tlsConfigurationValidLoadedTrueCondition(gen, "using configured CA bundle"), } } @@ -588,7 +588,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { Message: fmt.Sprintf(`secret "%s" not found`, testBindSecretName), ObservedGeneration: 1234, }, - tlsConfigurationValidLoadedTrueCondition(1234, "loaded TLS configuration"), + tlsConfigurationValidLoadedTrueCondition(1234, "using configured CA bundle"), }, }, }}, @@ -616,7 +616,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { Message: fmt.Sprintf(`referenced Secret "%s" has wrong type "some-other-type" (should be "kubernetes.io/basic-auth")`, testBindSecretName), ObservedGeneration: 1234, }, - tlsConfigurationValidLoadedTrueCondition(1234, "loaded TLS configuration"), + tlsConfigurationValidLoadedTrueCondition(1234, "using configured CA bundle"), }, }, }}, @@ -643,7 +643,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { Message: fmt.Sprintf(`referenced Secret "%s" is missing required keys ["username" "password"]`, testBindSecretName), ObservedGeneration: 1234, }, - tlsConfigurationValidLoadedTrueCondition(1234, "loaded TLS configuration"), + tlsConfigurationValidLoadedTrueCondition(1234, "using configured CA bundle"), }, }, }}, @@ -746,7 +746,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { Status: "True", LastTransitionTime: now, Reason: "Success", - Message: "spec.tls is valid: no TLS configuration provided", + Message: "spec.tls is valid: no TLS configuration provided: using default root CA bundle from container image", ObservedGeneration: 1234, }, }, @@ -816,7 +816,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { "ldap.example.com", testBindUsername, testBindSecretName, "4242"), ObservedGeneration: 1234, }, - tlsConfigurationValidLoadedTrueCondition(1234, "loaded TLS configuration"), + tlsConfigurationValidLoadedTrueCondition(1234, "using configured CA bundle"), }, }, }}, @@ -890,7 +890,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { "ldap.example.com:5678", testBindUsername, "ldap.example.com:5678"), ObservedGeneration: 1234, }, - tlsConfigurationValidLoadedTrueCondition(1234, "loaded TLS configuration"), + tlsConfigurationValidLoadedTrueCondition(1234, "using configured CA bundle"), }, }, }}, @@ -937,7 +937,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { Conditions: []metav1.Condition{ bindSecretValidTrueCondition(1234), ldapConnectionValidTrueCondition(1234, "4242"), - tlsConfigurationValidLoadedTrueCondition(1234, "no TLS configuration provided"), + tlsConfigurationValidLoadedTrueCondition(1234, "no TLS configuration provided: using default root CA bundle from container image"), }, }, }}, @@ -981,7 +981,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { Message: fmt.Sprintf(`secret "%s" not found`, "non-existent-secret"), ObservedGeneration: 42, }, - tlsConfigurationValidLoadedTrueCondition(42, "loaded TLS configuration"), + tlsConfigurationValidLoadedTrueCondition(42, "using configured CA bundle"), }, }, }, @@ -1031,7 +1031,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { testHost, testBindUsername, testBindUsername), ObservedGeneration: 1234, }, - tlsConfigurationValidLoadedTrueCondition(1234, "loaded TLS configuration"), + tlsConfigurationValidLoadedTrueCondition(1234, "using configured CA bundle"), }, }, }}, @@ -1363,7 +1363,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { Status: "True", LastTransitionTime: now, Reason: "Success", - Message: "spec.tls is valid: loaded TLS configuration", + Message: "spec.tls is valid: using configured CA bundle", ObservedGeneration: 1234, }, }, diff --git a/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher_test.go b/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher_test.go index baccac721..57f6481a2 100644 --- a/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher_test.go +++ b/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher_test.go @@ -268,7 +268,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { wantLogs: []string{ `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"ClientCredentialsSecretValid","status":"False","reason":"SecretNotFound","message":"secret \"test-client-secret\" not found"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","status":"True","reason":"Success","message":"discovered issuer configuration"}`, - `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: loaded TLS configuration"}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: using configured CA bundle"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"AdditionalAuthorizeParametersValid","status":"True","reason":"Success","message":"additionalAuthorizeParameters parameter names are allowed"}`, `{"level":"error","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"oidcupstreamwatcher/oidc_upstream_watcher.go:$oidcupstreamwatcher.(*oidcWatcherController).validateUpstream","message":"found failing condition","namespace":"test-namespace","name":"test-name","type":"ClientCredentialsSecretValid","reason":"SecretNotFound","message":"secret \"test-client-secret\" not found","error":"OIDCIdentityProvider has a failing condition"}`, }, @@ -284,7 +284,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { {Type: "OIDCDiscoverySucceeded", Status: "True", LastTransitionTime: now, Reason: "Success", Message: "discovered issuer configuration"}, {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", - Message: "spec.tls is valid: loaded TLS configuration"}, + Message: "spec.tls is valid: using configured CA bundle"}, }, }, }}, @@ -308,7 +308,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { wantLogs: []string{ `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"ClientCredentialsSecretValid","status":"False","reason":"SecretWrongType","message":"referenced Secret \"test-client-secret\" has wrong type \"some-other-type\" (should be \"secrets.pinniped.dev/oidc-client\")"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","status":"True","reason":"Success","message":"discovered issuer configuration"}`, - `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: loaded TLS configuration"}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: using configured CA bundle"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"AdditionalAuthorizeParametersValid","status":"True","reason":"Success","message":"additionalAuthorizeParameters parameter names are allowed"}`, `{"level":"error","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"oidcupstreamwatcher/oidc_upstream_watcher.go:$oidcupstreamwatcher.(*oidcWatcherController).validateUpstream","message":"found failing condition","namespace":"test-namespace","name":"test-name","type":"ClientCredentialsSecretValid","reason":"SecretWrongType","message":"referenced Secret \"test-client-secret\" has wrong type \"some-other-type\" (should be \"secrets.pinniped.dev/oidc-client\")","error":"OIDCIdentityProvider has a failing condition"}`, }, @@ -324,7 +324,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { {Type: "OIDCDiscoverySucceeded", Status: "True", LastTransitionTime: now, Reason: "Success", Message: "discovered issuer configuration"}, {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", - Message: "spec.tls is valid: loaded TLS configuration"}, + Message: "spec.tls is valid: using configured CA bundle"}, }, }, }}, @@ -347,7 +347,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { wantLogs: []string{ `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"ClientCredentialsSecretValid","status":"False","reason":"SecretMissingKeys","message":"referenced Secret \"test-client-secret\" is missing required keys [\"clientID\" \"clientSecret\"]"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","status":"True","reason":"Success","message":"discovered issuer configuration"}`, - `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: loaded TLS configuration"}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: using configured CA bundle"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"AdditionalAuthorizeParametersValid","status":"True","reason":"Success","message":"additionalAuthorizeParameters parameter names are allowed"}`, `{"level":"error","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"oidcupstreamwatcher/oidc_upstream_watcher.go:$oidcupstreamwatcher.(*oidcWatcherController).validateUpstream","message":"found failing condition","namespace":"test-namespace","name":"test-name","type":"ClientCredentialsSecretValid","reason":"SecretMissingKeys","message":"referenced Secret \"test-client-secret\" is missing required keys [\"clientID\" \"clientSecret\"]","error":"OIDCIdentityProvider has a failing condition"}`, }, @@ -363,7 +363,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { {Type: "OIDCDiscoverySucceeded", Status: "True", LastTransitionTime: now, Reason: "Success", Message: "discovered issuer configuration"}, {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", - Message: "spec.tls is valid: loaded TLS configuration"}, + Message: "spec.tls is valid: using configured CA bundle"}, }, }, }}, @@ -472,7 +472,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { wantLogs: []string{ `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"ClientCredentialsSecretValid","status":"True","reason":"Success","message":"loaded client credentials"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","status":"False","reason":"Unreachable","message":"failed to parse issuer URL: parse \"%invalid-url-that-is-really-really-long-nanananananananannanananan-batman-nanananananananananananananana-batman-lalalalalalalalalal-batman-weeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\": invalid URL escape \"%in\""}`, - `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: no TLS configuration provided"}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: no TLS configuration provided: using default root CA bundle from container image"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"AdditionalAuthorizeParametersValid","status":"True","reason":"Success","message":"additionalAuthorizeParameters parameter names are allowed"}`, `{"level":"error","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"oidcupstreamwatcher/oidc_upstream_watcher.go:$oidcupstreamwatcher.(*oidcWatcherController).validateUpstream","message":"found failing condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","reason":"Unreachable","message":"failed to parse issuer URL: parse \"%invalid-url-that-is-really-really-long-nanananananananannanananan-batman-nanananananananananananananana-batman-lalalalalalalalalal-batman-weeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\": invalid URL escape \"%in\"","error":"OIDCIdentityProvider has a failing condition"}`, }, @@ -488,7 +488,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { {Type: "OIDCDiscoverySucceeded", Status: "False", LastTransitionTime: now, Reason: "Unreachable", Message: `failed to parse issuer URL: parse "%invalid-url-that-is-really-really-long-nanananananananannanananan-batman-nanananananananananananananana-batman-lalalalalalalalalal-batman-weeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee": invalid URL escape "%in"`}, {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", - Message: "spec.tls is valid: no TLS configuration provided"}, + Message: "spec.tls is valid: no TLS configuration provided: using default root CA bundle from container image"}, }, }, }}, @@ -511,7 +511,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { wantLogs: []string{ `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"ClientCredentialsSecretValid","status":"True","reason":"Success","message":"loaded client credentials"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","status":"False","reason":"Unreachable","message":"issuer URL '` + strings.Replace(testIssuerURL, "https", "http", 1) + `' must have \"https\" scheme, not \"http\""}`, - `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: no TLS configuration provided"}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: no TLS configuration provided: using default root CA bundle from container image"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"AdditionalAuthorizeParametersValid","status":"True","reason":"Success","message":"additionalAuthorizeParameters parameter names are allowed"}`, `{"level":"error","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"oidcupstreamwatcher/oidc_upstream_watcher.go:$oidcupstreamwatcher.(*oidcWatcherController).validateUpstream","message":"found failing condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","reason":"Unreachable","message":"issuer URL '` + strings.Replace(testIssuerURL, "https", "http", 1) + `' must have \"https\" scheme, not \"http\"","error":"OIDCIdentityProvider has a failing condition"}`, }, @@ -527,7 +527,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { {Type: "OIDCDiscoverySucceeded", Status: "False", LastTransitionTime: now, Reason: "Unreachable", Message: `issuer URL '` + strings.Replace(testIssuerURL, "https", "http", 1) + `' must have "https" scheme, not "http"`}, {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", - Message: "spec.tls is valid: no TLS configuration provided"}, + Message: "spec.tls is valid: no TLS configuration provided: using default root CA bundle from container image"}, }, }, }}, @@ -550,7 +550,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { wantLogs: []string{ `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"ClientCredentialsSecretValid","status":"True","reason":"Success","message":"loaded client credentials"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","status":"False","reason":"Unreachable","message":"issuer URL '` + testIssuerURL + `?sub=foo' cannot contain query or fragment component"}`, - `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: no TLS configuration provided"}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: no TLS configuration provided: using default root CA bundle from container image"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"AdditionalAuthorizeParametersValid","status":"True","reason":"Success","message":"additionalAuthorizeParameters parameter names are allowed"}`, `{"level":"error","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"oidcupstreamwatcher/oidc_upstream_watcher.go:$oidcupstreamwatcher.(*oidcWatcherController).validateUpstream","message":"found failing condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","reason":"Unreachable","message":"issuer URL '` + testIssuerURL + `?sub=foo' cannot contain query or fragment component","error":"OIDCIdentityProvider has a failing condition"}`, }, @@ -566,7 +566,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { {Type: "OIDCDiscoverySucceeded", Status: "False", LastTransitionTime: now, Reason: "Unreachable", Message: `issuer URL '` + testIssuerURL + "?sub=foo" + `' cannot contain query or fragment component`}, {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", - Message: "spec.tls is valid: no TLS configuration provided"}, + Message: "spec.tls is valid: no TLS configuration provided: using default root CA bundle from container image"}, }, }, }}, @@ -589,7 +589,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { wantLogs: []string{ `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"ClientCredentialsSecretValid","status":"True","reason":"Success","message":"loaded client credentials"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","status":"False","reason":"Unreachable","message":"issuer URL '` + testIssuerURL + `#fragment' cannot contain query or fragment component"}`, - `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: no TLS configuration provided"}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: no TLS configuration provided: using default root CA bundle from container image"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"AdditionalAuthorizeParametersValid","status":"True","reason":"Success","message":"additionalAuthorizeParameters parameter names are allowed"}`, `{"level":"error","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"oidcupstreamwatcher/oidc_upstream_watcher.go:$oidcupstreamwatcher.(*oidcWatcherController).validateUpstream","message":"found failing condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","reason":"Unreachable","message":"issuer URL '` + testIssuerURL + `#fragment' cannot contain query or fragment component","error":"OIDCIdentityProvider has a failing condition"}`, }, @@ -605,7 +605,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { {Type: "OIDCDiscoverySucceeded", Status: "False", LastTransitionTime: now, Reason: "Unreachable", Message: `issuer URL '` + testIssuerURL + "#fragment" + `' cannot contain query or fragment component`}, {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", - Message: "spec.tls is valid: no TLS configuration provided"}, + Message: "spec.tls is valid: no TLS configuration provided: using default root CA bundle from container image"}, }, }, }}, @@ -630,7 +630,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { `{"level":"error","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"oidcupstreamwatcher/oidc_upstream_watcher.go:$oidcupstreamwatcher.(*oidcWatcherController).validateIssuer","message":"failed to perform OIDC discovery","namespace":"test-namespace","name":"test-name","issuer":"` + testIssuerURL + `/valid-url-that-is-really-really-long-nanananananananannanananan-batman-nanananananananananananananana-batman-lalalalalalalalalal-batman-weeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee","error":"Get \"` + testIssuerURL + `/valid-url-that-is-really-really-long-nanananananananannanananan-batman-nanananananananananananananana-batman-lalalalalalalalalal-batman-weeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee/.well-known/openid-configuration\": tls: failed to verify certificate: x509: certificate signed by unknown authority"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"ClientCredentialsSecretValid","status":"True","reason":"Success","message":"loaded client credentials"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","status":"False","reason":"Unreachable","message":"failed to perform OIDC discovery against \"` + testIssuerURL + `/valid-url-that-is-really-really-long-nanananananananannanananan-batman-nanananananananananananananana-batman-lalalalalalalalalal-batman-weeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\":\nGet \"` + testIssuerURL + `/valid-url-that-is-really-really-long-nanananananananannanananan-batman-nanananananananananananananana-batman-lalalalalalalalalal-batman-weeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee/.well-known/openid-configuration\": tls: failed to verify certificate: x509: certificate signed by unknown authority"}`, - `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: loaded TLS configuration"}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: using configured CA bundle"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"AdditionalAuthorizeParametersValid","status":"True","reason":"Success","message":"additionalAuthorizeParameters parameter names are allowed"}`, `{"level":"error","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"oidcupstreamwatcher/oidc_upstream_watcher.go:$oidcupstreamwatcher.(*oidcWatcherController).validateUpstream","message":"found failing condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","reason":"Unreachable","message":"failed to perform OIDC discovery against \"` + testIssuerURL + `/valid-url-that-is-really-really-long-nanananananananannanananan-batman-nanananananananananananananana-batman-lalalalalalalalalal-batman-weeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\":\nGet \"` + testIssuerURL + `/valid-url-that-is-really-really-long-nanananananananannanananan-batman-nanananananananananananananana-batman-lalalalalalalalalal-batman-weeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee/.well-known/openid-configuration\": tls: failed to verify certificate: x509: certificate signed by unknown authority","error":"OIDCIdentityProvider has a failing condition"}`, }, @@ -647,7 +647,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { Message: `failed to perform OIDC discovery against "` + testIssuerURL + `/valid-url-that-is-really-really-long-nanananananananannanananan-batman-nanananananananananananananana-batman-lalalalalalalalalal-batman-weeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee":` + "\n" + `Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nanananananananannanananan-batman-nanananananananananananananana-batman-lalalalalalalalalal-batman-weeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee/.well-known/openid-configuration": tls: failed to verify certificate: x509: certificate signed by unknown authority`}, {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", - Message: "spec.tls is valid: loaded TLS configuration"}, + Message: "spec.tls is valid: using configured CA bundle"}, }, }, }}, @@ -671,7 +671,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { wantLogs: []string{ `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"ClientCredentialsSecretValid","status":"True","reason":"Success","message":"loaded client credentials"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","status":"False","reason":"InvalidResponse","message":"failed to parse authorization endpoint URL: parse \"%\": invalid URL escape \"%\""}`, - `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: loaded TLS configuration"}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: using configured CA bundle"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"AdditionalAuthorizeParametersValid","status":"True","reason":"Success","message":"additionalAuthorizeParameters parameter names are allowed"}`, `{"level":"error","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"oidcupstreamwatcher/oidc_upstream_watcher.go:$oidcupstreamwatcher.(*oidcWatcherController).validateUpstream","message":"found failing condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","reason":"InvalidResponse","message":"failed to parse authorization endpoint URL: parse \"%\": invalid URL escape \"%\"","error":"OIDCIdentityProvider has a failing condition"}`, }, @@ -687,7 +687,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { {Type: "OIDCDiscoverySucceeded", Status: "False", LastTransitionTime: now, Reason: "InvalidResponse", Message: `failed to parse authorization endpoint URL: parse "%": invalid URL escape "%"`}, {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", - Message: "spec.tls is valid: loaded TLS configuration"}, + Message: "spec.tls is valid: using configured CA bundle"}, }, }, }}, @@ -711,7 +711,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { wantLogs: []string{ `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"ClientCredentialsSecretValid","status":"True","reason":"Success","message":"loaded client credentials"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","status":"False","reason":"InvalidResponse","message":"failed to parse revocation endpoint URL: parse \"%\": invalid URL escape \"%\""}`, - `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: loaded TLS configuration"}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: using configured CA bundle"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"AdditionalAuthorizeParametersValid","status":"True","reason":"Success","message":"additionalAuthorizeParameters parameter names are allowed"}`, `{"level":"error","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"oidcupstreamwatcher/oidc_upstream_watcher.go:$oidcupstreamwatcher.(*oidcWatcherController).validateUpstream","message":"found failing condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","reason":"InvalidResponse","message":"failed to parse revocation endpoint URL: parse \"%\": invalid URL escape \"%\"","error":"OIDCIdentityProvider has a failing condition"}`, }, @@ -727,7 +727,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { {Type: "OIDCDiscoverySucceeded", Status: "False", LastTransitionTime: now, Reason: "InvalidResponse", Message: `failed to parse revocation endpoint URL: parse "%": invalid URL escape "%"`}, {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", - Message: "spec.tls is valid: loaded TLS configuration"}, + Message: "spec.tls is valid: using configured CA bundle"}, }, }, }}, @@ -751,7 +751,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { wantLogs: []string{ `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"ClientCredentialsSecretValid","status":"True","reason":"Success","message":"loaded client credentials"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","status":"False","reason":"InvalidResponse","message":"authorization endpoint URL 'http://example.com/authorize' must have \"https\" scheme, not \"http\""}`, - `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: loaded TLS configuration"}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: using configured CA bundle"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"AdditionalAuthorizeParametersValid","status":"True","reason":"Success","message":"additionalAuthorizeParameters parameter names are allowed"}`, `{"level":"error","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"oidcupstreamwatcher/oidc_upstream_watcher.go:$oidcupstreamwatcher.(*oidcWatcherController).validateUpstream","message":"found failing condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","reason":"InvalidResponse","message":"authorization endpoint URL 'http://example.com/authorize' must have \"https\" scheme, not \"http\"","error":"OIDCIdentityProvider has a failing condition"}`, }, @@ -767,7 +767,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { {Type: "OIDCDiscoverySucceeded", Status: "False", LastTransitionTime: now, Reason: "InvalidResponse", Message: `authorization endpoint URL 'http://example.com/authorize' must have "https" scheme, not "http"`}, {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", - Message: "spec.tls is valid: loaded TLS configuration"}, + Message: "spec.tls is valid: using configured CA bundle"}, }, }, }}, @@ -791,7 +791,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { wantLogs: []string{ `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"ClientCredentialsSecretValid","status":"True","reason":"Success","message":"loaded client credentials"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","status":"False","reason":"InvalidResponse","message":"revocation endpoint URL 'http://example.com/revoke' must have \"https\" scheme, not \"http\""}`, - `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: loaded TLS configuration"}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: using configured CA bundle"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"AdditionalAuthorizeParametersValid","status":"True","reason":"Success","message":"additionalAuthorizeParameters parameter names are allowed"}`, `{"level":"error","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"oidcupstreamwatcher/oidc_upstream_watcher.go:$oidcupstreamwatcher.(*oidcWatcherController).validateUpstream","message":"found failing condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","reason":"InvalidResponse","message":"revocation endpoint URL 'http://example.com/revoke' must have \"https\" scheme, not \"http\"","error":"OIDCIdentityProvider has a failing condition"}`, }, @@ -807,7 +807,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { {Type: "OIDCDiscoverySucceeded", Status: "False", LastTransitionTime: now, Reason: "InvalidResponse", Message: `revocation endpoint URL 'http://example.com/revoke' must have "https" scheme, not "http"`}, {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", - Message: "spec.tls is valid: loaded TLS configuration"}, + Message: "spec.tls is valid: using configured CA bundle"}, }, }, }}, @@ -831,7 +831,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { wantLogs: []string{ `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"ClientCredentialsSecretValid","status":"True","reason":"Success","message":"loaded client credentials"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","status":"False","reason":"InvalidResponse","message":"token endpoint URL 'http://example.com/token' must have \"https\" scheme, not \"http\""}`, - `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: loaded TLS configuration"}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: using configured CA bundle"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"AdditionalAuthorizeParametersValid","status":"True","reason":"Success","message":"additionalAuthorizeParameters parameter names are allowed"}`, `{"level":"error","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"oidcupstreamwatcher/oidc_upstream_watcher.go:$oidcupstreamwatcher.(*oidcWatcherController).validateUpstream","message":"found failing condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","reason":"InvalidResponse","message":"token endpoint URL 'http://example.com/token' must have \"https\" scheme, not \"http\"","error":"OIDCIdentityProvider has a failing condition"}`, }, @@ -847,7 +847,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { {Type: "OIDCDiscoverySucceeded", Status: "False", LastTransitionTime: now, Reason: "InvalidResponse", Message: `token endpoint URL 'http://example.com/token' must have "https" scheme, not "http"`}, {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", - Message: "spec.tls is valid: loaded TLS configuration"}, + Message: "spec.tls is valid: using configured CA bundle"}, }, }, }}, @@ -871,7 +871,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { wantLogs: []string{ `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"ClientCredentialsSecretValid","status":"True","reason":"Success","message":"loaded client credentials"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","status":"False","reason":"InvalidResponse","message":"token endpoint URL '' must have \"https\" scheme, not \"\""}`, - `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: loaded TLS configuration"}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: using configured CA bundle"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"AdditionalAuthorizeParametersValid","status":"True","reason":"Success","message":"additionalAuthorizeParameters parameter names are allowed"}`, `{"level":"error","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"oidcupstreamwatcher/oidc_upstream_watcher.go:$oidcupstreamwatcher.(*oidcWatcherController).validateUpstream","message":"found failing condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","reason":"InvalidResponse","message":"token endpoint URL '' must have \"https\" scheme, not \"\"","error":"OIDCIdentityProvider has a failing condition"}`, }, @@ -887,7 +887,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { {Type: "OIDCDiscoverySucceeded", Status: "False", LastTransitionTime: now, Reason: "InvalidResponse", Message: `token endpoint URL '' must have "https" scheme, not ""`}, {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", - Message: "spec.tls is valid: loaded TLS configuration"}, + Message: "spec.tls is valid: using configured CA bundle"}, }, }, }}, @@ -911,7 +911,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { wantLogs: []string{ `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"ClientCredentialsSecretValid","status":"True","reason":"Success","message":"loaded client credentials"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","status":"False","reason":"InvalidResponse","message":"authorization endpoint URL '' must have \"https\" scheme, not \"\""}`, - `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: loaded TLS configuration"}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: using configured CA bundle"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"AdditionalAuthorizeParametersValid","status":"True","reason":"Success","message":"additionalAuthorizeParameters parameter names are allowed"}`, `{"level":"error","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"oidcupstreamwatcher/oidc_upstream_watcher.go:$oidcupstreamwatcher.(*oidcWatcherController).validateUpstream","message":"found failing condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","reason":"InvalidResponse","message":"authorization endpoint URL '' must have \"https\" scheme, not \"\"","error":"OIDCIdentityProvider has a failing condition"}`, }, @@ -927,7 +927,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { {Type: "OIDCDiscoverySucceeded", Status: "False", LastTransitionTime: now, Reason: "InvalidResponse", Message: `authorization endpoint URL '' must have "https" scheme, not ""`}, {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", - Message: "spec.tls is valid: loaded TLS configuration"}, + Message: "spec.tls is valid: using configured CA bundle"}, }, }, }}, @@ -964,7 +964,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { wantLogs: []string{ `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"ClientCredentialsSecretValid","status":"True","reason":"Success","message":"loaded client credentials"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","status":"True","reason":"Success","message":"discovered issuer configuration"}`, - `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: loaded TLS configuration"}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: using configured CA bundle"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"AdditionalAuthorizeParametersValid","status":"True","reason":"Success","message":"additionalAuthorizeParameters parameter names are allowed"}`, }, wantResultingCache: []*oidctestutil.TestUpstreamOIDCIdentityProvider{ @@ -993,7 +993,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { {Type: "OIDCDiscoverySucceeded", Status: "True", LastTransitionTime: now, Reason: "Success", Message: "discovered issuer configuration"}, {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", - Message: "spec.tls is valid: loaded TLS configuration"}, + Message: "spec.tls is valid: using configured CA bundle"}, }, }, }}, @@ -1027,7 +1027,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { wantLogs: []string{ `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"ClientCredentialsSecretValid","status":"True","reason":"Success","message":"loaded client credentials"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","status":"True","reason":"Success","message":"discovered issuer configuration"}`, - `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: loaded TLS configuration"}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: using configured CA bundle"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"AdditionalAuthorizeParametersValid","status":"True","reason":"Success","message":"additionalAuthorizeParameters parameter names are allowed"}`, }, wantResultingCache: []*oidctestutil.TestUpstreamOIDCIdentityProvider{ @@ -1057,7 +1057,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { {Type: "OIDCDiscoverySucceeded", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "discovered issuer configuration", ObservedGeneration: 1234}, {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", - Message: "spec.tls is valid: loaded TLS configuration", ObservedGeneration: 1234}, + Message: "spec.tls is valid: using configured CA bundle", ObservedGeneration: 1234}, }, }, }}, @@ -1083,7 +1083,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { {Type: "OIDCDiscoverySucceeded", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "discovered issuer configuration", ObservedGeneration: 1234}, {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", - Message: "spec.tls is valid: loaded TLS configuration", ObservedGeneration: 1234}, + Message: "spec.tls is valid: using configured CA bundle", ObservedGeneration: 1234}, }, }, }}, @@ -1150,7 +1150,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { {Type: "OIDCDiscoverySucceeded", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "discovered issuer configuration", ObservedGeneration: 1234}, {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", - Message: "spec.tls is valid: loaded TLS configuration", ObservedGeneration: 1234}, + Message: "spec.tls is valid: using configured CA bundle", ObservedGeneration: 1234}, }, }, }}, @@ -1187,7 +1187,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { wantLogs: []string{ `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"ClientCredentialsSecretValid","status":"True","reason":"Success","message":"loaded client credentials"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","status":"True","reason":"Success","message":"discovered issuer configuration"}`, - `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: loaded TLS configuration"}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: using configured CA bundle"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"AdditionalAuthorizeParametersValid","status":"True","reason":"Success","message":"additionalAuthorizeParameters parameter names are allowed"}`, }, wantResultingCache: []*oidctestutil.TestUpstreamOIDCIdentityProvider{ @@ -1217,7 +1217,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { {Type: "OIDCDiscoverySucceeded", Status: "True", LastTransitionTime: now, Reason: "Success", Message: "discovered issuer configuration", ObservedGeneration: 1234}, {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", - Message: "spec.tls is valid: loaded TLS configuration", ObservedGeneration: 1234}, + Message: "spec.tls is valid: using configured CA bundle", ObservedGeneration: 1234}, }, }, }}, @@ -1253,7 +1253,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { wantLogs: []string{ `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"ClientCredentialsSecretValid","status":"True","reason":"Success","message":"loaded client credentials"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","status":"True","reason":"Success","message":"discovered issuer configuration"}`, - `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: loaded TLS configuration"}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: using configured CA bundle"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"AdditionalAuthorizeParametersValid","status":"True","reason":"Success","message":"additionalAuthorizeParameters parameter names are allowed"}`, }, wantResultingCache: []*oidctestutil.TestUpstreamOIDCIdentityProvider{ @@ -1283,7 +1283,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { {Type: "OIDCDiscoverySucceeded", Status: "True", LastTransitionTime: now, Reason: "Success", Message: "discovered issuer configuration", ObservedGeneration: 1234}, {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", - Message: "spec.tls is valid: loaded TLS configuration", ObservedGeneration: 1234}, + Message: "spec.tls is valid: using configured CA bundle", ObservedGeneration: 1234}, }, }, }}, @@ -1317,7 +1317,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { wantLogs: []string{ `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"ClientCredentialsSecretValid","status":"True","reason":"Success","message":"loaded client credentials"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","status":"True","reason":"Success","message":"discovered issuer configuration"}`, - `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: loaded TLS configuration"}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: using configured CA bundle"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"AdditionalAuthorizeParametersValid","status":"True","reason":"Success","message":"additionalAuthorizeParameters parameter names are allowed"}`, }, wantResultingCache: []*oidctestutil.TestUpstreamOIDCIdentityProvider{ @@ -1347,7 +1347,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { {Type: "OIDCDiscoverySucceeded", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "discovered issuer configuration", ObservedGeneration: 1234}, {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", - Message: "spec.tls is valid: loaded TLS configuration", ObservedGeneration: 1234}, + Message: "spec.tls is valid: using configured CA bundle", ObservedGeneration: 1234}, }, }, }}, @@ -1384,7 +1384,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { wantLogs: []string{ `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"ClientCredentialsSecretValid","status":"True","reason":"Success","message":"loaded client credentials"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","status":"True","reason":"Success","message":"discovered issuer configuration"}`, - `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: loaded TLS configuration"}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: using configured CA bundle"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"AdditionalAuthorizeParametersValid","status":"True","reason":"Success","message":"additionalAuthorizeParameters parameter names are allowed"}`, }, wantResultingCache: []*oidctestutil.TestUpstreamOIDCIdentityProvider{ @@ -1414,7 +1414,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { {Type: "OIDCDiscoverySucceeded", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "discovered issuer configuration", ObservedGeneration: 1234}, {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", - Message: "spec.tls is valid: loaded TLS configuration", ObservedGeneration: 1234}, + Message: "spec.tls is valid: using configured CA bundle", ObservedGeneration: 1234}, }, }, }}, @@ -1459,7 +1459,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { wantLogs: []string{ `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"ClientCredentialsSecretValid","status":"True","reason":"Success","message":"loaded client credentials"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","status":"True","reason":"Success","message":"discovered issuer configuration"}`, - `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: loaded TLS configuration"}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: using configured CA bundle"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"AdditionalAuthorizeParametersValid","status":"True","reason":"Success","message":"additionalAuthorizeParameters parameter names are allowed"}`, }, wantResultingCache: []*oidctestutil.TestUpstreamOIDCIdentityProvider{ @@ -1491,7 +1491,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { {Type: "OIDCDiscoverySucceeded", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "discovered issuer configuration", ObservedGeneration: 1234}, {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", - Message: "spec.tls is valid: loaded TLS configuration", ObservedGeneration: 1234}, + Message: "spec.tls is valid: using configured CA bundle", ObservedGeneration: 1234}, }, }, }}, @@ -1529,7 +1529,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { wantLogs: []string{ `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"ClientCredentialsSecretValid","status":"True","reason":"Success","message":"loaded client credentials"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","status":"True","reason":"Success","message":"discovered issuer configuration"}`, - `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: loaded TLS configuration"}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: using configured CA bundle"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"AdditionalAuthorizeParametersValid","status":"False","reason":"DisallowedParameterName","message":"the following additionalAuthorizeParameters are not allowed: response_type,scope,client_id,state,nonce,code_challenge,code_challenge_method,redirect_uri,hd"}`, `{"level":"error","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"oidcupstreamwatcher/oidc_upstream_watcher.go:$oidcupstreamwatcher.(*oidcWatcherController).validateUpstream","message":"found failing condition","namespace":"test-namespace","name":"test-name","type":"AdditionalAuthorizeParametersValid","reason":"DisallowedParameterName","message":"the following additionalAuthorizeParameters are not allowed: response_type,scope,client_id,state,nonce,code_challenge,code_challenge_method,redirect_uri,hd","error":"OIDCIdentityProvider has a failing condition"}`, }, @@ -1547,7 +1547,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { {Type: "OIDCDiscoverySucceeded", Status: "True", LastTransitionTime: now, Reason: "Success", Message: "discovered issuer configuration", ObservedGeneration: 1234}, {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", - Message: "spec.tls is valid: loaded TLS configuration", ObservedGeneration: 1234}, + Message: "spec.tls is valid: using configured CA bundle", ObservedGeneration: 1234}, }, }, }}, @@ -1572,7 +1572,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { `{"level":"error","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"oidcupstreamwatcher/oidc_upstream_watcher.go:$oidcupstreamwatcher.(*oidcWatcherController).validateIssuer","message":"failed to perform OIDC discovery","namespace":"test-namespace","name":"test-name","issuer":"` + testIssuerURL + `/ends-with-slash","error":"oidc: issuer did not match the issuer returned by provider, expected \"` + testIssuerURL + `/ends-with-slash\" got \"` + testIssuerURL + `/ends-with-slash/\""}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"ClientCredentialsSecretValid","status":"True","reason":"Success","message":"loaded client credentials"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","status":"False","reason":"Unreachable","message":"failed to perform OIDC discovery against \"` + testIssuerURL + `/ends-with-slash\":\noidc: issuer did not match the issuer returned by provider, expected \"` + testIssuerURL + `/ends-with-slash\" got \"` + testIssuerURL + `/ends-with-slash/\""}`, - `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: loaded TLS configuration"}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: using configured CA bundle"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"AdditionalAuthorizeParametersValid","status":"True","reason":"Success","message":"additionalAuthorizeParameters parameter names are allowed"}`, `{"level":"error","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"oidcupstreamwatcher/oidc_upstream_watcher.go:$oidcupstreamwatcher.(*oidcWatcherController).validateUpstream","message":"found failing condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","reason":"Unreachable","message":"failed to perform OIDC discovery against \"` + testIssuerURL + `/ends-with-slash\":\noidc: issuer did not match the issuer returned by provider, expected \"` + testIssuerURL + `/ends-with-slash\" got \"` + testIssuerURL + `/ends-with-slash/\"","error":"OIDCIdentityProvider has a failing condition"}`, }, @@ -1588,7 +1588,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { {Type: "OIDCDiscoverySucceeded", Status: "False", LastTransitionTime: now, Reason: "Unreachable", Message: `failed to perform OIDC discovery against "` + testIssuerURL + `/ends-with-slash":` + "\n" + `oidc: issuer did not match the issuer returned by provider, expected "` + testIssuerURL + `/ends-with-slash" got "` + testIssuerURL + `/ends-with-slash/"`}, {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", - Message: "spec.tls is valid: loaded TLS configuration"}, + Message: "spec.tls is valid: using configured CA bundle"}, }, }, }}, @@ -1613,7 +1613,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { `{"level":"error","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"oidcupstreamwatcher/oidc_upstream_watcher.go:$oidcupstreamwatcher.(*oidcWatcherController).validateIssuer","message":"failed to perform OIDC discovery","namespace":"test-namespace","name":"test-name","issuer":"` + testIssuerURL + `/","error":"oidc: issuer did not match the issuer returned by provider, expected \"` + testIssuerURL + `/\" got \"` + testIssuerURL + `\""}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"ClientCredentialsSecretValid","status":"True","reason":"Success","message":"loaded client credentials"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","status":"False","reason":"Unreachable","message":"failed to perform OIDC discovery against \"` + testIssuerURL + `/\":\noidc: issuer did not match the issuer returned by provider, expected \"` + testIssuerURL + `/\" got \"` + testIssuerURL + `\""}`, - `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: loaded TLS configuration"}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"TLSConfigurationValid","status":"True","reason":"Success","message":"spec.tls is valid: using configured CA bundle"}`, `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"conditionsutil/conditions_util.go:$conditionsutil.MergeConditions","message":"updated condition","namespace":"test-namespace","name":"test-name","type":"AdditionalAuthorizeParametersValid","status":"True","reason":"Success","message":"additionalAuthorizeParameters parameter names are allowed"}`, `{"level":"error","timestamp":"2099-08-08T13:57:36.123456Z","logger":"oidc-upstream-observer","caller":"oidcupstreamwatcher/oidc_upstream_watcher.go:$oidcupstreamwatcher.(*oidcWatcherController).validateUpstream","message":"found failing condition","namespace":"test-namespace","name":"test-name","type":"OIDCDiscoverySucceeded","reason":"Unreachable","message":"failed to perform OIDC discovery against \"` + testIssuerURL + `/\":\noidc: issuer did not match the issuer returned by provider, expected \"` + testIssuerURL + `/\" got \"` + testIssuerURL + `\"","error":"OIDCIdentityProvider has a failing condition"}`, }, @@ -1630,7 +1630,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { Message: `failed to perform OIDC discovery against "` + testIssuerURL + `/":` + "\n" + `oidc: issuer did not match the issuer returned by provider, expected "` + testIssuerURL + `/" got "` + testIssuerURL + `"`}, {Type: "TLSConfigurationValid", Status: "True", LastTransitionTime: now, Reason: "Success", - Message: "spec.tls is valid: loaded TLS configuration"}, + Message: "spec.tls is valid: using configured CA bundle"}, }, }, }}, diff --git a/internal/controller/tlsconfigutil/tls_config_util.go b/internal/controller/tlsconfigutil/tls_config_util.go index 42116a288..69376e3dc 100644 --- a/internal/controller/tlsconfigutil/tls_config_util.go +++ b/internal/controller/tlsconfigutil/tls_config_util.go @@ -21,8 +21,8 @@ import ( const ( ReasonInvalidTLSConfig = "InvalidTLSConfig" - noTLSConfigurationMessage = "no TLS configuration provided" - loadedTLSConfigurationMessage = "loaded TLS configuration" + noTLSConfigurationMessage = "no TLS configuration provided: using default root CA bundle from container image" + loadedTLSConfigurationMessage = "using configured CA bundle" typeTLSConfigurationValid = "TLSConfigurationValid" ) @@ -94,7 +94,7 @@ func ValidateTLSConfig( if err != nil { return invalidTLSCondition(err.Error()), nil } - if len(caBundle.PEMBytes()) < 1 { + if len(caBundle.PEMBytes()) == 0 { // An empty or nil CA bundle results in a valid TLS condition which indicates that no CA data was supplied. return validTLSCondition(fmt.Sprintf("%s is valid: %s", conditionPrefix, noTLSConfigurationMessage)), nil } diff --git a/internal/controller/tlsconfigutil/tls_config_util_test.go b/internal/controller/tlsconfigutil/tls_config_util_test.go index 0fae9f05c..52fbdff87 100644 --- a/internal/controller/tlsconfigutil/tls_config_util_test.go +++ b/internal/controller/tlsconfigutil/tls_config_util_test.go @@ -140,7 +140,7 @@ func TestValidateTLSConfig(t *testing.T) { Type: typeTLSConfigurationValid, Status: metav1.ConditionTrue, Reason: conditionsutil.ReasonSuccess, - Message: "spec.foo.tls is valid: loaded TLS configuration", + Message: "spec.foo.tls is valid: using configured CA bundle", }, }, { @@ -170,7 +170,7 @@ func TestValidateTLSConfig(t *testing.T) { Type: typeTLSConfigurationValid, Status: metav1.ConditionTrue, Reason: conditionsutil.ReasonSuccess, - Message: "spec.foo.tls is valid: loaded TLS configuration", + Message: "spec.foo.tls is valid: using configured CA bundle", }, }, { @@ -399,7 +399,7 @@ func TestValidateTLSConfig(t *testing.T) { Type: typeTLSConfigurationValid, Status: metav1.ConditionTrue, Reason: conditionsutil.ReasonSuccess, - Message: "spec.foo.tls is valid: loaded TLS configuration", + Message: "spec.foo.tls is valid: using configured CA bundle", }, }, { diff --git a/test/integration/concierge_jwtauthenticator_status_test.go b/test/integration/concierge_jwtauthenticator_status_test.go index 0dea96d4d..3c199192a 100644 --- a/test/integration/concierge_jwtauthenticator_status_test.go +++ b/test/integration/concierge_jwtauthenticator_status_test.go @@ -222,7 +222,7 @@ func TestConciergeJWTAuthenticatorStatus_Parallel(t *testing.T) { Type: "TLSConfigurationValid", Status: "True", Reason: "Success", - Message: "spec.tls is valid: loaded TLS configuration", + Message: "spec.tls is valid: using configured CA bundle", }, }, ), @@ -424,7 +424,7 @@ func TestConciergeJWTAuthenticatorCRDValidations_Parallel(t *testing.T) { func allSuccessfulJWTAuthenticatorConditions(caBundleExists bool) []metav1.Condition { tlsConfigValidMsg := "no CA bundle specified" if caBundleExists { - tlsConfigValidMsg = "spec.tls is valid: loaded TLS configuration" + tlsConfigValidMsg = "spec.tls is valid: using configured CA bundle" } return []metav1.Condition{{ Type: "AuthenticatorValid", diff --git a/test/integration/concierge_webhookauthenticator_status_test.go b/test/integration/concierge_webhookauthenticator_status_test.go index 6cb6475b1..ac8fd5153 100644 --- a/test/integration/concierge_webhookauthenticator_status_test.go +++ b/test/integration/concierge_webhookauthenticator_status_test.go @@ -382,7 +382,7 @@ func allSuccessfulWebhookAuthenticatorConditions() []metav1.Condition { Type: "TLSConfigurationValid", Status: "True", Reason: "Success", - Message: "spec.tls is valid: loaded TLS configuration", + Message: "spec.tls is valid: using configured CA bundle", }, { Type: "WebhookConnectionValid", diff --git a/test/integration/supervisor_github_idp_test.go b/test/integration/supervisor_github_idp_test.go index 47c237334..3a135dbc1 100644 --- a/test/integration/supervisor_github_idp_test.go +++ b/test/integration/supervisor_github_idp_test.go @@ -410,7 +410,7 @@ func TestGitHubIDPPhaseAndConditions_Parallel(t *testing.T) { Type: "TLSConfigurationValid", Status: metav1.ConditionTrue, Reason: "Success", - Message: "spec.githubAPI.tls is valid: no TLS configuration provided", + Message: "spec.githubAPI.tls is valid: no TLS configuration provided: using default root CA bundle from container image", }, }, }, @@ -479,7 +479,7 @@ func TestGitHubIDPPhaseAndConditions_Parallel(t *testing.T) { Type: "TLSConfigurationValid", Status: metav1.ConditionTrue, Reason: "Success", - Message: `spec.githubAPI.tls is valid: no TLS configuration provided`, + Message: `spec.githubAPI.tls is valid: no TLS configuration provided: using default root CA bundle from container image`, }, }, }, @@ -686,7 +686,7 @@ func TestGitHubIDPSecretInOtherNamespace_Parallel(t *testing.T) { Type: "TLSConfigurationValid", Status: metav1.ConditionTrue, Reason: "Success", - Message: "spec.githubAPI.tls is valid: no TLS configuration provided", + Message: "spec.githubAPI.tls is valid: no TLS configuration provided: using default root CA bundle from container image", }, }) } diff --git a/test/integration/supervisor_login_test.go b/test/integration/supervisor_login_test.go index ee4490fa2..98442418f 100644 --- a/test/integration/supervisor_login_test.go +++ b/test/integration/supervisor_login_test.go @@ -2811,7 +2811,7 @@ func requireSuccessfulLDAPIdentityProviderConditions(t *testing.T, ldapIDP *idpv case "BindSecretValid": require.Equal(t, "loaded bind secret", condition.Message) case "TLSConfigurationValid": - require.Equal(t, "spec.tls is valid: loaded TLS configuration", condition.Message) + require.Equal(t, "spec.tls is valid: using configured CA bundle", condition.Message) case "LDAPConnectionValid": require.Equal(t, expectedLDAPConnectionValidMessage, condition.Message) } @@ -2836,7 +2836,7 @@ func requireSuccessfulActiveDirectoryIdentityProviderConditions(t *testing.T, ad case "BindSecretValid": require.Equal(t, "loaded bind secret", condition.Message) case "TLSConfigurationValid": - require.Equal(t, "spec.tls is valid: loaded TLS configuration", condition.Message) + require.Equal(t, "spec.tls is valid: using configured CA bundle", condition.Message) case "LDAPConnectionValid": require.Equal(t, expectedActiveDirectoryConnectionValidMessage, condition.Message) } @@ -2870,7 +2870,7 @@ func requireEventuallySuccessfulLDAPIdentityProviderConditions(t *testing.T, req case "BindSecretValid": requireEventually.Equal("loaded bind secret", condition.Message) case "TLSConfigurationValid": - requireEventually.Equal("spec.tls is valid: loaded TLS configuration", condition.Message) + requireEventually.Equal("spec.tls is valid: using configured CA bundle", condition.Message) case "LDAPConnectionValid": requireEventually.Equal(expectedLDAPConnectionValidMessage, condition.Message) } @@ -2896,7 +2896,7 @@ func requireEventuallySuccessfulActiveDirectoryIdentityProviderConditions(t *tes case "BindSecretValid": requireEventually.Equal("loaded bind secret", condition.Message) case "TLSConfigurationValid": - requireEventually.Equal("spec.tls is valid: loaded TLS configuration", condition.Message) + requireEventually.Equal("spec.tls is valid: using configured CA bundle", condition.Message) case "LDAPConnectionValid": requireEventually.Equal(expectedActiveDirectoryConnectionValidMessage, condition.Message) } diff --git a/test/integration/supervisor_upstream_test.go b/test/integration/supervisor_upstream_test.go index ab5ffcfd5..69f66f198 100644 --- a/test/integration/supervisor_upstream_test.go +++ b/test/integration/supervisor_upstream_test.go @@ -50,7 +50,7 @@ Get "https://127.0.0.1:444444/invalid-url-that-is-really-really-long-nananananan Type: "TLSConfigurationValid", Status: "True", Reason: "Success", - Message: `spec.tls is valid: no TLS configuration provided`, + Message: `spec.tls is valid: no TLS configuration provided: using default root CA bundle from container image`, }, }) }) @@ -94,7 +94,7 @@ oidc: issuer did not match the issuer returned by provider, expected "` + env.Su Type: "TLSConfigurationValid", Status: "True", Reason: "Success", - Message: `spec.tls is valid: loaded TLS configuration`, + Message: `spec.tls is valid: using configured CA bundle`, }, }) }) @@ -137,7 +137,7 @@ oidc: issuer did not match the issuer returned by provider, expected "` + env.Su Type: "TLSConfigurationValid", Status: "True", Reason: "Success", - Message: `spec.tls is valid: loaded TLS configuration`, + Message: `spec.tls is valid: using configured CA bundle`, }, }) }) From 3891f90f43f9446f0d5102a1a72cdc1c6397f34c Mon Sep 17 00:00:00 2001 From: Ryan Richard Date: Mon, 29 Jul 2024 11:15:51 -0700 Subject: [PATCH 68/99] skip external CA bundle tests when CA bundle is empty Co-authored-by: Ashish Amarnath --- .../concierge_jwtauthenticator_status_test.go | 16 +++--- test/integration/e2e_test.go | 55 +++++++++---------- test/integration/supervisor_login_test.go | 53 +++++++++++------- 3 files changed, 68 insertions(+), 56 deletions(-) diff --git a/test/integration/concierge_jwtauthenticator_status_test.go b/test/integration/concierge_jwtauthenticator_status_test.go index 3c199192a..26e71f732 100644 --- a/test/integration/concierge_jwtauthenticator_status_test.go +++ b/test/integration/concierge_jwtauthenticator_status_test.go @@ -21,6 +21,11 @@ import ( func TestConciergeJWTAuthenticatorWithExternalCABundleStatusIsUpdatedWhenExternalBundleIsUpdated_Parallel(t *testing.T) { env := testlib.IntegrationEnv(t) + + if len(env.SupervisorUpstreamOIDC.CABundle) == 0 { + t.Skip("skipping external CA bundle test because env.SupervisorUpstreamOIDC.CABundle is empty") + } + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute) t.Cleanup(cancel) @@ -128,7 +133,7 @@ func TestConciergeJWTAuthenticatorStatus_Parallel(t *testing.T) { }, }, wantPhase: authenticationv1alpha1.JWTAuthenticatorPhaseReady, - wantConditions: allSuccessfulJWTAuthenticatorConditions(true), + wantConditions: allSuccessfulJWTAuthenticatorConditions(len(env.SupervisorUpstreamOIDC.CABundle) != 0), }, { name: "valid spec with invalid CA in TLS config will result in a jwt authenticator that is not ready", @@ -218,11 +223,6 @@ func TestConciergeJWTAuthenticatorStatus_Parallel(t *testing.T) { Status: "False", Reason: "InvalidDiscoveryProbe", Message: `could not perform oidc discovery on provider issuer: Get "` + env.SupervisorUpstreamOIDC.Issuer + `/.well-known/openid-configuration": tls: failed to verify certificate: x509: certificate signed by unknown authority`, - }, { - Type: "TLSConfigurationValid", - Status: "True", - Reason: "Success", - Message: "spec.tls is valid: using configured CA bundle", }, }, ), @@ -238,7 +238,7 @@ func TestConciergeJWTAuthenticatorStatus_Parallel(t *testing.T) { }, wantPhase: authenticationv1alpha1.JWTAuthenticatorPhaseError, wantConditions: replaceSomeConditions( - allSuccessfulJWTAuthenticatorConditions(true), + allSuccessfulJWTAuthenticatorConditions(len(env.SupervisorUpstreamOIDC.CABundle) != 0), []metav1.Condition{ { Type: "Ready", @@ -422,7 +422,7 @@ func TestConciergeJWTAuthenticatorCRDValidations_Parallel(t *testing.T) { } func allSuccessfulJWTAuthenticatorConditions(caBundleExists bool) []metav1.Condition { - tlsConfigValidMsg := "no CA bundle specified" + tlsConfigValidMsg := "spec.tls is valid: no TLS configuration provided: using default root CA bundle from container image" if caBundleExists { tlsConfigValidMsg = "spec.tls is valid: using configured CA bundle" } diff --git a/test/integration/e2e_test.go b/test/integration/e2e_test.go index bd6e7bb08..89edb8d84 100644 --- a/test/integration/e2e_test.go +++ b/test/integration/e2e_test.go @@ -74,35 +74,35 @@ func TestE2EFullIntegration_Browser(t *testing.T) { // Generate a CA bundle with which to serve this provider. t.Logf("generating test CA") - ca, err := certauthority.New("Downstream Test CA", 1*time.Hour) + federationDomainSelfSignedCA, err := certauthority.New("Downstream Test CA", 1*time.Hour) require.NoError(t, err) // Save that bundle plus the one that signs the upstream issuer, for test purposes. - testCABundlePath := filepath.Join(t.TempDir(), "test-ca.pem") - testCABundlePEM := []byte(string(ca.Bundle()) + "\n" + env.SupervisorUpstreamOIDC.CABundle) - testCABundleBase64 := base64.StdEncoding.EncodeToString(testCABundlePEM) - require.NoError(t, os.WriteFile(testCABundlePath, testCABundlePEM, 0600)) + federationDomainCABundlePath := filepath.Join(t.TempDir(), "test-ca.pem") + federationDomainCABundlePEM := federationDomainSelfSignedCA.Bundle() + require.NoError(t, os.WriteFile(federationDomainCABundlePath, federationDomainCABundlePEM, 0600)) // Use the CA to issue a TLS server cert. t.Logf("issuing test certificate") - tlsCert, err := ca.IssueServerCert([]string{issuerURL.Hostname()}, nil, 1*time.Hour) + federationDomainTLSServingCert, err := federationDomainSelfSignedCA.IssueServerCert( + []string{issuerURL.Hostname()}, nil, 1*time.Hour) require.NoError(t, err) - certPEM, keyPEM, err := certauthority.ToPEM(tlsCert) + federationDomainTLSServingCertPEM, federationDomainTLSServingCertKeyPEM, err := certauthority.ToPEM(federationDomainTLSServingCert) require.NoError(t, err) // Write the serving cert to a secret. - certSecret := testlib.CreateTestSecret(t, + federationDomainTLSServingCertSecret := testlib.CreateTestSecret(t, env.SupervisorNamespace, "oidc-provider-tls", corev1.SecretTypeTLS, - map[string]string{"tls.crt": string(certPEM), "tls.key": string(keyPEM)}, + map[string]string{"tls.crt": string(federationDomainTLSServingCertPEM), "tls.key": string(federationDomainTLSServingCertKeyPEM)}, ) // Create the downstream FederationDomain and expect it to go into the success status condition. federationDomain := testlib.CreateTestFederationDomain(topSetupCtx, t, supervisorconfigv1alpha1.FederationDomainSpec{ Issuer: issuerURL.String(), - TLS: &supervisorconfigv1alpha1.FederationDomainTLSSpec{SecretName: certSecret.Name}, + TLS: &supervisorconfigv1alpha1.FederationDomainTLSSpec{SecretName: federationDomainTLSServingCertSecret.Name}, }, supervisorconfigv1alpha1.FederationDomainPhaseError, // in phase error until there is an IDP created ) @@ -113,7 +113,7 @@ func TestE2EFullIntegration_Browser(t *testing.T) { defaultJWTAuthenticatorSpec := authenticationv1alpha1.JWTAuthenticatorSpec{ Issuer: federationDomain.Spec.Issuer, Audience: clusterAudience, - TLS: &authenticationv1alpha1.TLSSpec{CertificateAuthorityData: testCABundleBase64}, + TLS: &authenticationv1alpha1.TLSSpec{CertificateAuthorityData: base64.StdEncoding.EncodeToString(federationDomainCABundlePEM)}, } // Add an OIDC upstream IDP and try using it to authenticate during kubectl commands. @@ -172,7 +172,7 @@ func TestE2EFullIntegration_Browser(t *testing.T) { "--concierge-authenticator-type", "jwt", "--concierge-authenticator-name", authenticator.Name, "--oidc-skip-browser", - "--oidc-ca-bundle", testCABundlePath, + "--oidc-ca-bundle", federationDomainCABundlePath, "--oidc-session-cache", sessionCachePath, "--credential-cache", credentialCachePath, // use default for --oidc-scopes, which is to request all relevant scopes @@ -231,7 +231,7 @@ func TestE2EFullIntegration_Browser(t *testing.T) { // in this test, use a secret of type TLS to source ca bundle for the JWT authenticator caSecret := testlib.CreateTestSecret(t, env.ConciergeNamespace, "ca-cert", corev1.SecretTypeTLS, map[string]string{ - "ca.crt": string(testCABundlePEM), + "ca.crt": string(federationDomainCABundlePEM), "tls.crt": "", "tls.key": "", }) @@ -243,6 +243,7 @@ func TestE2EFullIntegration_Browser(t *testing.T) { Key: "ca.crt", } authenticator := testlib.CreateTestJWTAuthenticator(testCtx, t, *jwtAuthnSpec, authenticationv1alpha1.JWTAuthenticatorPhaseError) + // Create upstream OIDC provider and wait for it to become ready. createdProvider := testlib.CreateTestOIDCIdentityProvider(t, idpv1alpha1.OIDCIdentityProviderSpec{ Issuer: env.SupervisorUpstreamOIDC.Issuer, @@ -273,7 +274,7 @@ func TestE2EFullIntegration_Browser(t *testing.T) { "--concierge-authenticator-type", "jwt", "--concierge-authenticator-name", authenticator.Name, "--oidc-skip-browser", - "--oidc-ca-bundle", testCABundlePath, + "--oidc-ca-bundle", federationDomainCABundlePath, "--oidc-session-cache", sessionCachePath, "--credential-cache", credentialCachePath, "--oidc-scopes", "offline_access,openid,pinniped:request-audience", // does not request username or groups @@ -334,9 +335,8 @@ func TestE2EFullIntegration_Browser(t *testing.T) { // in this test, use a secret of type opaque to source ca bundle for the JWT authenticator caSecret := testlib.CreateTestSecret(t, env.ConciergeNamespace, "ca-cert", corev1.SecretTypeOpaque, map[string]string{ - "ca.crt": string(testCABundlePEM), + "ca.crt": string(federationDomainCABundlePEM), }) - t.Logf("created secret %s/%s", caSecret.Namespace, caSecret.Name) jwtAuthnSpec := defaultJWTAuthenticatorSpec.DeepCopy() jwtAuthnSpec.TLS.CertificateAuthorityData = "" jwtAuthnSpec.TLS.CertificateAuthorityDataSource = &authenticationv1alpha1.CABundleSource{ @@ -344,9 +344,8 @@ func TestE2EFullIntegration_Browser(t *testing.T) { Name: caSecret.Name, Key: "ca.crt", } - authenticator := testlib.CreateTestJWTAuthenticator(testCtx, t, *jwtAuthnSpec, authenticationv1alpha1.JWTAuthenticatorPhaseError) - t.Logf("authenticator: %s/%s; concierge ns: %s", authenticator.Namespace, authenticator.Name, env.ConciergeNamespace) + // Create upstream OIDC provider and wait for it to become ready. createdProvider := testlib.CreateTestOIDCIdentityProvider(t, idpv1alpha1.OIDCIdentityProviderSpec{ Issuer: env.SupervisorUpstreamOIDC.Issuer, @@ -378,7 +377,7 @@ func TestE2EFullIntegration_Browser(t *testing.T) { "--concierge-authenticator-name", authenticator.Name, "--oidc-skip-browser", "--oidc-skip-listen", - "--oidc-ca-bundle", testCABundlePath, + "--oidc-ca-bundle", federationDomainCABundlePath, "--oidc-session-cache", sessionCachePath, "--credential-cache", credentialCachePath, // use default for --oidc-scopes, which is to request all relevant scopes @@ -474,7 +473,7 @@ func TestE2EFullIntegration_Browser(t *testing.T) { // in this test, use a configmap to source ca bundle for the JWT authenticator caConfigMap := testlib.CreateTestConfigMap(t, env.ConciergeNamespace, "ca-cert", map[string]string{ - "ca.crt": string(testCABundlePEM), + "ca.crt": string(federationDomainCABundlePEM), }) jwtAuthnSpec := defaultJWTAuthenticatorSpec.DeepCopy() jwtAuthnSpec.TLS.CertificateAuthorityData = "" @@ -483,8 +482,8 @@ func TestE2EFullIntegration_Browser(t *testing.T) { Name: caConfigMap.Name, Key: "ca.crt", } + authenticator := testlib.CreateTestJWTAuthenticator(testCtx, t, *jwtAuthnSpec, authenticationv1alpha1.JWTAuthenticatorPhaseError) - authenticator := testlib.CreateTestJWTAuthenticator(testCtx, t, defaultJWTAuthenticatorSpec, authenticationv1alpha1.JWTAuthenticatorPhaseError) // Create upstream OIDC provider and wait for it to become ready. createdProvider := testlib.CreateTestOIDCIdentityProvider(t, idpv1alpha1.OIDCIdentityProviderSpec{ Issuer: env.SupervisorUpstreamOIDC.Issuer, @@ -516,7 +515,7 @@ func TestE2EFullIntegration_Browser(t *testing.T) { "--concierge-authenticator-name", authenticator.Name, "--oidc-skip-browser", "--oidc-skip-listen", - "--oidc-ca-bundle", testCABundlePath, + "--oidc-ca-bundle", federationDomainCABundlePath, "--oidc-session-cache", sessionCachePath, "--credential-cache", credentialCachePath, // use default for --oidc-scopes, which is to request all relevant scopes @@ -649,7 +648,7 @@ func TestE2EFullIntegration_Browser(t *testing.T) { "--oidc-skip-browser", "--oidc-skip-listen", "--upstream-identity-provider-flow", "cli_password", // create a kubeconfig configured to use the cli_password flow - "--oidc-ca-bundle", testCABundlePath, + "--oidc-ca-bundle", federationDomainCABundlePath, "--oidc-session-cache", sessionCachePath, "--credential-cache", credentialCachePath, // use default for --oidc-scopes, which is to request all relevant scopes @@ -729,7 +728,7 @@ func TestE2EFullIntegration_Browser(t *testing.T) { "--upstream-identity-provider-name", oidcIdentityProvider.Name, "--upstream-identity-provider-type", "oidc", "--upstream-identity-provider-flow", "cli_password", - "--oidc-ca-bundle", testCABundlePath, + "--oidc-ca-bundle", federationDomainCABundlePath, "--oidc-session-cache", sessionCachePath, "--credential-cache", credentialCachePath, // use default for --oidc-scopes, which is to request all relevant scopes @@ -1116,7 +1115,7 @@ func TestE2EFullIntegration_Browser(t *testing.T) { "--concierge-authenticator-type", "jwt", "--concierge-authenticator-name", authenticator.Name, "--oidc-skip-browser", - "--oidc-ca-bundle", testCABundlePath, + "--oidc-ca-bundle", federationDomainCABundlePath, "--upstream-identity-provider-flow", "browser_authcode", "--oidc-session-cache", sessionCachePath, "--credential-cache", credentialCachePath, @@ -1172,7 +1171,7 @@ func TestE2EFullIntegration_Browser(t *testing.T) { "--concierge-authenticator-type", "jwt", "--concierge-authenticator-name", authenticator.Name, "--oidc-skip-browser", - "--oidc-ca-bundle", testCABundlePath, + "--oidc-ca-bundle", federationDomainCABundlePath, "--upstream-identity-provider-flow", "browser_authcode", "--oidc-session-cache", sessionCachePath, "--credential-cache", credentialCachePath, @@ -1228,7 +1227,7 @@ func TestE2EFullIntegration_Browser(t *testing.T) { "--concierge-authenticator-type", "jwt", "--concierge-authenticator-name", authenticator.Name, "--oidc-skip-browser", - "--oidc-ca-bundle", testCABundlePath, + "--oidc-ca-bundle", federationDomainCABundlePath, "--upstream-identity-provider-flow", "cli_password", // put cli_password in the kubeconfig, so we can override it with the env var "--oidc-session-cache", sessionCachePath, "--credential-cache", credentialCachePath, @@ -1317,7 +1316,7 @@ func TestE2EFullIntegration_Browser(t *testing.T) { "--concierge-authenticator-type", "jwt", "--concierge-authenticator-name", authenticator.Name, "--oidc-skip-browser", - "--oidc-ca-bundle", testCABundlePath, + "--oidc-ca-bundle", federationDomainCABundlePath, "--oidc-session-cache", sessionCachePath, "--credential-cache", credentialCachePath, // use default for --oidc-scopes, which is to request all relevant scopes diff --git a/test/integration/supervisor_login_test.go b/test/integration/supervisor_login_test.go index 98442418f..d6482e36c 100644 --- a/test/integration/supervisor_login_test.go +++ b/test/integration/supervisor_login_test.go @@ -178,6 +178,29 @@ func TestSupervisorLogin_Browser(t *testing.T) { testlib.SkipTestWhenActiveDirectoryIsUnavailable(t, env) } + skipExternalCABundleOIDCTestsWhenCABundleIsEmpty := func(t *testing.T) { + t.Helper() + if len(env.SupervisorUpstreamOIDC.CABundle) == 0 { + t.Skip("skipping external CA bundle test because env.SupervisorUpstreamOIDC.CABundle is empty") + } + } + + skipExternalCABundleLDAPTestsWhenCABundleIsEmpty := func(t *testing.T) { + t.Helper() + skipLDAPTests(t) + if len(env.SupervisorUpstreamLDAP.CABundle) == 0 { + t.Skip("skipping external CA bundle test because env.SupervisorUpstreamLDAP.CABundle is empty") + } + } + + skipExternalCABundleActiveDirectoryTestsWhenCABundleIsEmpty := func(t *testing.T) { + t.Helper() + skipActiveDirectoryTests(t) + if len(env.SupervisorUpstreamActiveDirectory.CABundle) == 0 { + t.Skip("skipping external CA bundle test because env.SupervisorUpstreamActiveDirectory.CABundle is empty") + } + } + basicOIDCIdentityProviderSpec := func() idpv1alpha1.OIDCIdentityProviderSpec { return idpv1alpha1.OIDCIdentityProviderSpec{ Issuer: env.SupervisorUpstreamOIDC.Issuer, @@ -340,7 +363,7 @@ func TestSupervisorLogin_Browser(t *testing.T) { }, { name: "oidc IDP using secrets of type opaque to source ca bundle with default username and groups claim settings", - maybeSkip: skipNever, + maybeSkip: skipExternalCABundleOIDCTestsWhenCABundleIsEmpty, createIDP: func(t *testing.T) string { idpSpec := basicOIDCIdentityProviderSpec() caData, err := base64.StdEncoding.DecodeString(idpSpec.TLS.CertificateAuthorityData) @@ -355,7 +378,6 @@ func TestSupervisorLogin_Browser(t *testing.T) { Name: caSecret.Name, Key: "ca.crt", } - return testlib.CreateTestOIDCIdentityProvider(t, idpSpec, idpv1alpha1.PhaseReady).Name }, requestAuthorization: requestAuthorizationUsingBrowserAuthcodeFlowOIDC, @@ -369,7 +391,7 @@ func TestSupervisorLogin_Browser(t *testing.T) { }, { name: "oidc IDP using secrets of type TLS to source ca bundle with default username and groups claim settings", - maybeSkip: skipNever, + maybeSkip: skipExternalCABundleOIDCTestsWhenCABundleIsEmpty, createIDP: func(t *testing.T) string { idpSpec := basicOIDCIdentityProviderSpec() caData, err := base64.StdEncoding.DecodeString(idpSpec.TLS.CertificateAuthorityData) @@ -386,7 +408,6 @@ func TestSupervisorLogin_Browser(t *testing.T) { Name: caSecret.Name, Key: "ca.crt", } - return testlib.CreateTestOIDCIdentityProvider(t, idpSpec, idpv1alpha1.PhaseReady).Name }, requestAuthorization: requestAuthorizationUsingBrowserAuthcodeFlowOIDC, @@ -400,7 +421,7 @@ func TestSupervisorLogin_Browser(t *testing.T) { }, { name: "oidc IDP using configmaps to source ca bundle with default username and groups claim settings", - maybeSkip: skipNever, + maybeSkip: skipExternalCABundleOIDCTestsWhenCABundleIsEmpty, createIDP: func(t *testing.T) string { idpSpec := basicOIDCIdentityProviderSpec() caData, err := base64.StdEncoding.DecodeString(idpSpec.TLS.CertificateAuthorityData) @@ -414,7 +435,6 @@ func TestSupervisorLogin_Browser(t *testing.T) { Name: caConfigMap.Name, Key: "ca.crt", } - return testlib.CreateTestOIDCIdentityProvider(t, idpSpec, idpv1alpha1.PhaseReady).Name }, requestAuthorization: requestAuthorizationUsingBrowserAuthcodeFlowOIDC, @@ -429,7 +449,7 @@ func TestSupervisorLogin_Browser(t *testing.T) { { name: "oidc IDP using secrets of type opaque to source ca bundle with default username and groups claim settings", - maybeSkip: skipNever, + maybeSkip: skipExternalCABundleOIDCTestsWhenCABundleIsEmpty, createIDP: func(t *testing.T) string { idpSpec := basicOIDCIdentityProviderSpec() caData, err := base64.StdEncoding.DecodeString(idpSpec.TLS.CertificateAuthorityData) @@ -444,7 +464,6 @@ func TestSupervisorLogin_Browser(t *testing.T) { Name: caSecret.Name, Key: "ca.crt", } - return testlib.CreateTestOIDCIdentityProvider(t, idpSpec, idpv1alpha1.PhaseReady).Name }, requestAuthorization: requestAuthorizationUsingBrowserAuthcodeFlowOIDC, @@ -655,14 +674,13 @@ func TestSupervisorLogin_Browser(t *testing.T) { }, { name: "ldap IDP using secrets of type opaque to source ca bundle and with email as username and groups names as DNs and using an LDAP provider which supports TLS", - maybeSkip: skipLDAPTests, + maybeSkip: skipExternalCABundleLDAPTestsWhenCABundleIsEmpty, createIDP: func(t *testing.T) string { idp, _ := createLDAPIdentityProvider(t, func(spec *idpv1alpha1.LDAPIdentityProviderSpec) { caSecret := testlib.CreateTestSecret(t, env.SupervisorNamespace, "ca-cert", corev1.SecretTypeOpaque, map[string]string{ "ca.crt": env.SupervisorUpstreamLDAP.CABundle, }) - spec.TLS.CertificateAuthorityData = "" spec.TLS.CertificateAuthorityDataSource = &idpv1alpha1.CABundleSource{ Kind: "Secret", @@ -705,7 +723,7 @@ func TestSupervisorLogin_Browser(t *testing.T) { }, { name: "ldap IDP using secrets of type TLS to source ca bundle and with email as username and groups names as DNs and using an LDAP provider which supports TLS", - maybeSkip: skipLDAPTests, + maybeSkip: skipExternalCABundleLDAPTestsWhenCABundleIsEmpty, createIDP: func(t *testing.T) string { idp, _ := createLDAPIdentityProvider(t, func(spec *idpv1alpha1.LDAPIdentityProviderSpec) { caSecret := testlib.CreateTestSecret(t, env.SupervisorNamespace, "ca-cert", corev1.SecretTypeTLS, @@ -714,7 +732,6 @@ func TestSupervisorLogin_Browser(t *testing.T) { "tls.crt": "", "tls.key": "", }) - spec.TLS.CertificateAuthorityData = "" spec.TLS.CertificateAuthorityDataSource = &idpv1alpha1.CABundleSource{ Kind: "Secret", @@ -757,14 +774,13 @@ func TestSupervisorLogin_Browser(t *testing.T) { }, { name: "ldap IDP using configmaps to source ca bundle and with email as username and groups names as DNs and using an LDAP provider which supports TLS", - maybeSkip: skipLDAPTests, + maybeSkip: skipExternalCABundleLDAPTestsWhenCABundleIsEmpty, createIDP: func(t *testing.T) string { idp, _ := createLDAPIdentityProvider(t, func(spec *idpv1alpha1.LDAPIdentityProviderSpec) { caConfigMap := testlib.CreateTestConfigMap(t, env.SupervisorNamespace, "ca-cert", map[string]string{ "ca.crt": env.SupervisorUpstreamLDAP.CABundle, }) - spec.TLS.CertificateAuthorityData = "" spec.TLS.CertificateAuthorityDataSource = &idpv1alpha1.CABundleSource{ Kind: "ConfigMap", @@ -1242,14 +1258,13 @@ func TestSupervisorLogin_Browser(t *testing.T) { }, { name: "active directory IDP using secret of type opaque to source ca bundle with all default options", - maybeSkip: skipActiveDirectoryTests, + maybeSkip: skipExternalCABundleActiveDirectoryTestsWhenCABundleIsEmpty, createIDP: func(t *testing.T) string { idp, _ := createActiveDirectoryIdentityProvider(t, func(spec *idpv1alpha1.ActiveDirectoryIdentityProviderSpec) { caSecret := testlib.CreateTestSecret(t, env.SupervisorNamespace, "ca-cert", corev1.SecretTypeOpaque, map[string]string{ "ca.crt": env.SupervisorUpstreamActiveDirectory.CABundle, }) - spec.TLS.CertificateAuthorityData = "" spec.TLS.CertificateAuthorityDataSource = &idpv1alpha1.CABundleSource{ Kind: "Secret", @@ -1283,7 +1298,7 @@ func TestSupervisorLogin_Browser(t *testing.T) { }, { name: "active directory IDP using secret of type TLS to source ca bundle with all default options", - maybeSkip: skipActiveDirectoryTests, + maybeSkip: skipExternalCABundleActiveDirectoryTestsWhenCABundleIsEmpty, createIDP: func(t *testing.T) string { idp, _ := createActiveDirectoryIdentityProvider(t, func(spec *idpv1alpha1.ActiveDirectoryIdentityProviderSpec) { caSecret := testlib.CreateTestSecret(t, env.SupervisorNamespace, "ca-cert", corev1.SecretTypeTLS, @@ -1292,7 +1307,6 @@ func TestSupervisorLogin_Browser(t *testing.T) { "tls.crt": "", "tls.key": "", }) - spec.TLS.CertificateAuthorityData = "" spec.TLS.CertificateAuthorityDataSource = &idpv1alpha1.CABundleSource{ Kind: "Secret", @@ -1326,14 +1340,13 @@ func TestSupervisorLogin_Browser(t *testing.T) { }, { name: "active directory IDP using configmaps to source ca bundle with all default options", - maybeSkip: skipActiveDirectoryTests, + maybeSkip: skipExternalCABundleActiveDirectoryTestsWhenCABundleIsEmpty, createIDP: func(t *testing.T) string { idp, _ := createActiveDirectoryIdentityProvider(t, func(spec *idpv1alpha1.ActiveDirectoryIdentityProviderSpec) { caConfigMap := testlib.CreateTestConfigMap(t, env.SupervisorNamespace, "ca-cert", map[string]string{ "ca.crt": env.SupervisorUpstreamActiveDirectory.CABundle, }) - spec.TLS.CertificateAuthorityData = "" spec.TLS.CertificateAuthorityDataSource = &idpv1alpha1.CABundleSource{ Kind: "Secret", From 8725ab4caac6e6f0cfd9c9ef3683388d29d3c7df Mon Sep 17 00:00:00 2001 From: Ryan Richard Date: Mon, 29 Jul 2024 11:35:45 -0700 Subject: [PATCH 69/99] do not make any assumption about OIDC issuer 404 page body in test Instead of using Dex or Okta, use a fake localhost issuer which does not exist. This will give a consistent connection error message. Needed because Dex and Okta return different 404 error pages, so we can't easily make a test assertion that works for both. Co-authored-by: Ashish Amarnath --- .../concierge_jwtauthenticator_status_test.go | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/test/integration/concierge_jwtauthenticator_status_test.go b/test/integration/concierge_jwtauthenticator_status_test.go index 26e71f732..f2721a45c 100644 --- a/test/integration/concierge_jwtauthenticator_status_test.go +++ b/test/integration/concierge_jwtauthenticator_status_test.go @@ -117,6 +117,8 @@ func TestConciergeJWTAuthenticatorStatus_Parallel(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute) t.Cleanup(cancel) + unusedLocalhostPort := findRecentlyUnusedLocalhostPorts(t, 1)[0] + tests := []struct { name string spec authenticationv1alpha1.JWTAuthenticatorSpec @@ -219,10 +221,12 @@ func TestConciergeJWTAuthenticatorStatus_Parallel(t *testing.T) { Reason: "UnableToValidate", Message: "unable to validate; see other conditions for details", }, { - Type: "DiscoveryURLValid", - Status: "False", - Reason: "InvalidDiscoveryProbe", - Message: `could not perform oidc discovery on provider issuer: Get "` + env.SupervisorUpstreamOIDC.Issuer + `/.well-known/openid-configuration": tls: failed to verify certificate: x509: certificate signed by unknown authority`, + Type: "DiscoveryURLValid", + Status: "False", + Reason: "InvalidDiscoveryProbe", + Message: `could not perform oidc discovery on provider issuer: Get "` + + env.SupervisorUpstreamOIDC.Issuer + + `/.well-known/openid-configuration": tls: failed to verify certificate: x509: certificate signed by unknown authority`, }, }, ), @@ -230,7 +234,7 @@ func TestConciergeJWTAuthenticatorStatus_Parallel(t *testing.T) { { name: "invalid with bad issuer will result in a jwt authenticator that is not ready", spec: authenticationv1alpha1.JWTAuthenticatorSpec{ - Issuer: env.SupervisorUpstreamOIDC.Issuer + "/this-is-the-wrong-path", + Issuer: fmt.Sprintf("https://127.0.0.1:%s/some-fake-issuer-path", unusedLocalhostPort), Audience: "some-fake-audience", TLS: &authenticationv1alpha1.TLSSpec{ CertificateAuthorityData: base64.StdEncoding.EncodeToString([]byte(env.SupervisorUpstreamOIDC.CABundle)), @@ -261,10 +265,14 @@ func TestConciergeJWTAuthenticatorStatus_Parallel(t *testing.T) { Reason: "UnableToValidate", Message: "unable to validate; see other conditions for details", }, { - Type: "DiscoveryURLValid", - Status: "False", - Reason: "InvalidDiscoveryProbe", - Message: "could not perform oidc discovery on provider issuer: 404 Not Found: 404 page not found\n", + Type: "DiscoveryURLValid", + Status: "False", + Reason: "InvalidDiscoveryProbe", + Message: fmt.Sprintf( + `could not perform oidc discovery on provider issuer: `+ + `Get "https://127.0.0.1:%s/some-fake-issuer-path/.well-known/openid-configuration": `+ + `dial tcp 127.0.0.1:%s: connect: connection refused`, + unusedLocalhostPort, unusedLocalhostPort), }, }, ), @@ -310,7 +318,7 @@ func TestConciergeJWTAuthenticatorCRDValidations_Parallel(t *testing.T) { jwtAuthenticator: &authenticationv1alpha1.JWTAuthenticator{ ObjectMeta: objectMeta, Spec: authenticationv1alpha1.JWTAuthenticatorSpec{ - Issuer: "https://example.com", + Issuer: env.CLIUpstreamOIDC.Issuer, Audience: "", }, }, From 290676e4d185f25978025f439e9e8ed47d6407ed Mon Sep 17 00:00:00 2001 From: Ryan Richard Date: Mon, 29 Jul 2024 14:46:18 -0700 Subject: [PATCH 70/99] improve info/debug log messages for jwtcachefiller & webhookcachefiller Co-authored-by: Ashish Amarnath --- .../jwtcachefiller/jwtcachefiller.go | 22 +- .../jwtcachefiller/jwtcachefiller_test.go | 379 ++++++++++++++---- .../webhookcachefiller/webhookcachefiller.go | 25 +- .../webhookcachefiller_test.go | 364 +++++++++++++---- .../concierge_jwtauthenticator_status_test.go | 3 + ...cierge_webhookauthenticator_status_test.go | 17 +- 6 files changed, 645 insertions(+), 165 deletions(-) diff --git a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go index 6bd3b885c..e1682d72c 100644 --- a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go +++ b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go @@ -239,7 +239,7 @@ func (c *jwtCacheFillerController) syncIndividualJWTAuthenticator(ctx context.Co tlsBundleOk && // if there was any error while validating the CA bundle, then run remaining validations and update status oldJWTAuthenticatorFromCache.caBundleHash.Equal(caBundle.Hash()) { c.log.WithValues("jwtAuthenticator", klog.KObj(jwtAuthenticator), "issuer", jwtAuthenticator.Spec.Issuer). - Info("actual jwt authenticator and desired jwt authenticator are the same") + Info("cached jwt authenticator and desired jwt authenticator are the same: already cached, so skipping validations") // Stop, no more work to be done. This authenticator is already validated and cached. return nil } @@ -283,6 +283,11 @@ func (c *jwtCacheFillerController) syncIndividualJWTAuthenticator(ctx context.Co // validated and cached. Do not allow an old, previously validated spec of the authenticator to continue // being used for authentication. c.cache.Delete(cacheKey) + c.log.WithValues( + "jwtAuthenticator", klog.KObj(jwtAuthenticator), + "issuer", jwtAuthenticator.Spec.Issuer, + "removedFromCache", oldJWTAuthenticatorFromCache != nil, + ).Info("invalid jwt authenticator") } updateErr := c.updateStatus(ctx, jwtAuthenticator, conditions) @@ -293,8 +298,11 @@ func (c *jwtCacheFillerController) syncIndividualJWTAuthenticator(ctx context.Co // and skip trying to update its status again, which would leave its old status permanently intact. if authenticatorValid && updateErr == nil { c.cache.Store(cacheKey, newJWTAuthenticatorForCache) - c.log.WithValues("jwtAuthenticator", klog.KObj(jwtAuthenticator), "issuer", jwtAuthenticator.Spec.Issuer). - Info("added new jwt authenticator") + c.log.WithValues( + "jwtAuthenticator", klog.KObj(jwtAuthenticator), + "issuer", jwtAuthenticator.Spec.Issuer, + "isOverwrite", oldJWTAuthenticatorFromCache != nil, + ).Info("added or updated jwt authenticator in cache") } // Sync loop errors: @@ -669,11 +677,14 @@ func (c *jwtCacheFillerController) updateStatus( }) } + // TODO: this should use c.log.WithValues("jwtAuthenticator", original.Name) + log := plog.New().WithName(controllerName).WithValues("jwtAuthenticator", original.Name) + _ = conditionsutil.MergeConditions( conditions, original.Generation, &updated.Status.Conditions, - plog.New().WithName(controllerName), + log, metav1.NewTime(c.clock.Now()), ) @@ -681,5 +692,8 @@ func (c *jwtCacheFillerController) updateStatus( return nil } _, err := c.client.AuthenticationV1alpha1().JWTAuthenticators().UpdateStatus(ctx, updated, metav1.UpdateOptions{}) + if err == nil { + log.Debug("jwtauthenticator status successfully updated") + } return err } diff --git a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller_test.go b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller_test.go index a8005650b..8c2e16b08 100644 --- a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller_test.go +++ b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller_test.go @@ -755,11 +755,12 @@ func TestController(t *testing.T) { }, }, wantLogs: []map[string]any{{ - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "jwtcachefiller-controller", - "message": "added new jwt authenticator", - "issuer": goodIssuer, + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "jwtcachefiller-controller", + "message": "added or updated jwt authenticator in cache", + "isOverwrite": false, + "issuer": goodIssuer, "jwtAuthenticator": map[string]any{ "name": "test-name", }, @@ -812,21 +813,45 @@ func TestController(t *testing.T) { ), wantLogs: []map[string]any{ { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "jwtcachefiller-controller", - "message": "added new jwt authenticator", - "issuer": goodIssuer, + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "jwtcachefiller-controller", + "message": "invalid jwt authenticator", + "removedFromCache": false, + "issuer": badIssuerJWKSURIJWTAuthenticatorSpec.Issuer, + "jwtAuthenticator": map[string]any{ + "name": "another-invalid-jwt-authenticator", + }, + }, + { + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "jwtcachefiller-controller", + "message": "added or updated jwt authenticator in cache", + "isOverwrite": false, + "issuer": goodIssuer, "jwtAuthenticator": map[string]any{ "name": "existing-jwt-authenticator", }, }, { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "jwtcachefiller-controller", - "message": "added new jwt authenticator", - "issuer": goodIssuer, + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "jwtcachefiller-controller", + "message": "invalid jwt authenticator", + "removedFromCache": false, + "issuer": badIssuerJWKSURIJWTAuthenticatorSpec.Issuer, + "jwtAuthenticator": map[string]any{ + "name": "invalid-jwt-authenticator", + }, + }, + { + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "jwtcachefiller-controller", + "message": "added or updated jwt authenticator in cache", + "isOverwrite": false, + "issuer": goodIssuer, "jwtAuthenticator": map[string]any{ "name": "new-jwt-authenticator", }, @@ -926,11 +951,12 @@ func TestController(t *testing.T) { }, }, wantLogs: []map[string]any{{ - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "jwtcachefiller-controller", - "message": "added new jwt authenticator", - "issuer": goodIssuer, + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "jwtcachefiller-controller", + "message": "added or updated jwt authenticator in cache", + "isOverwrite": false, + "issuer": goodIssuer, "jwtAuthenticator": map[string]any{ "name": "test-name", }, @@ -973,11 +999,12 @@ func TestController(t *testing.T) { }, }, wantLogs: []map[string]any{{ - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "jwtcachefiller-controller", - "message": "added new jwt authenticator", - "issuer": goodIssuer, + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "jwtcachefiller-controller", + "message": "added or updated jwt authenticator in cache", + "isOverwrite": false, + "issuer": goodIssuer, "jwtAuthenticator": map[string]any{ "name": "test-name", }, @@ -1016,11 +1043,12 @@ func TestController(t *testing.T) { someSecretWithCA, }, wantLogs: []map[string]any{{ - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "jwtcachefiller-controller", - "message": "added new jwt authenticator", - "issuer": goodIssuer, + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "jwtcachefiller-controller", + "message": "added or updated jwt authenticator in cache", + "isOverwrite": false, + "issuer": goodIssuer, "jwtAuthenticator": map[string]any{ "name": "test-name", }, @@ -1059,11 +1087,12 @@ func TestController(t *testing.T) { someConfigMapWithCA, }, wantLogs: []map[string]any{{ - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "jwtcachefiller-controller", - "message": "added new jwt authenticator", - "issuer": goodIssuer, + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "jwtcachefiller-controller", + "message": "added or updated jwt authenticator in cache", + "isOverwrite": false, + "issuer": goodIssuer, "jwtAuthenticator": map[string]any{ "name": "test-name", }, @@ -1099,11 +1128,12 @@ func TestController(t *testing.T) { }, }, wantLogs: []map[string]any{{ - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "jwtcachefiller-controller", - "message": "added new jwt authenticator", - "issuer": goodIssuer, + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "jwtcachefiller-controller", + "message": "added or updated jwt authenticator in cache", + "isOverwrite": false, + "issuer": goodIssuer, "jwtAuthenticator": map[string]any{ "name": "test-name", }, @@ -1140,11 +1170,12 @@ func TestController(t *testing.T) { }, }, wantLogs: []map[string]any{{ - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "jwtcachefiller-controller", - "message": "added new jwt authenticator", - "issuer": goodIssuer, + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "jwtcachefiller-controller", + "message": "added or updated jwt authenticator in cache", + "isOverwrite": false, + "issuer": goodIssuer, "jwtAuthenticator": map[string]any{ "name": "test-name", }, @@ -1193,11 +1224,12 @@ func TestController(t *testing.T) { }, }, wantLogs: []map[string]any{{ - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "jwtcachefiller-controller", - "message": "added new jwt authenticator", - "issuer": goodIssuer, + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "jwtcachefiller-controller", + "message": "added or updated jwt authenticator in cache", + "isOverwrite": true, + "issuer": goodIssuer, "jwtAuthenticator": map[string]any{ "name": "test-name", }, @@ -1302,11 +1334,12 @@ func TestController(t *testing.T) { someSecretWithCA, }, wantLogs: []map[string]any{{ - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "jwtcachefiller-controller", - "message": "added new jwt authenticator", - "issuer": goodIssuer, + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "jwtcachefiller-controller", + "message": "added or updated jwt authenticator in cache", + "isOverwrite": true, + "issuer": goodIssuer, "jwtAuthenticator": map[string]any{ "name": "test-name", }, @@ -1358,7 +1391,7 @@ func TestController(t *testing.T) { "level": "info", "timestamp": "2099-08-08T13:57:36.123456Z", "logger": "jwtcachefiller-controller", - "message": "actual jwt authenticator and desired jwt authenticator are the same", + "message": "cached jwt authenticator and desired jwt authenticator are the same: already cached, so skipping validations", "issuer": goodIssuer, "jwtAuthenticator": map[string]any{ "name": "test-name", @@ -1413,11 +1446,12 @@ func TestController(t *testing.T) { "actualType": "*mockcachevalue.MockValue", }, { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "jwtcachefiller-controller", - "message": "added new jwt authenticator", - "issuer": goodIssuer, + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "jwtcachefiller-controller", + "message": "added or updated jwt authenticator in cache", + "isOverwrite": false, + "issuer": goodIssuer, "jwtAuthenticator": map[string]any{ "name": "test-name", }, @@ -1453,6 +1487,19 @@ func TestController(t *testing.T) { Spec: *missingTLSJWTAuthenticatorSpec, }, }, + wantLogs: []map[string]any{ + { + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "jwtcachefiller-controller", + "message": "invalid jwt authenticator", + "removedFromCache": false, + "issuer": missingTLSJWTAuthenticatorSpec.Issuer, + "jwtAuthenticator": map[string]any{ + "name": "test-name", + }, + }, + }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -1495,6 +1542,19 @@ func TestController(t *testing.T) { Spec: *invalidTLSJWTAuthenticatorSpec, }, }, + wantLogs: []map[string]any{ + { + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "jwtcachefiller-controller", + "message": "invalid jwt authenticator", + "removedFromCache": false, + "issuer": invalidTLSJWTAuthenticatorSpec.Issuer, + "jwtAuthenticator": map[string]any{ + "name": "test-name", + }, + }, + }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -1548,6 +1608,19 @@ func TestController(t *testing.T) { Spec: *invalidTLSJWTAuthenticatorSpec, }, }, + wantLogs: []map[string]any{ + { + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "jwtcachefiller-controller", + "message": "invalid jwt authenticator", + "removedFromCache": true, + "issuer": invalidTLSJWTAuthenticatorSpec.Issuer, + "jwtAuthenticator": map[string]any{ + "name": "test-name", + }, + }, + }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -1600,6 +1673,19 @@ func TestController(t *testing.T) { Spec: *invalidIssuerJWTAuthenticatorSpec, }, }, + wantLogs: []map[string]any{ + { + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "jwtcachefiller-controller", + "message": "invalid jwt authenticator", + "removedFromCache": true, + "issuer": invalidIssuerJWTAuthenticatorSpec.Issuer, + "jwtAuthenticator": map[string]any{ + "name": "test-name", + }, + }, + }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -1641,6 +1727,19 @@ func TestController(t *testing.T) { Spec: *invalidIssuerJWTAuthenticatorSpec, }, }, + wantLogs: []map[string]any{ + { + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "jwtcachefiller-controller", + "message": "invalid jwt authenticator", + "removedFromCache": false, + "issuer": invalidIssuerJWTAuthenticatorSpec.Issuer, + "jwtAuthenticator": map[string]any{ + "name": "test-name", + }, + }, + }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -1680,6 +1779,19 @@ func TestController(t *testing.T) { Spec: *invalidIssuerSchemeJWTAuthenticatorSpec, }, }, + wantLogs: []map[string]any{ + { + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "jwtcachefiller-controller", + "message": "invalid jwt authenticator", + "removedFromCache": false, + "issuer": invalidIssuerSchemeJWTAuthenticatorSpec.Issuer, + "jwtAuthenticator": map[string]any{ + "name": "test-name", + }, + }, + }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -1723,6 +1835,19 @@ func TestController(t *testing.T) { }, }, }, + wantLogs: []map[string]any{ + { + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "jwtcachefiller-controller", + "message": "invalid jwt authenticator", + "removedFromCache": false, + "issuer": "https://www.example.com/foo/bar/#do-not-include-fragment", + "jwtAuthenticator": map[string]any{ + "name": "test-name", + }, + }, + }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -1770,6 +1895,19 @@ func TestController(t *testing.T) { }, }, }, + wantLogs: []map[string]any{ + { + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "jwtcachefiller-controller", + "message": "invalid jwt authenticator", + "removedFromCache": false, + "issuer": "https://www.example.com/foo/bar/?query-params=not-allowed", + "jwtAuthenticator": map[string]any{ + "name": "test-name", + }, + }, + }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -1817,6 +1955,19 @@ func TestController(t *testing.T) { }, }, }, + wantLogs: []map[string]any{ + { + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "jwtcachefiller-controller", + "message": "invalid jwt authenticator", + "removedFromCache": false, + "issuer": "https://www.example.com/foo/bar/.well-known/openid-configuration", + "jwtAuthenticator": map[string]any{ + "name": "test-name", + }, + }, + }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -1860,6 +2011,19 @@ func TestController(t *testing.T) { Spec: *validIssuerURLButDoesNotExistJWTAuthenticatorSpec, }, }, + wantLogs: []map[string]any{ + { + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "jwtcachefiller-controller", + "message": "invalid jwt authenticator", + "removedFromCache": false, + "issuer": validIssuerURLButDoesNotExistJWTAuthenticatorSpec.Issuer, + "jwtAuthenticator": map[string]any{ + "name": "test-name", + }, + }, + }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -1905,6 +2069,19 @@ func TestController(t *testing.T) { }, }, }, + wantLogs: []map[string]any{ + { + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "jwtcachefiller-controller", + "message": "invalid jwt authenticator", + "removedFromCache": false, + "issuer": goodIssuer + "/path/to/not/found", + "jwtAuthenticator": map[string]any{ + "name": "test-name", + }, + }, + }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -1955,6 +2132,19 @@ func TestController(t *testing.T) { Spec: *badIssuerJWKSURIJWTAuthenticatorSpec, }, }, + wantLogs: []map[string]any{ + { + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "jwtcachefiller-controller", + "message": "invalid jwt authenticator", + "removedFromCache": false, + "issuer": badIssuerJWKSURIJWTAuthenticatorSpec.Issuer, + "jwtAuthenticator": map[string]any{ + "name": "test-name", + }, + }, + }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -1994,6 +2184,19 @@ func TestController(t *testing.T) { Spec: *badIssuerJWKSURISchemeJWTAuthenticatorSpec, }, }, + wantLogs: []map[string]any{ + { + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "jwtcachefiller-controller", + "message": "invalid jwt authenticator", + "removedFromCache": false, + "issuer": badIssuerJWKSURISchemeJWTAuthenticatorSpec.Issuer, + "jwtAuthenticator": map[string]any{ + "name": "test-name", + }, + }, + }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -2033,6 +2236,19 @@ func TestController(t *testing.T) { Spec: *badIssuerUsesOurHardcodedPrivateKeyJWTAuthenticatorSpec, }, }, + wantLogs: []map[string]any{ + { + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "jwtcachefiller-controller", + "message": "invalid jwt authenticator", + "removedFromCache": false, + "issuer": badIssuerUsesOurHardcodedPrivateKeyJWTAuthenticatorSpec.Issuer, + "jwtAuthenticator": map[string]any{ + "name": "test-name", + }, + }, + }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -2073,6 +2289,19 @@ func TestController(t *testing.T) { Spec: *jwksFetchShouldFailJWTAuthenticatorSpec, }, }, + wantLogs: []map[string]any{ + { + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "jwtcachefiller-controller", + "message": "invalid jwt authenticator", + "removedFromCache": false, + "issuer": jwksFetchShouldFailJWTAuthenticatorSpec.Issuer, + "jwtAuthenticator": map[string]any{ + "name": "test-name", + }, + }, + }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -2117,11 +2346,12 @@ func TestController(t *testing.T) { }, }, wantLogs: []map[string]any{{ - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "jwtcachefiller-controller", - "message": "added new jwt authenticator", - "issuer": goodIssuer, + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "jwtcachefiller-controller", + "message": "added or updated jwt authenticator in cache", + "isOverwrite": false, + "issuer": goodIssuer, "jwtAuthenticator": map[string]any{ "name": "test-name", }, @@ -2154,11 +2384,12 @@ func TestController(t *testing.T) { }, }, wantLogs: []map[string]any{{ - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "jwtcachefiller-controller", - "message": "added new jwt authenticator", - "issuer": goodIssuer, + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "jwtcachefiller-controller", + "message": "added or updated jwt authenticator in cache", + "isOverwrite": false, + "issuer": goodIssuer, "jwtAuthenticator": map[string]any{ "name": "test-name", }, @@ -2288,8 +2519,12 @@ func TestController(t *testing.T) { require.Equal(t, len(tt.wantNamesOfJWTAuthenticatorsInCache), len(cache.Keys()), fmt.Sprintf("expected cache entries is incorrect. wanted:%d, got: %d, keys: %v", len(tt.wantNamesOfJWTAuthenticatorsInCache), len(cache.Keys()), cache.Keys())) + wantLogsAsJSON, err := json.Marshal(tt.wantLogs) + require.NoError(t, err) + actualLogLines := testutil.SplitByNewline(log.String()) - require.Equal(t, len(tt.wantLogs), len(actualLogLines), "log line count should be correct") + require.Equalf(t, len(tt.wantLogs), len(actualLogLines), + "log line count should be correct\nactual: %s\nwant: %s", actualLogLines, wantLogsAsJSON) for actualLogLineNum, actualLogLine := range actualLogLines { wantLine := tt.wantLogs[actualLogLineNum] diff --git a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go index b27293b1c..e131e75a6 100644 --- a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go +++ b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go @@ -174,14 +174,15 @@ func (c *webhookCacheFillerController) syncIndividualWebhookAuthenticator(ctx co // rather than trying to show the most up-to-date status possible. These validations are for administrator // convenience at the time of a configuration change, to catch typos and blatant misconfigurations, rather // than to constantly monitor for external issues. + var oldWebhookAuthenticatorFromCache *cachedWebhookAuthenticator if valueFromCache := c.cache.Get(cacheKey); valueFromCache != nil { - oldWebhookAuthenticatorFromCache := c.cacheValueAsWebhookAuthenticator(valueFromCache) + oldWebhookAuthenticatorFromCache = c.cacheValueAsWebhookAuthenticator(valueFromCache) if oldWebhookAuthenticatorFromCache != nil && reflect.DeepEqual(oldWebhookAuthenticatorFromCache.spec, &webhookAuthenticator.Spec) && tlsBundleOk && // if there was any error while validating the CA bundle, then run remaining validations and update status oldWebhookAuthenticatorFromCache.caBundleHash.Equal(caBundle.Hash()) { c.log.WithValues("webhookAuthenticator", klog.KObj(webhookAuthenticator), "endpoint", webhookAuthenticator.Spec.Endpoint). - Info("actual webhook authenticator and desired webhook authenticator are the same") + Info("cached webhook authenticator and desired webhook authenticator are the same: already cached, so skipping validations") // Stop, no more work to be done. This authenticator is already validated and cached. return nil } @@ -214,6 +215,11 @@ func (c *webhookCacheFillerController) syncIndividualWebhookAuthenticator(ctx co // validated and cached. Do not allow an old, previously validated spec of the authenticator to continue // being used for authentication. c.cache.Delete(cacheKey) + c.log.WithValues( + "webhookAuthenticator", klog.KObj(webhookAuthenticator), + "endpoint", webhookAuthenticator.Spec.Endpoint, + "removedFromCache", oldWebhookAuthenticatorFromCache != nil, + ).Info("invalid webhook authenticator") } updateErr := c.updateStatus(ctx, webhookAuthenticator, conditions) @@ -228,8 +234,11 @@ func (c *webhookCacheFillerController) syncIndividualWebhookAuthenticator(ctx co spec: webhookAuthenticator.Spec.DeepCopy(), // deep copy to avoid caching original object caBundleHash: caBundle.Hash(), }) - c.log.WithValues("webhookAuthenticator", klog.KObj(webhookAuthenticator), "endpoint", webhookAuthenticator.Spec.Endpoint). - Info("added new webhook authenticator") + c.log.WithValues( + "webhookAuthenticator", klog.KObj(webhookAuthenticator), + "endpoint", webhookAuthenticator.Spec.Endpoint, + "isOverwrite", oldWebhookAuthenticatorFromCache != nil, + ).Info("added or updated webhook authenticator in cache") } // Sync loop errors: @@ -456,11 +465,14 @@ func (c *webhookCacheFillerController) updateStatus( }) } + // TODO: this should use c.log.WithValues("webhookAuthenticator", original.Name) + log := plog.New().WithName(controllerName).WithValues("webhookAuthenticator", original.Name) + _ = conditionsutil.MergeConditions( conditions, original.Generation, &updated.Status.Conditions, - plog.New().WithName(controllerName), + log, metav1.NewTime(c.clock.Now()), ) @@ -468,5 +480,8 @@ func (c *webhookCacheFillerController) updateStatus( return nil } _, err := c.client.AuthenticationV1alpha1().WebhookAuthenticators().UpdateStatus(ctx, updated, metav1.UpdateOptions{}) + if err == nil { + log.Debug("webhookauthenticator status successfully updated") + } return err } diff --git a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go index 5a5cbdd41..f399fa35c 100644 --- a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go +++ b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go @@ -453,11 +453,12 @@ func TestController(t *testing.T) { }, wantLogs: []map[string]any{ { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "webhookcachefiller-controller", - "message": "added new webhook authenticator", - "endpoint": goodWebhookDefaultServingCertEndpoint, + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "webhookcachefiller-controller", + "message": "added or updated webhook authenticator in cache", + "isOverwrite": false, + "endpoint": goodWebhookDefaultServingCertEndpoint, "webhookAuthenticator": map[string]any{ "name": "test-name", }, @@ -505,21 +506,45 @@ func TestController(t *testing.T) { }, wantLogs: []map[string]any{ { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "webhookcachefiller-controller", - "message": "added new webhook authenticator", - "endpoint": goodWebhookDefaultServingCertEndpoint, + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "webhookcachefiller-controller", + "message": "invalid webhook authenticator", + "removedFromCache": false, + "endpoint": goodWebhookDefaultServingCertEndpoint, + "webhookAuthenticator": map[string]any{ + "name": "another-invalid-webhook-authenticator", + }, + }, + { + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "webhookcachefiller-controller", + "message": "added or updated webhook authenticator in cache", + "isOverwrite": false, + "endpoint": goodWebhookDefaultServingCertEndpoint, "webhookAuthenticator": map[string]any{ "name": "existing-webhook-authenticator", }, }, { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "webhookcachefiller-controller", - "message": "added new webhook authenticator", - "endpoint": goodWebhookDefaultServingCertEndpoint, + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "webhookcachefiller-controller", + "message": "invalid webhook authenticator", + "removedFromCache": false, + "endpoint": goodWebhookDefaultServingCertEndpoint, + "webhookAuthenticator": map[string]any{ + "name": "invalid-webhook-authenticator", + }, + }, + { + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "webhookcachefiller-controller", + "message": "added or updated webhook authenticator in cache", + "isOverwrite": false, + "endpoint": goodWebhookDefaultServingCertEndpoint, "webhookAuthenticator": map[string]any{ "name": "new-webhook-authenticator", }, @@ -604,11 +629,12 @@ func TestController(t *testing.T) { }, wantLogs: []map[string]any{ { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "webhookcachefiller-controller", - "message": "added new webhook authenticator", - "endpoint": goodWebhookDefaultServingCertEndpoint, + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "webhookcachefiller-controller", + "message": "added or updated webhook authenticator in cache", + "isOverwrite": false, + "endpoint": goodWebhookDefaultServingCertEndpoint, "webhookAuthenticator": map[string]any{ "name": "test-name", }, @@ -649,11 +675,12 @@ func TestController(t *testing.T) { }, wantLogs: []map[string]any{ { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "webhookcachefiller-controller", - "message": "added new webhook authenticator", - "endpoint": goodWebhookDefaultServingCertEndpoint, + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "webhookcachefiller-controller", + "message": "added or updated webhook authenticator in cache", + "isOverwrite": false, + "endpoint": goodWebhookDefaultServingCertEndpoint, "webhookAuthenticator": map[string]any{ "name": "test-name", }, @@ -704,11 +731,12 @@ func TestController(t *testing.T) { }, wantLogs: []map[string]any{ { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "webhookcachefiller-controller", - "message": "added new webhook authenticator", - "endpoint": goodWebhookDefaultServingCertEndpoint, + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "webhookcachefiller-controller", + "message": "added or updated webhook authenticator in cache", + "isOverwrite": true, + "endpoint": goodWebhookDefaultServingCertEndpoint, "webhookAuthenticator": map[string]any{ "name": "test-name", }, @@ -758,6 +786,19 @@ func TestController(t *testing.T) { Spec: badWebhookAuthenticatorSpecInvalidTLS, }, }, + wantLogs: []map[string]any{ + { + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "webhookcachefiller-controller", + "message": "invalid webhook authenticator", + "removedFromCache": true, + "endpoint": badWebhookAuthenticatorSpecInvalidTLS.Endpoint, + "webhookAuthenticator": map[string]any{ + "name": "test-name", + }, + }, + }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -817,7 +858,7 @@ func TestController(t *testing.T) { "level": "info", "timestamp": "2099-08-08T13:57:36.123456Z", "logger": "webhookcachefiller-controller", - "message": "actual webhook authenticator and desired webhook authenticator are the same", + "message": "cached webhook authenticator and desired webhook authenticator are the same: already cached, so skipping validations", "endpoint": goodWebhookDefaultServingCertEndpoint, "webhookAuthenticator": map[string]any{ "name": "test-name", @@ -870,11 +911,12 @@ func TestController(t *testing.T) { "actualType": "*mockcachevalue.MockValue", }, { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "webhookcachefiller-controller", - "message": "added new webhook authenticator", - "endpoint": goodWebhookDefaultServingCertEndpoint, + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "webhookcachefiller-controller", + "message": "added or updated webhook authenticator in cache", + "isOverwrite": false, + "endpoint": goodWebhookDefaultServingCertEndpoint, "webhookAuthenticator": map[string]any{ "name": "test-name", }, @@ -935,11 +977,12 @@ func TestController(t *testing.T) { }, wantLogs: []map[string]any{ { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "webhookcachefiller-controller", - "message": "added new webhook authenticator", - "endpoint": goodWebhookDefaultServingCertEndpoint, + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "webhookcachefiller-controller", + "message": "added or updated webhook authenticator in cache", + "isOverwrite": true, + "endpoint": goodWebhookDefaultServingCertEndpoint, "webhookAuthenticator": map[string]any{ "name": "test-name", }, @@ -1038,11 +1081,12 @@ func TestController(t *testing.T) { }, wantLogs: []map[string]any{ { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "webhookcachefiller-controller", - "message": "added new webhook authenticator", - "endpoint": goodWebhookDefaultServingCertEndpoint, + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "webhookcachefiller-controller", + "message": "added or updated webhook authenticator in cache", + "isOverwrite": false, + "endpoint": goodWebhookDefaultServingCertEndpoint, "webhookAuthenticator": map[string]any{ "name": "test-name", }, @@ -1087,11 +1131,12 @@ func TestController(t *testing.T) { }, wantLogs: []map[string]any{ { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "webhookcachefiller-controller", - "message": "added new webhook authenticator", - "endpoint": hostLocalIPv6Server.URL, + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "webhookcachefiller-controller", + "message": "added or updated webhook authenticator in cache", + "isOverwrite": false, + "endpoint": hostLocalIPv6Server.URL, "webhookAuthenticator": map[string]any{ "name": "test-name", }, @@ -1134,6 +1179,19 @@ func TestController(t *testing.T) { Spec: goodWebhookAuthenticatorSpecWithoutCA, }, }, + wantLogs: []map[string]any{ + { + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "webhookcachefiller-controller", + "message": "invalid webhook authenticator", + "removedFromCache": false, + "endpoint": goodWebhookAuthenticatorSpecWithoutCA.Endpoint, + "webhookAuthenticator": map[string]any{ + "name": "test-name", + }, + }, + }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -1173,6 +1231,19 @@ func TestController(t *testing.T) { Spec: badWebhookAuthenticatorSpecInvalidTLS, }, }, + wantLogs: []map[string]any{ + { + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "webhookcachefiller-controller", + "message": "invalid webhook authenticator", + "removedFromCache": false, + "endpoint": badWebhookAuthenticatorSpecInvalidTLS.Endpoint, + "webhookAuthenticator": map[string]any{ + "name": "test-name", + }, + }, + }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -1225,6 +1296,19 @@ func TestController(t *testing.T) { }, }, }, + wantLogs: []map[string]any{ + { + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "webhookcachefiller-controller", + "message": "invalid webhook authenticator", + "removedFromCache": true, + "endpoint": badEndpointInvalidURL, + "webhookAuthenticator": map[string]any{ + "name": "test-name", + }, + }, + }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -1289,6 +1373,19 @@ func TestController(t *testing.T) { }, }, }, + wantLogs: []map[string]any{ + { + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "webhookcachefiller-controller", + "message": "invalid webhook authenticator", + "removedFromCache": true, + "endpoint": badEndpointInvalidURL, + "webhookAuthenticator": map[string]any{ + "name": "test-name", + }, + }, + }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -1333,6 +1430,19 @@ func TestController(t *testing.T) { }, }, }, + wantLogs: []map[string]any{ + { + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "webhookcachefiller-controller", + "message": "invalid webhook authenticator", + "removedFromCache": false, + "endpoint": badEndpointInvalidURL, + "webhookAuthenticator": map[string]any{ + "name": "test-name", + }, + }, + }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -1376,6 +1486,19 @@ func TestController(t *testing.T) { }, }, }, + wantLogs: []map[string]any{ + { + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "webhookcachefiller-controller", + "message": "invalid webhook authenticator", + "removedFromCache": false, + "endpoint": badEndpointNoHTTPS, + "webhookAuthenticator": map[string]any{ + "name": "test-name", + }, + }, + }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -1422,6 +1545,19 @@ func TestController(t *testing.T) { }, }, }, + wantLogs: []map[string]any{ + { + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "webhookcachefiller-controller", + "message": "invalid webhook authenticator", + "removedFromCache": false, + "endpoint": "https://[0:0:0:0:0:0:0:1]:69999/some/fake/path", + "webhookAuthenticator": map[string]any{ + "name": "test-name", + }, + }, + }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -1466,6 +1602,19 @@ func TestController(t *testing.T) { }, }, wantSyncErr: testutil.WantExactErrorString("error for WebhookAuthenticator test-name: cannot dial server: tls: failed to verify certificate: x509: certificate signed by unknown authority"), + wantLogs: []map[string]any{ + { + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "webhookcachefiller-controller", + "message": "invalid webhook authenticator", + "removedFromCache": false, + "endpoint": badWebhookAuthenticatorSpecGoodEndpointButUnknownCA.Endpoint, + "webhookAuthenticator": map[string]any{ + "name": "test-name", + }, + }, + }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -1508,11 +1657,12 @@ func TestController(t *testing.T) { }, wantLogs: []map[string]any{ { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "webhookcachefiller-controller", - "message": "added new webhook authenticator", - "endpoint": goodWebhookDefaultServingCertEndpointBut404, + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "webhookcachefiller-controller", + "message": "added or updated webhook authenticator in cache", + "isOverwrite": false, + "endpoint": goodWebhookDefaultServingCertEndpointBut404, "webhookAuthenticator": map[string]any{ "name": "test-name", }, @@ -1560,11 +1710,12 @@ func TestController(t *testing.T) { }, wantLogs: []map[string]any{ { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "webhookcachefiller-controller", - "message": "added new webhook authenticator", - "endpoint": fmt.Sprintf("https://localhost:%s", localhostURL.Port()), + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "webhookcachefiller-controller", + "message": "added or updated webhook authenticator in cache", + "isOverwrite": false, + "endpoint": fmt.Sprintf("https://localhost:%s", localhostURL.Port()), "webhookAuthenticator": map[string]any{ "name": "test-name", }, @@ -1593,6 +1744,19 @@ func TestController(t *testing.T) { }, }, }, + wantLogs: []map[string]any{ + { + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "webhookcachefiller-controller", + "message": "invalid webhook authenticator", + "removedFromCache": false, + "endpoint": "https://[0:0:0:0:0:0:0:1]:4242/some/fake/path", + "webhookAuthenticator": map[string]any{ + "name": "test-name", + }, + }, + }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -1641,6 +1805,19 @@ func TestController(t *testing.T) { }, }, }, + wantLogs: []map[string]any{ + { + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "webhookcachefiller-controller", + "message": "invalid webhook authenticator", + "removedFromCache": false, + "endpoint": "https://[0:0:0:0:0:0:0:1]/some/fake/path", + "webhookAuthenticator": map[string]any{ + "name": "test-name", + }, + }, + }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -1695,11 +1872,12 @@ func TestController(t *testing.T) { }, wantLogs: []map[string]any{ { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "webhookcachefiller-controller", - "message": "added new webhook authenticator", - "endpoint": hostAs127001WebhookServer.URL, + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "webhookcachefiller-controller", + "message": "added or updated webhook authenticator in cache", + "isOverwrite": false, + "endpoint": hostAs127001WebhookServer.URL, "webhookAuthenticator": map[string]any{ "name": "test-name", }, @@ -1727,6 +1905,19 @@ func TestController(t *testing.T) { }, }, }, + wantLogs: []map[string]any{ + { + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "webhookcachefiller-controller", + "message": "invalid webhook authenticator", + "removedFromCache": false, + "endpoint": localWithExampleDotComWeebhookAuthenticatorSpec.Endpoint, + "webhookAuthenticator": map[string]any{ + "name": "test-name", + }, + }, + }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -1770,6 +1961,19 @@ func TestController(t *testing.T) { }, }, }, + wantLogs: []map[string]any{ + { + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "webhookcachefiller-controller", + "message": "invalid webhook authenticator", + "removedFromCache": false, + "endpoint": "https://0:0:0:0:0:0:0:1/some/fake/path", + "webhookAuthenticator": map[string]any{ + "name": "test-name", + }, + }, + }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -1819,11 +2023,12 @@ func TestController(t *testing.T) { }, wantLogs: []map[string]any{ { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "webhookcachefiller-controller", - "message": "added new webhook authenticator", - "endpoint": goodWebhookDefaultServingCertEndpoint, + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "webhookcachefiller-controller", + "message": "added or updated webhook authenticator in cache", + "isOverwrite": false, + "endpoint": goodWebhookDefaultServingCertEndpoint, "webhookAuthenticator": map[string]any{ "name": "test-name", }, @@ -1858,11 +2063,12 @@ func TestController(t *testing.T) { }, wantLogs: []map[string]any{ { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "webhookcachefiller-controller", - "message": "added new webhook authenticator", - "endpoint": goodWebhookDefaultServingCertEndpoint, + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "webhookcachefiller-controller", + "message": "added or updated webhook authenticator in cache", + "isOverwrite": false, + "endpoint": goodWebhookDefaultServingCertEndpoint, "webhookAuthenticator": map[string]any{ "name": "test-name", }, @@ -1979,8 +2185,12 @@ func TestController(t *testing.T) { require.Equal(t, tt.wantActions(), pinnipedAPIClient.Actions()) require.Equal(t, len(tt.wantNamesOfWebhookAuthenticatorsInCache), len(cache.Keys()), fmt.Sprintf("expected cache entries is incorrect. wanted:%d, got: %d, keys: %v", len(tt.wantNamesOfWebhookAuthenticatorsInCache), len(cache.Keys()), cache.Keys())) + wantLogsAsJSON, err := json.Marshal(tt.wantLogs) + require.NoError(t, err) + actualLogLines := testutil.SplitByNewline(log.String()) - require.Equal(t, len(tt.wantLogs), len(actualLogLines), "log line count should be correct") + require.Equalf(t, len(tt.wantLogs), len(actualLogLines), + "log line count should be correct\nactual: %s\nwant: %s", actualLogLines, wantLogsAsJSON) for actualLogLineNum, actualLogLine := range actualLogLines { wantLine := tt.wantLogs[actualLogLineNum] diff --git a/test/integration/concierge_jwtauthenticator_status_test.go b/test/integration/concierge_jwtauthenticator_status_test.go index f2721a45c..dfc14565d 100644 --- a/test/integration/concierge_jwtauthenticator_status_test.go +++ b/test/integration/concierge_jwtauthenticator_status_test.go @@ -101,6 +101,9 @@ func TestConciergeJWTAuthenticatorWithExternalCABundleStatusIsUpdatedWhenExterna }, }, authenticationv1alpha1.JWTAuthenticatorPhaseReady) + t.Logf("created jwtauthenticator %s with CA bundle source %s %s", + authenticator.Name, test.caBundleSourceSpecKind, caBundleResourceName) + test.updateCABundle(t, caBundleResourceName, "this is not a valid CA bundle value") testlib.WaitForJWTAuthenticatorStatusPhase(ctx, t, authenticator.Name, authenticationv1alpha1.JWTAuthenticatorPhaseError) diff --git a/test/integration/concierge_webhookauthenticator_status_test.go b/test/integration/concierge_webhookauthenticator_status_test.go index ac8fd5153..357e766e0 100644 --- a/test/integration/concierge_webhookauthenticator_status_test.go +++ b/test/integration/concierge_webhookauthenticator_status_test.go @@ -82,13 +82,13 @@ func TestConciergeWebhookAuthenticatorWithExternalCABundleStatusIsUpdatedWhenExt t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { t.Parallel() - caBundlePEM, err := base64.StdEncoding.DecodeString(testlib.IntegrationEnv(t).TestWebhook.TLS.CertificateAuthorityData) + caBundlePEM, err := base64.StdEncoding.DecodeString(env.TestWebhook.TLS.CertificateAuthorityData) require.NoError(t, err) caBundleResourceName := test.createResourceForCABundle(t, string(caBundlePEM)) authenticator := testlib.CreateTestWebhookAuthenticator(ctx, t, &authenticationv1alpha1.WebhookAuthenticatorSpec{ - Endpoint: testlib.IntegrationEnv(t).TestWebhook.Endpoint, + Endpoint: env.TestWebhook.Endpoint, TLS: &authenticationv1alpha1.TLSSpec{ CertificateAuthorityDataSource: &authenticationv1alpha1.CABundleSource{ Kind: test.caBundleSourceSpecKind, @@ -98,6 +98,9 @@ func TestConciergeWebhookAuthenticatorWithExternalCABundleStatusIsUpdatedWhenExt }, }, authenticationv1alpha1.WebhookAuthenticatorPhaseReady) + t.Logf("created webhookauthenticator %s with CA bundle source %s %s", + authenticator.Name, test.caBundleSourceSpecKind, caBundleResourceName) + test.updateCABundle(t, caBundleResourceName, "this is not a valid CA bundle value") testlib.WaitForWebhookAuthenticatorStatusPhase(ctx, t, authenticator.Name, authenticationv1alpha1.WebhookAuthenticatorPhaseError) @@ -110,7 +113,7 @@ func TestConciergeWebhookAuthenticatorWithExternalCABundleStatusIsUpdatedWhenExt } func TestConciergeWebhookAuthenticatorStatus_Parallel(t *testing.T) { - testEnv := testlib.IntegrationEnv(t) + env := testlib.IntegrationEnv(t) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute) t.Cleanup(cancel) @@ -126,7 +129,7 @@ func TestConciergeWebhookAuthenticatorStatus_Parallel(t *testing.T) { { name: "basic test to see if the WebhookAuthenticator wakes up or not", spec: func() *authenticationv1alpha1.WebhookAuthenticatorSpec { - return &testlib.IntegrationEnv(t).TestWebhook + return &env.TestWebhook }, initialPhase: authenticationv1alpha1.WebhookAuthenticatorPhaseReady, finalConditions: allSuccessfulWebhookAuthenticatorConditions(), @@ -135,7 +138,7 @@ func TestConciergeWebhookAuthenticatorStatus_Parallel(t *testing.T) { name: "valid spec with invalid CA in TLS config will result in a WebhookAuthenticator that is not ready", spec: func() *authenticationv1alpha1.WebhookAuthenticatorSpec { caBundleString := "invalid base64-encoded data" - webhookSpec := testEnv.TestWebhook.DeepCopy() + webhookSpec := env.TestWebhook.DeepCopy() webhookSpec.TLS = &authenticationv1alpha1.TLSSpec{ CertificateAuthorityData: caBundleString, } @@ -172,7 +175,7 @@ func TestConciergeWebhookAuthenticatorStatus_Parallel(t *testing.T) { { name: "valid spec with valid CA in TLS config but does not match issuer server will result in a WebhookAuthenticator that is not ready", spec: func() *authenticationv1alpha1.WebhookAuthenticatorSpec { - webhookSpec := testEnv.TestWebhook.DeepCopy() + webhookSpec := env.TestWebhook.DeepCopy() webhookSpec.TLS = &authenticationv1alpha1.TLSSpec{ CertificateAuthorityData: caBundleSomePivotalCA, } @@ -204,7 +207,7 @@ func TestConciergeWebhookAuthenticatorStatus_Parallel(t *testing.T) { { name: "invalid with unresponsive endpoint will result in a WebhookAuthenticator that is not ready", spec: func() *authenticationv1alpha1.WebhookAuthenticatorSpec { - webhookSpec := testEnv.TestWebhook.DeepCopy() + webhookSpec := env.TestWebhook.DeepCopy() webhookSpec.TLS = &authenticationv1alpha1.TLSSpec{ CertificateAuthorityData: caBundleSomePivotalCA, } From dedd51df9105a1bda25972b091c50b67da6d9ad0 Mon Sep 17 00:00:00 2001 From: Joshua Casey Date: Tue, 30 Jul 2024 15:22:44 -0500 Subject: [PATCH 71/99] Test Refactor: webhookauthenticator_test checks exact log line equality Co-authored-by: Ryan Richard --- .../webhookcachefiller_test.go | 474 +++--------------- 1 file changed, 69 insertions(+), 405 deletions(-) diff --git a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go index f399fa35c..abb6573ba 100644 --- a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go +++ b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go @@ -16,6 +16,7 @@ import ( "net/http" "net/http/httptest" "net/url" + "strings" "testing" "time" @@ -26,7 +27,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/util/sets" k8sinformers "k8s.io/client-go/informers" kubeinformers "k8s.io/client-go/informers" kubernetesfake "k8s.io/client-go/kubernetes/fake" @@ -414,20 +414,15 @@ func TestController(t *testing.T) { // for modifying the clients to hack in arbitrary api responses configClient func(*conciergefake.Clientset) wantSyncErr testutil.RequireErrorStringFunc - wantLogs []map[string]any + wantLogLines []string wantActions func() []coretesting.Action // random comment so lines above don't have huge indents wantNamesOfWebhookAuthenticatorsInCache []string }{ { name: "Sync: No WebhookAuthenticators found results in no errors and no status conditions", - wantLogs: []map[string]any{ - { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "webhookcachefiller-controller", - "message": "No WebhookAuthenticators found", - }, + wantLogLines: []string{ + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).Sync","message":"No WebhookAuthenticators found"}`, }, wantActions: func() []coretesting.Action { return []coretesting.Action{ @@ -451,18 +446,8 @@ func TestController(t *testing.T) { }, }, }, - wantLogs: []map[string]any{ - { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "webhookcachefiller-controller", - "message": "added or updated webhook authenticator in cache", - "isOverwrite": false, - "endpoint": goodWebhookDefaultServingCertEndpoint, - "webhookAuthenticator": map[string]any{ - "name": "test-name", - }, - }, + wantLogLines: []string{ + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":{"name":"test-name"},"endpoint":"%s","isOverwrite":false}`, goodWebhookDefaultServingCertEndpoint), }, wantActions: func() []coretesting.Action { return []coretesting.Action{ @@ -504,51 +489,11 @@ func TestController(t *testing.T) { Spec: badWebhookAuthenticatorSpecInvalidTLS, }, }, - wantLogs: []map[string]any{ - { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "webhookcachefiller-controller", - "message": "invalid webhook authenticator", - "removedFromCache": false, - "endpoint": goodWebhookDefaultServingCertEndpoint, - "webhookAuthenticator": map[string]any{ - "name": "another-invalid-webhook-authenticator", - }, - }, - { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "webhookcachefiller-controller", - "message": "added or updated webhook authenticator in cache", - "isOverwrite": false, - "endpoint": goodWebhookDefaultServingCertEndpoint, - "webhookAuthenticator": map[string]any{ - "name": "existing-webhook-authenticator", - }, - }, - { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "webhookcachefiller-controller", - "message": "invalid webhook authenticator", - "removedFromCache": false, - "endpoint": goodWebhookDefaultServingCertEndpoint, - "webhookAuthenticator": map[string]any{ - "name": "invalid-webhook-authenticator", - }, - }, - { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "webhookcachefiller-controller", - "message": "added or updated webhook authenticator in cache", - "isOverwrite": false, - "endpoint": goodWebhookDefaultServingCertEndpoint, - "webhookAuthenticator": map[string]any{ - "name": "new-webhook-authenticator", - }, - }, + wantLogLines: []string{ + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":{"name":"another-invalid-webhook-authenticator"},"endpoint":"%s","removedFromCache":false}`, goodWebhookDefaultServingCertEndpoint), + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":{"name":"existing-webhook-authenticator"},"endpoint":"%s","isOverwrite":false}`, goodWebhookDefaultServingCertEndpoint), + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":{"name":"invalid-webhook-authenticator"},"endpoint":"%s","removedFromCache":false}`, goodWebhookDefaultServingCertEndpoint), + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":{"name":"new-webhook-authenticator"},"endpoint":"%s","isOverwrite":false}`, goodWebhookDefaultServingCertEndpoint), }, wantActions: func() []coretesting.Action { updateValidStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -627,18 +572,8 @@ func TestController(t *testing.T) { secretsAndConfigMaps: []runtime.Object{ someSecretWithCA, }, - wantLogs: []map[string]any{ - { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "webhookcachefiller-controller", - "message": "added or updated webhook authenticator in cache", - "isOverwrite": false, - "endpoint": goodWebhookDefaultServingCertEndpoint, - "webhookAuthenticator": map[string]any{ - "name": "test-name", - }, - }, + wantLogLines: []string{ + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":{"name":"test-name"},"endpoint":"%s","isOverwrite":false}`, goodWebhookDefaultServingCertEndpoint), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -673,18 +608,8 @@ func TestController(t *testing.T) { secretsAndConfigMaps: []runtime.Object{ someConfigMapWithCA, }, - wantLogs: []map[string]any{ - { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "webhookcachefiller-controller", - "message": "added or updated webhook authenticator in cache", - "isOverwrite": false, - "endpoint": goodWebhookDefaultServingCertEndpoint, - "webhookAuthenticator": map[string]any{ - "name": "test-name", - }, - }, + wantLogLines: []string{ + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":{"name":"test-name"},"endpoint":"%s","isOverwrite":false}`, goodWebhookDefaultServingCertEndpoint), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -729,18 +654,8 @@ func TestController(t *testing.T) { secretsAndConfigMaps: []runtime.Object{ someConfigMapWithCA, }, - wantLogs: []map[string]any{ - { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "webhookcachefiller-controller", - "message": "added or updated webhook authenticator in cache", - "isOverwrite": true, - "endpoint": goodWebhookDefaultServingCertEndpoint, - "webhookAuthenticator": map[string]any{ - "name": "test-name", - }, - }, + wantLogLines: []string{ + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":{"name":"test-name"},"endpoint":"%s","isOverwrite":true}`, goodWebhookDefaultServingCertEndpoint), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -786,18 +701,8 @@ func TestController(t *testing.T) { Spec: badWebhookAuthenticatorSpecInvalidTLS, }, }, - wantLogs: []map[string]any{ - { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "webhookcachefiller-controller", - "message": "invalid webhook authenticator", - "removedFromCache": true, - "endpoint": badWebhookAuthenticatorSpecInvalidTLS.Endpoint, - "webhookAuthenticator": map[string]any{ - "name": "test-name", - }, - }, + wantLogLines: []string{ + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":{"name":"test-name"},"endpoint":"%s","removedFromCache":true}`, badWebhookAuthenticatorSpecInvalidTLS.Endpoint), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -902,25 +807,9 @@ func TestController(t *testing.T) { Spec: goodWebhookAuthenticatorSpecWithCA, }, }, - wantLogs: []map[string]any{ - { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "webhookcachefiller-controller", - "message": "wrong webhook authenticator type in cache", - "actualType": "*mockcachevalue.MockValue", - }, - { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "webhookcachefiller-controller", - "message": "added or updated webhook authenticator in cache", - "isOverwrite": false, - "endpoint": goodWebhookDefaultServingCertEndpoint, - "webhookAuthenticator": map[string]any{ - "name": "test-name", - }, - }, + wantLogLines: []string{ + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).cacheValueAsWebhookAuthenticator","message":"wrong webhook authenticator type in cache","actualType":"*mockcachevalue.MockValue"}`, + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":{"name":"test-name"},"endpoint":"%s","isOverwrite":false}`, goodWebhookDefaultServingCertEndpoint), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -975,18 +864,8 @@ func TestController(t *testing.T) { }, }, }, - wantLogs: []map[string]any{ - { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "webhookcachefiller-controller", - "message": "added or updated webhook authenticator in cache", - "isOverwrite": true, - "endpoint": goodWebhookDefaultServingCertEndpoint, - "webhookAuthenticator": map[string]any{ - "name": "test-name", - }, - }, + wantLogLines: []string{ + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":{"name":"test-name"},"endpoint":"%s","isOverwrite":true}`, goodWebhookDefaultServingCertEndpoint), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -1046,7 +925,7 @@ func TestController(t *testing.T) { Spec: goodWebhookAuthenticatorSpecWithCA, }, }, - wantLogs: []map[string]any{}, + wantLogLines: []string{}, // wants no logs wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -1079,18 +958,8 @@ func TestController(t *testing.T) { Spec: goodWebhookAuthenticatorSpecWithCA, }, }, - wantLogs: []map[string]any{ - { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "webhookcachefiller-controller", - "message": "added or updated webhook authenticator in cache", - "isOverwrite": false, - "endpoint": goodWebhookDefaultServingCertEndpoint, - "webhookAuthenticator": map[string]any{ - "name": "test-name", - }, - }, + wantLogLines: []string{ + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":{"name":"test-name"},"endpoint":"%s","isOverwrite":false}`, goodWebhookDefaultServingCertEndpoint), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -1129,18 +998,8 @@ func TestController(t *testing.T) { }(), }, }, - wantLogs: []map[string]any{ - { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "webhookcachefiller-controller", - "message": "added or updated webhook authenticator in cache", - "isOverwrite": false, - "endpoint": hostLocalIPv6Server.URL, - "webhookAuthenticator": map[string]any{ - "name": "test-name", - }, - }, + wantLogLines: []string{ + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":{"name":"test-name"},"endpoint":"%s","isOverwrite":false}`, hostLocalIPv6Server.URL), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -1179,18 +1038,8 @@ func TestController(t *testing.T) { Spec: goodWebhookAuthenticatorSpecWithoutCA, }, }, - wantLogs: []map[string]any{ - { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "webhookcachefiller-controller", - "message": "invalid webhook authenticator", - "removedFromCache": false, - "endpoint": goodWebhookAuthenticatorSpecWithoutCA.Endpoint, - "webhookAuthenticator": map[string]any{ - "name": "test-name", - }, - }, + wantLogLines: []string{ + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":{"name":"test-name"},"endpoint":"%s","removedFromCache":false}`, goodWebhookAuthenticatorSpecWithoutCA.Endpoint), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -1231,18 +1080,8 @@ func TestController(t *testing.T) { Spec: badWebhookAuthenticatorSpecInvalidTLS, }, }, - wantLogs: []map[string]any{ - { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "webhookcachefiller-controller", - "message": "invalid webhook authenticator", - "removedFromCache": false, - "endpoint": badWebhookAuthenticatorSpecInvalidTLS.Endpoint, - "webhookAuthenticator": map[string]any{ - "name": "test-name", - }, - }, + wantLogLines: []string{ + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":{"name":"test-name"},"endpoint":"%s","removedFromCache":false}`, badWebhookAuthenticatorSpecInvalidTLS.Endpoint), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -1296,18 +1135,8 @@ func TestController(t *testing.T) { }, }, }, - wantLogs: []map[string]any{ - { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "webhookcachefiller-controller", - "message": "invalid webhook authenticator", - "removedFromCache": true, - "endpoint": badEndpointInvalidURL, - "webhookAuthenticator": map[string]any{ - "name": "test-name", - }, - }, + wantLogLines: []string{ + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":{"name":"test-name"},"endpoint":"%s","removedFromCache":true}`, badEndpointInvalidURL), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -1373,18 +1202,8 @@ func TestController(t *testing.T) { }, }, }, - wantLogs: []map[string]any{ - { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "webhookcachefiller-controller", - "message": "invalid webhook authenticator", - "removedFromCache": true, - "endpoint": badEndpointInvalidURL, - "webhookAuthenticator": map[string]any{ - "name": "test-name", - }, - }, + wantLogLines: []string{ + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":{"name":"test-name"},"endpoint":"%s","removedFromCache":true}`, badEndpointInvalidURL), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -1430,18 +1249,8 @@ func TestController(t *testing.T) { }, }, }, - wantLogs: []map[string]any{ - { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "webhookcachefiller-controller", - "message": "invalid webhook authenticator", - "removedFromCache": false, - "endpoint": badEndpointInvalidURL, - "webhookAuthenticator": map[string]any{ - "name": "test-name", - }, - }, + wantLogLines: []string{ + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":{"name":"test-name"},"endpoint":"%s","removedFromCache":false}`, badEndpointInvalidURL), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -1486,18 +1295,8 @@ func TestController(t *testing.T) { }, }, }, - wantLogs: []map[string]any{ - { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "webhookcachefiller-controller", - "message": "invalid webhook authenticator", - "removedFromCache": false, - "endpoint": badEndpointNoHTTPS, - "webhookAuthenticator": map[string]any{ - "name": "test-name", - }, - }, + wantLogLines: []string{ + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":{"name":"test-name"},"endpoint":"%s","removedFromCache":false}`, badEndpointNoHTTPS), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -1545,18 +1344,8 @@ func TestController(t *testing.T) { }, }, }, - wantLogs: []map[string]any{ - { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "webhookcachefiller-controller", - "message": "invalid webhook authenticator", - "removedFromCache": false, - "endpoint": "https://[0:0:0:0:0:0:0:1]:69999/some/fake/path", - "webhookAuthenticator": map[string]any{ - "name": "test-name", - }, - }, + wantLogLines: []string{ + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":{"name":"test-name"},"endpoint":"https://[0:0:0:0:0:0:0:1]:69999/some/fake/path","removedFromCache":false}`, }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -1602,18 +1391,8 @@ func TestController(t *testing.T) { }, }, wantSyncErr: testutil.WantExactErrorString("error for WebhookAuthenticator test-name: cannot dial server: tls: failed to verify certificate: x509: certificate signed by unknown authority"), - wantLogs: []map[string]any{ - { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "webhookcachefiller-controller", - "message": "invalid webhook authenticator", - "removedFromCache": false, - "endpoint": badWebhookAuthenticatorSpecGoodEndpointButUnknownCA.Endpoint, - "webhookAuthenticator": map[string]any{ - "name": "test-name", - }, - }, + wantLogLines: []string{ + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":{"name":"test-name"},"endpoint":"%s","removedFromCache":false}`, badWebhookAuthenticatorSpecGoodEndpointButUnknownCA.Endpoint), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -1655,18 +1434,8 @@ func TestController(t *testing.T) { Spec: goodWebhookAuthenticatorSpecWith404Endpoint, }, }, - wantLogs: []map[string]any{ - { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "webhookcachefiller-controller", - "message": "added or updated webhook authenticator in cache", - "isOverwrite": false, - "endpoint": goodWebhookDefaultServingCertEndpointBut404, - "webhookAuthenticator": map[string]any{ - "name": "test-name", - }, - }, + wantLogLines: []string{ + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":{"name":"test-name"},"endpoint":"%s","isOverwrite":false}`, goodWebhookDefaultServingCertEndpointBut404), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -1708,18 +1477,8 @@ func TestController(t *testing.T) { }, }, }, - wantLogs: []map[string]any{ - { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "webhookcachefiller-controller", - "message": "added or updated webhook authenticator in cache", - "isOverwrite": false, - "endpoint": fmt.Sprintf("https://localhost:%s", localhostURL.Port()), - "webhookAuthenticator": map[string]any{ - "name": "test-name", - }, - }, + wantLogLines: []string{ + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":{"name":"test-name"},"endpoint":"%s","isOverwrite":false}`, fmt.Sprintf("https://localhost:%s", localhostURL.Port())), }, wantActions: func() []coretesting.Action { return []coretesting.Action{ @@ -1744,18 +1503,8 @@ func TestController(t *testing.T) { }, }, }, - wantLogs: []map[string]any{ - { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "webhookcachefiller-controller", - "message": "invalid webhook authenticator", - "removedFromCache": false, - "endpoint": "https://[0:0:0:0:0:0:0:1]:4242/some/fake/path", - "webhookAuthenticator": map[string]any{ - "name": "test-name", - }, - }, + wantLogLines: []string{ + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":{"name":"test-name"},"endpoint":"https://[0:0:0:0:0:0:0:1]:4242/some/fake/path","removedFromCache":false}`, }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -1805,18 +1554,8 @@ func TestController(t *testing.T) { }, }, }, - wantLogs: []map[string]any{ - { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "webhookcachefiller-controller", - "message": "invalid webhook authenticator", - "removedFromCache": false, - "endpoint": "https://[0:0:0:0:0:0:0:1]/some/fake/path", - "webhookAuthenticator": map[string]any{ - "name": "test-name", - }, - }, + wantLogLines: []string{ + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":{"name":"test-name"},"endpoint":"https://[0:0:0:0:0:0:0:1]/some/fake/path","removedFromCache":false}`, }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -1870,18 +1609,8 @@ func TestController(t *testing.T) { }, }, }, - wantLogs: []map[string]any{ - { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "webhookcachefiller-controller", - "message": "added or updated webhook authenticator in cache", - "isOverwrite": false, - "endpoint": hostAs127001WebhookServer.URL, - "webhookAuthenticator": map[string]any{ - "name": "test-name", - }, - }, + wantLogLines: []string{ + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":{"name":"test-name"},"endpoint":"%s","isOverwrite":false}`, hostAs127001WebhookServer.URL), }, wantActions: func() []coretesting.Action { return []coretesting.Action{ @@ -1905,18 +1634,8 @@ func TestController(t *testing.T) { }, }, }, - wantLogs: []map[string]any{ - { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "webhookcachefiller-controller", - "message": "invalid webhook authenticator", - "removedFromCache": false, - "endpoint": localWithExampleDotComWeebhookAuthenticatorSpec.Endpoint, - "webhookAuthenticator": map[string]any{ - "name": "test-name", - }, - }, + wantLogLines: []string{ + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":{"name":"test-name"},"endpoint":"%s","removedFromCache":false}`, localWithExampleDotComWeebhookAuthenticatorSpec.Endpoint), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -1961,18 +1680,8 @@ func TestController(t *testing.T) { }, }, }, - wantLogs: []map[string]any{ - { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "webhookcachefiller-controller", - "message": "invalid webhook authenticator", - "removedFromCache": false, - "endpoint": "https://0:0:0:0:0:0:0:1/some/fake/path", - "webhookAuthenticator": map[string]any{ - "name": "test-name", - }, - }, + wantLogLines: []string{ + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":{"name":"test-name"},"endpoint":"https://0:0:0:0:0:0:0:1/some/fake/path","removedFromCache":false}`, }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -2021,18 +1730,8 @@ func TestController(t *testing.T) { }, }, }, - wantLogs: []map[string]any{ - { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "webhookcachefiller-controller", - "message": "added or updated webhook authenticator in cache", - "isOverwrite": false, - "endpoint": goodWebhookDefaultServingCertEndpoint, - "webhookAuthenticator": map[string]any{ - "name": "test-name", - }, - }, + wantLogLines: []string{ + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":{"name":"test-name"},"endpoint":"%s","isOverwrite":false}`, goodWebhookDefaultServingCertEndpoint), }, wantActions: func() []coretesting.Action { return []coretesting.Action{ @@ -2061,18 +1760,8 @@ func TestController(t *testing.T) { }, }, }, - wantLogs: []map[string]any{ - { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "webhookcachefiller-controller", - "message": "added or updated webhook authenticator in cache", - "isOverwrite": false, - "endpoint": goodWebhookDefaultServingCertEndpoint, - "webhookAuthenticator": map[string]any{ - "name": "test-name", - }, - }, + wantLogLines: []string{ + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":{"name":"test-name"},"endpoint":"%s","isOverwrite":false}`, goodWebhookDefaultServingCertEndpoint), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -2113,7 +1802,7 @@ func TestController(t *testing.T) { Spec: goodWebhookAuthenticatorSpecWithCA, }, }, - wantLogs: []map[string]any{}, + wantLogLines: []string{}, // wants no logs wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -2185,36 +1874,11 @@ func TestController(t *testing.T) { require.Equal(t, tt.wantActions(), pinnipedAPIClient.Actions()) require.Equal(t, len(tt.wantNamesOfWebhookAuthenticatorsInCache), len(cache.Keys()), fmt.Sprintf("expected cache entries is incorrect. wanted:%d, got: %d, keys: %v", len(tt.wantNamesOfWebhookAuthenticatorsInCache), len(cache.Keys()), cache.Keys())) - wantLogsAsJSON, err := json.Marshal(tt.wantLogs) - require.NoError(t, err) - - actualLogLines := testutil.SplitByNewline(log.String()) - require.Equalf(t, len(tt.wantLogs), len(actualLogLines), - "log line count should be correct\nactual: %s\nwant: %s", actualLogLines, wantLogsAsJSON) - - for actualLogLineNum, actualLogLine := range actualLogLines { - wantLine := tt.wantLogs[actualLogLineNum] - require.NotNil(t, wantLine, "expected log line should never be empty") - - var actualParsedLine map[string]any - err := json.Unmarshal([]byte(actualLogLine), &actualParsedLine) - require.NoError(t, err) - - wantLineAsJSON, err := json.Marshal(wantLine) - require.NoError(t, err) - wantLine["caller"] = "we don't want to actually make equality comparisons about this" - require.Lenf(t, actualParsedLine, len(wantLine), "actual: %s\nwant: %s", actualLogLine, string(wantLineAsJSON)) - require.Equal(t, sets.StringKeySet(actualParsedLine), sets.StringKeySet(wantLine)) - - for k := range actualParsedLine { - if k == "caller" { - require.NotEmpty(t, actualParsedLine["caller"]) - } else { - require.Equal(t, wantLine[k], actualParsedLine[k], - fmt.Sprintf("log line (%d) key %q was not equal\nactual: %s\nwant: %s", - actualLogLineNum, k, actualParsedLine[k], wantLine[k])) - } - } + if len(tt.wantLogLines) == 0 { + require.Empty(t, log.String()) + } else { + actualLog, _ := strings.CutSuffix(log.String(), "\n") + require.Equal(t, tt.wantLogLines, strings.Split(actualLog, "\n")) } }) } From 05a2fd97f8b5fb575af41a5bfb5ab769587cf4de Mon Sep 17 00:00:00 2001 From: Joshua Casey Date: Tue, 30 Jul 2024 15:30:32 -0500 Subject: [PATCH 72/99] webhookcontroller now only logs the webhook authenticator name instead of an object Co-authored-by: Ryan Richard --- .../webhookcachefiller/webhookcachefiller.go | 7 +- .../webhookcachefiller_test.go | 73 ++++++++----------- 2 files changed, 35 insertions(+), 45 deletions(-) diff --git a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go index e131e75a6..84dde901c 100644 --- a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go +++ b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go @@ -26,7 +26,6 @@ import ( "k8s.io/apiserver/plugin/pkg/authenticator/token/webhook" corev1informers "k8s.io/client-go/informers/core/v1" "k8s.io/client-go/rest" - "k8s.io/klog/v2" "k8s.io/utils/clock" authenticationv1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/authentication/v1alpha1" @@ -181,7 +180,7 @@ func (c *webhookCacheFillerController) syncIndividualWebhookAuthenticator(ctx co reflect.DeepEqual(oldWebhookAuthenticatorFromCache.spec, &webhookAuthenticator.Spec) && tlsBundleOk && // if there was any error while validating the CA bundle, then run remaining validations and update status oldWebhookAuthenticatorFromCache.caBundleHash.Equal(caBundle.Hash()) { - c.log.WithValues("webhookAuthenticator", klog.KObj(webhookAuthenticator), "endpoint", webhookAuthenticator.Spec.Endpoint). + c.log.WithValues("webhookAuthenticator", webhookAuthenticator.Name, "endpoint", webhookAuthenticator.Spec.Endpoint). Info("cached webhook authenticator and desired webhook authenticator are the same: already cached, so skipping validations") // Stop, no more work to be done. This authenticator is already validated and cached. return nil @@ -216,7 +215,7 @@ func (c *webhookCacheFillerController) syncIndividualWebhookAuthenticator(ctx co // being used for authentication. c.cache.Delete(cacheKey) c.log.WithValues( - "webhookAuthenticator", klog.KObj(webhookAuthenticator), + "webhookAuthenticator", webhookAuthenticator.Name, "endpoint", webhookAuthenticator.Spec.Endpoint, "removedFromCache", oldWebhookAuthenticatorFromCache != nil, ).Info("invalid webhook authenticator") @@ -235,7 +234,7 @@ func (c *webhookCacheFillerController) syncIndividualWebhookAuthenticator(ctx co caBundleHash: caBundle.Hash(), }) c.log.WithValues( - "webhookAuthenticator", klog.KObj(webhookAuthenticator), + "webhookAuthenticator", webhookAuthenticator.Name, "endpoint", webhookAuthenticator.Spec.Endpoint, "isOverwrite", oldWebhookAuthenticatorFromCache != nil, ).Info("added or updated webhook authenticator in cache") diff --git a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go index abb6573ba..afa295d90 100644 --- a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go +++ b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go @@ -447,7 +447,7 @@ func TestController(t *testing.T) { }, }, wantLogLines: []string{ - fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":{"name":"test-name"},"endpoint":"%s","isOverwrite":false}`, goodWebhookDefaultServingCertEndpoint), + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":"test-name","endpoint":"%s","isOverwrite":false}`, goodWebhookDefaultServingCertEndpoint), }, wantActions: func() []coretesting.Action { return []coretesting.Action{ @@ -490,10 +490,10 @@ func TestController(t *testing.T) { }, }, wantLogLines: []string{ - fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":{"name":"another-invalid-webhook-authenticator"},"endpoint":"%s","removedFromCache":false}`, goodWebhookDefaultServingCertEndpoint), - fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":{"name":"existing-webhook-authenticator"},"endpoint":"%s","isOverwrite":false}`, goodWebhookDefaultServingCertEndpoint), - fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":{"name":"invalid-webhook-authenticator"},"endpoint":"%s","removedFromCache":false}`, goodWebhookDefaultServingCertEndpoint), - fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":{"name":"new-webhook-authenticator"},"endpoint":"%s","isOverwrite":false}`, goodWebhookDefaultServingCertEndpoint), + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":"another-invalid-webhook-authenticator","endpoint":"%s","removedFromCache":false}`, goodWebhookDefaultServingCertEndpoint), + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":"existing-webhook-authenticator","endpoint":"%s","isOverwrite":false}`, goodWebhookDefaultServingCertEndpoint), + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":"invalid-webhook-authenticator","endpoint":"%s","removedFromCache":false}`, goodWebhookDefaultServingCertEndpoint), + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":"new-webhook-authenticator","endpoint":"%s","isOverwrite":false}`, goodWebhookDefaultServingCertEndpoint), }, wantActions: func() []coretesting.Action { updateValidStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -573,7 +573,7 @@ func TestController(t *testing.T) { someSecretWithCA, }, wantLogLines: []string{ - fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":{"name":"test-name"},"endpoint":"%s","isOverwrite":false}`, goodWebhookDefaultServingCertEndpoint), + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":"test-name","endpoint":"%s","isOverwrite":false}`, goodWebhookDefaultServingCertEndpoint), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -609,7 +609,7 @@ func TestController(t *testing.T) { someConfigMapWithCA, }, wantLogLines: []string{ - fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":{"name":"test-name"},"endpoint":"%s","isOverwrite":false}`, goodWebhookDefaultServingCertEndpoint), + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":"test-name","endpoint":"%s","isOverwrite":false}`, goodWebhookDefaultServingCertEndpoint), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -655,7 +655,7 @@ func TestController(t *testing.T) { someConfigMapWithCA, }, wantLogLines: []string{ - fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":{"name":"test-name"},"endpoint":"%s","isOverwrite":true}`, goodWebhookDefaultServingCertEndpoint), + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":"test-name","endpoint":"%s","isOverwrite":true}`, goodWebhookDefaultServingCertEndpoint), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -702,7 +702,7 @@ func TestController(t *testing.T) { }, }, wantLogLines: []string{ - fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":{"name":"test-name"},"endpoint":"%s","removedFromCache":true}`, badWebhookAuthenticatorSpecInvalidTLS.Endpoint), + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":"test-name","endpoint":"%s","removedFromCache":true}`, badWebhookAuthenticatorSpecInvalidTLS.Endpoint), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -758,17 +758,8 @@ func TestController(t *testing.T) { }, }, }, - wantLogs: []map[string]any{ - { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "webhookcachefiller-controller", - "message": "cached webhook authenticator and desired webhook authenticator are the same: already cached, so skipping validations", - "endpoint": goodWebhookDefaultServingCertEndpoint, - "webhookAuthenticator": map[string]any{ - "name": "test-name", - }, - }, + wantLogLines: []string{ + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"cached webhook authenticator and desired webhook authenticator are the same: already cached, so skipping validations","webhookAuthenticator":{"name":"test-name"},"endpoint":"%s"}`, goodWebhookDefaultServingCertEndpoint), }, wantActions: func() []coretesting.Action { return []coretesting.Action{ @@ -809,7 +800,7 @@ func TestController(t *testing.T) { }, wantLogLines: []string{ `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).cacheValueAsWebhookAuthenticator","message":"wrong webhook authenticator type in cache","actualType":"*mockcachevalue.MockValue"}`, - fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":{"name":"test-name"},"endpoint":"%s","isOverwrite":false}`, goodWebhookDefaultServingCertEndpoint), + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":"test-name","endpoint":"%s","isOverwrite":false}`, goodWebhookDefaultServingCertEndpoint), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -865,7 +856,7 @@ func TestController(t *testing.T) { }, }, wantLogLines: []string{ - fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":{"name":"test-name"},"endpoint":"%s","isOverwrite":true}`, goodWebhookDefaultServingCertEndpoint), + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":"test-name","endpoint":"%s","isOverwrite":true}`, goodWebhookDefaultServingCertEndpoint), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -959,7 +950,7 @@ func TestController(t *testing.T) { }, }, wantLogLines: []string{ - fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":{"name":"test-name"},"endpoint":"%s","isOverwrite":false}`, goodWebhookDefaultServingCertEndpoint), + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":"test-name","endpoint":"%s","isOverwrite":false}`, goodWebhookDefaultServingCertEndpoint), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -999,7 +990,7 @@ func TestController(t *testing.T) { }, }, wantLogLines: []string{ - fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":{"name":"test-name"},"endpoint":"%s","isOverwrite":false}`, hostLocalIPv6Server.URL), + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":"test-name","endpoint":"%s","isOverwrite":false}`, hostLocalIPv6Server.URL), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -1039,7 +1030,7 @@ func TestController(t *testing.T) { }, }, wantLogLines: []string{ - fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":{"name":"test-name"},"endpoint":"%s","removedFromCache":false}`, goodWebhookAuthenticatorSpecWithoutCA.Endpoint), + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":"test-name","endpoint":"%s","removedFromCache":false}`, goodWebhookAuthenticatorSpecWithoutCA.Endpoint), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -1081,7 +1072,7 @@ func TestController(t *testing.T) { }, }, wantLogLines: []string{ - fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":{"name":"test-name"},"endpoint":"%s","removedFromCache":false}`, badWebhookAuthenticatorSpecInvalidTLS.Endpoint), + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":"test-name","endpoint":"%s","removedFromCache":false}`, badWebhookAuthenticatorSpecInvalidTLS.Endpoint), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -1136,7 +1127,7 @@ func TestController(t *testing.T) { }, }, wantLogLines: []string{ - fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":{"name":"test-name"},"endpoint":"%s","removedFromCache":true}`, badEndpointInvalidURL), + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":"test-name","endpoint":"%s","removedFromCache":true}`, badEndpointInvalidURL), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -1203,7 +1194,7 @@ func TestController(t *testing.T) { }, }, wantLogLines: []string{ - fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":{"name":"test-name"},"endpoint":"%s","removedFromCache":true}`, badEndpointInvalidURL), + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":"test-name","endpoint":"%s","removedFromCache":true}`, badEndpointInvalidURL), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -1250,7 +1241,7 @@ func TestController(t *testing.T) { }, }, wantLogLines: []string{ - fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":{"name":"test-name"},"endpoint":"%s","removedFromCache":false}`, badEndpointInvalidURL), + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":"test-name","endpoint":"%s","removedFromCache":false}`, badEndpointInvalidURL), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -1296,7 +1287,7 @@ func TestController(t *testing.T) { }, }, wantLogLines: []string{ - fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":{"name":"test-name"},"endpoint":"%s","removedFromCache":false}`, badEndpointNoHTTPS), + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":"test-name","endpoint":"%s","removedFromCache":false}`, badEndpointNoHTTPS), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -1345,7 +1336,7 @@ func TestController(t *testing.T) { }, }, wantLogLines: []string{ - `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":{"name":"test-name"},"endpoint":"https://[0:0:0:0:0:0:0:1]:69999/some/fake/path","removedFromCache":false}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":"test-name","endpoint":"https://[0:0:0:0:0:0:0:1]:69999/some/fake/path","removedFromCache":false}`, }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -1392,7 +1383,7 @@ func TestController(t *testing.T) { }, wantSyncErr: testutil.WantExactErrorString("error for WebhookAuthenticator test-name: cannot dial server: tls: failed to verify certificate: x509: certificate signed by unknown authority"), wantLogLines: []string{ - fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":{"name":"test-name"},"endpoint":"%s","removedFromCache":false}`, badWebhookAuthenticatorSpecGoodEndpointButUnknownCA.Endpoint), + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":"test-name","endpoint":"%s","removedFromCache":false}`, badWebhookAuthenticatorSpecGoodEndpointButUnknownCA.Endpoint), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -1435,7 +1426,7 @@ func TestController(t *testing.T) { }, }, wantLogLines: []string{ - fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":{"name":"test-name"},"endpoint":"%s","isOverwrite":false}`, goodWebhookDefaultServingCertEndpointBut404), + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":"test-name","endpoint":"%s","isOverwrite":false}`, goodWebhookDefaultServingCertEndpointBut404), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -1478,7 +1469,7 @@ func TestController(t *testing.T) { }, }, wantLogLines: []string{ - fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":{"name":"test-name"},"endpoint":"%s","isOverwrite":false}`, fmt.Sprintf("https://localhost:%s", localhostURL.Port())), + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":"test-name","endpoint":"%s","isOverwrite":false}`, fmt.Sprintf("https://localhost:%s", localhostURL.Port())), }, wantActions: func() []coretesting.Action { return []coretesting.Action{ @@ -1504,7 +1495,7 @@ func TestController(t *testing.T) { }, }, wantLogLines: []string{ - `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":{"name":"test-name"},"endpoint":"https://[0:0:0:0:0:0:0:1]:4242/some/fake/path","removedFromCache":false}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":"test-name","endpoint":"https://[0:0:0:0:0:0:0:1]:4242/some/fake/path","removedFromCache":false}`, }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -1555,7 +1546,7 @@ func TestController(t *testing.T) { }, }, wantLogLines: []string{ - `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":{"name":"test-name"},"endpoint":"https://[0:0:0:0:0:0:0:1]/some/fake/path","removedFromCache":false}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":"test-name","endpoint":"https://[0:0:0:0:0:0:0:1]/some/fake/path","removedFromCache":false}`, }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -1610,7 +1601,7 @@ func TestController(t *testing.T) { }, }, wantLogLines: []string{ - fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":{"name":"test-name"},"endpoint":"%s","isOverwrite":false}`, hostAs127001WebhookServer.URL), + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":"test-name","endpoint":"%s","isOverwrite":false}`, hostAs127001WebhookServer.URL), }, wantActions: func() []coretesting.Action { return []coretesting.Action{ @@ -1635,7 +1626,7 @@ func TestController(t *testing.T) { }, }, wantLogLines: []string{ - fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":{"name":"test-name"},"endpoint":"%s","removedFromCache":false}`, localWithExampleDotComWeebhookAuthenticatorSpec.Endpoint), + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":"test-name","endpoint":"%s","removedFromCache":false}`, localWithExampleDotComWeebhookAuthenticatorSpec.Endpoint), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -1681,7 +1672,7 @@ func TestController(t *testing.T) { }, }, wantLogLines: []string{ - `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":{"name":"test-name"},"endpoint":"https://0:0:0:0:0:0:0:1/some/fake/path","removedFromCache":false}`, + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":"test-name","endpoint":"https://0:0:0:0:0:0:0:1/some/fake/path","removedFromCache":false}`, }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -1731,7 +1722,7 @@ func TestController(t *testing.T) { }, }, wantLogLines: []string{ - fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":{"name":"test-name"},"endpoint":"%s","isOverwrite":false}`, goodWebhookDefaultServingCertEndpoint), + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":"test-name","endpoint":"%s","isOverwrite":false}`, goodWebhookDefaultServingCertEndpoint), }, wantActions: func() []coretesting.Action { return []coretesting.Action{ @@ -1761,7 +1752,7 @@ func TestController(t *testing.T) { }, }, wantLogLines: []string{ - fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":{"name":"test-name"},"endpoint":"%s","isOverwrite":false}`, goodWebhookDefaultServingCertEndpoint), + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":"test-name","endpoint":"%s","isOverwrite":false}`, goodWebhookDefaultServingCertEndpoint), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ From ca5bb2170cc799dd4faf5e5f2efeb0186bcab1c0 Mon Sep 17 00:00:00 2001 From: Joshua Casey Date: Tue, 30 Jul 2024 16:15:01 -0500 Subject: [PATCH 73/99] webhookcontroller should use a logger that is built for each webhook authenticator --- .../webhookcachefiller/webhookcachefiller.go | 51 ++++++++++--------- .../webhookcachefiller_test.go | 47 +++++++++++++---- 2 files changed, 65 insertions(+), 33 deletions(-) diff --git a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go index 84dde901c..82b7f1674 100644 --- a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go +++ b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go @@ -165,6 +165,11 @@ func (c *webhookCacheFillerController) syncIndividualWebhookAuthenticator(ctx co conditions := make([]*metav1.Condition, 0) caBundle, conditions, tlsBundleOk := c.validateTLSBundle(webhookAuthenticator.Spec.TLS, conditions) + + webhookSpecificLogger := c.log.WithValues( + "webhookAuthenticator", webhookAuthenticator.Name, + "endpoint", webhookAuthenticator.Spec.Endpoint) + // Only revalidate and update the cache if the cached authenticator is different from the desired authenticator. // There is no need to repeat validations for a spec that was already successfully validated. We are making a // design decision to avoid repeating the validation which dials the server, even though the server's TLS @@ -175,13 +180,12 @@ func (c *webhookCacheFillerController) syncIndividualWebhookAuthenticator(ctx co // than to constantly monitor for external issues. var oldWebhookAuthenticatorFromCache *cachedWebhookAuthenticator if valueFromCache := c.cache.Get(cacheKey); valueFromCache != nil { - oldWebhookAuthenticatorFromCache = c.cacheValueAsWebhookAuthenticator(valueFromCache) + oldWebhookAuthenticatorFromCache = c.cacheValueAsWebhookAuthenticator(valueFromCache, webhookSpecificLogger) if oldWebhookAuthenticatorFromCache != nil && reflect.DeepEqual(oldWebhookAuthenticatorFromCache.spec, &webhookAuthenticator.Spec) && tlsBundleOk && // if there was any error while validating the CA bundle, then run remaining validations and update status oldWebhookAuthenticatorFromCache.caBundleHash.Equal(caBundle.Hash()) { - c.log.WithValues("webhookAuthenticator", webhookAuthenticator.Name, "endpoint", webhookAuthenticator.Spec.Endpoint). - Info("cached webhook authenticator and desired webhook authenticator are the same: already cached, so skipping validations") + webhookSpecificLogger.Info("cached webhook authenticator and desired webhook authenticator are the same: already cached, so skipping validations") // Stop, no more work to be done. This authenticator is already validated and cached. return nil } @@ -191,7 +195,7 @@ func (c *webhookCacheFillerController) syncIndividualWebhookAuthenticator(ctx co endpointHostPort, conditions, endpointOk := c.validateEndpoint(webhookAuthenticator.Spec.Endpoint, conditions) okSoFar := tlsBundleOk && endpointOk - conditions, tlsNegotiateErr := c.validateConnection(caBundle.CertPool(), endpointHostPort, conditions, okSoFar) + conditions, tlsNegotiateErr := c.validateConnection(caBundle.CertPool(), endpointHostPort, conditions, okSoFar, webhookSpecificLogger) errs = append(errs, tlsNegotiateErr) okSoFar = okSoFar && tlsNegotiateErr == nil @@ -214,14 +218,11 @@ func (c *webhookCacheFillerController) syncIndividualWebhookAuthenticator(ctx co // validated and cached. Do not allow an old, previously validated spec of the authenticator to continue // being used for authentication. c.cache.Delete(cacheKey) - c.log.WithValues( - "webhookAuthenticator", webhookAuthenticator.Name, - "endpoint", webhookAuthenticator.Spec.Endpoint, - "removedFromCache", oldWebhookAuthenticatorFromCache != nil, - ).Info("invalid webhook authenticator") + webhookSpecificLogger.Info("invalid webhook authenticator", + "removedFromCache", oldWebhookAuthenticatorFromCache != nil) } - updateErr := c.updateStatus(ctx, webhookAuthenticator, conditions) + updateErr := c.updateStatus(ctx, webhookAuthenticator, conditions, webhookSpecificLogger) errs = append(errs, updateErr) // Only add this WebhookAuthenticator to the cache if the status update succeeds. @@ -233,11 +234,8 @@ func (c *webhookCacheFillerController) syncIndividualWebhookAuthenticator(ctx co spec: webhookAuthenticator.Spec.DeepCopy(), // deep copy to avoid caching original object caBundleHash: caBundle.Hash(), }) - c.log.WithValues( - "webhookAuthenticator", webhookAuthenticator.Name, - "endpoint", webhookAuthenticator.Spec.Endpoint, - "isOverwrite", oldWebhookAuthenticatorFromCache != nil, - ).Info("added or updated webhook authenticator in cache") + webhookSpecificLogger.Info("added or updated webhook authenticator in cache", + "isOverwrite", oldWebhookAuthenticatorFromCache != nil) } // Sync loop errors: @@ -248,14 +246,15 @@ func (c *webhookCacheFillerController) syncIndividualWebhookAuthenticator(ctx co return utilerrors.NewAggregate(errs) } -func (c *webhookCacheFillerController) cacheValueAsWebhookAuthenticator(value authncache.Value) *cachedWebhookAuthenticator { +func (c *webhookCacheFillerController) cacheValueAsWebhookAuthenticator(value authncache.Value, log plog.Logger) *cachedWebhookAuthenticator { webhookAuthenticator, ok := value.(*cachedWebhookAuthenticator) if !ok { actualType := "" if t := reflect.TypeOf(value); t != nil { actualType = t.String() } - c.log.WithValues("actualType", actualType).Info("wrong webhook authenticator type in cache") + log.Info("wrong webhook authenticator type in cache", + "actualType", actualType) return nil } return webhookAuthenticator @@ -352,7 +351,13 @@ func newWebhookAuthenticator( return webhookAuthenticator, conditions, nil } -func (c *webhookCacheFillerController) validateConnection(certPool *x509.CertPool, endpointHostPort *endpointaddr.HostPort, conditions []*metav1.Condition, prereqOk bool) ([]*metav1.Condition, error) { +func (c *webhookCacheFillerController) validateConnection( + certPool *x509.CertPool, + endpointHostPort *endpointaddr.HostPort, + conditions []*metav1.Condition, + prereqOk bool, + logger plog.Logger, +) ([]*metav1.Condition, error) { if !prereqOk { conditions = append(conditions, &metav1.Condition{ Type: typeWebhookConnectionValid, @@ -381,7 +386,7 @@ func (c *webhookCacheFillerController) validateConnection(certPool *x509.CertPoo err = conn.Close() if err != nil { // no unit test for this failure - c.log.Error("error closing dialer", err) + logger.Error("error closing dialer", err) } conditions = append(conditions, &metav1.Condition{ @@ -443,6 +448,7 @@ func (c *webhookCacheFillerController) updateStatus( ctx context.Context, original *authenticationv1alpha1.WebhookAuthenticator, conditions []*metav1.Condition, + logger plog.Logger, ) error { updated := original.DeepCopy() @@ -464,14 +470,11 @@ func (c *webhookCacheFillerController) updateStatus( }) } - // TODO: this should use c.log.WithValues("webhookAuthenticator", original.Name) - log := plog.New().WithName(controllerName).WithValues("webhookAuthenticator", original.Name) - _ = conditionsutil.MergeConditions( conditions, original.Generation, &updated.Status.Conditions, - log, + logger, metav1.NewTime(c.clock.Now()), ) @@ -480,7 +483,7 @@ func (c *webhookCacheFillerController) updateStatus( } _, err := c.client.AuthenticationV1alpha1().WebhookAuthenticators().UpdateStatus(ctx, updated, metav1.UpdateOptions{}) if err == nil { - log.Debug("webhookauthenticator status successfully updated") + logger.Debug("webhookauthenticator status successfully updated") } return err } diff --git a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go index afa295d90..06f2f63eb 100644 --- a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go +++ b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go @@ -491,8 +491,11 @@ func TestController(t *testing.T) { }, wantLogLines: []string{ fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":"another-invalid-webhook-authenticator","endpoint":"%s","removedFromCache":false}`, goodWebhookDefaultServingCertEndpoint), + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"another-invalid-webhook-authenticator","endpoint":"%s"}`, goodWebhookDefaultServingCertEndpoint), fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":"existing-webhook-authenticator","endpoint":"%s","isOverwrite":false}`, goodWebhookDefaultServingCertEndpoint), fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":"invalid-webhook-authenticator","endpoint":"%s","removedFromCache":false}`, goodWebhookDefaultServingCertEndpoint), + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"invalid-webhook-authenticator","endpoint":"%s"}`, goodWebhookDefaultServingCertEndpoint), + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"new-webhook-authenticator","endpoint":"%s"}`, goodWebhookDefaultServingCertEndpoint), fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":"new-webhook-authenticator","endpoint":"%s","isOverwrite":false}`, goodWebhookDefaultServingCertEndpoint), }, wantActions: func() []coretesting.Action { @@ -573,6 +576,7 @@ func TestController(t *testing.T) { someSecretWithCA, }, wantLogLines: []string{ + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"%s"}`, goodWebhookDefaultServingCertEndpoint), fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":"test-name","endpoint":"%s","isOverwrite":false}`, goodWebhookDefaultServingCertEndpoint), }, wantActions: func() []coretesting.Action { @@ -609,6 +613,7 @@ func TestController(t *testing.T) { someConfigMapWithCA, }, wantLogLines: []string{ + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"%s"}`, goodWebhookDefaultServingCertEndpoint), fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":"test-name","endpoint":"%s","isOverwrite":false}`, goodWebhookDefaultServingCertEndpoint), }, wantActions: func() []coretesting.Action { @@ -655,6 +660,7 @@ func TestController(t *testing.T) { someConfigMapWithCA, }, wantLogLines: []string{ + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"%s"}`, goodWebhookDefaultServingCertEndpoint), fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":"test-name","endpoint":"%s","isOverwrite":true}`, goodWebhookDefaultServingCertEndpoint), }, wantActions: func() []coretesting.Action { @@ -703,6 +709,7 @@ func TestController(t *testing.T) { }, wantLogLines: []string{ fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":"test-name","endpoint":"%s","removedFromCache":true}`, badWebhookAuthenticatorSpecInvalidTLS.Endpoint), + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"%s"}`, goodWebhookDefaultServingCertEndpoint), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -759,7 +766,8 @@ func TestController(t *testing.T) { }, }, wantLogLines: []string{ - fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"cached webhook authenticator and desired webhook authenticator are the same: already cached, so skipping validations","webhookAuthenticator":{"name":"test-name"},"endpoint":"%s"}`, goodWebhookDefaultServingCertEndpoint), + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"cached webhook authenticator and desired webhook authenticator are the same: already cached, so skipping validations","webhookAuthenticator":"test-name","endpoint":"%s"}`, goodWebhookDefaultServingCertEndpoint), + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":"test-name","endpoint":"%s","isOverwrite":true}`, goodWebhookDefaultServingCertEndpoint), }, wantActions: func() []coretesting.Action { return []coretesting.Action{ @@ -799,7 +807,8 @@ func TestController(t *testing.T) { }, }, wantLogLines: []string{ - `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).cacheValueAsWebhookAuthenticator","message":"wrong webhook authenticator type in cache","actualType":"*mockcachevalue.MockValue"}`, + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).cacheValueAsWebhookAuthenticator","message":"wrong webhook authenticator type in cache","webhookAuthenticator":"test-name","endpoint":"%s","actualType":"*mockcachevalue.MockValue"}`, goodWebhookDefaultServingCertEndpoint), + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"%s"}`, goodWebhookDefaultServingCertEndpoint), fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":"test-name","endpoint":"%s","isOverwrite":false}`, goodWebhookDefaultServingCertEndpoint), }, wantActions: func() []coretesting.Action { @@ -856,6 +865,7 @@ func TestController(t *testing.T) { }, }, wantLogLines: []string{ + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"%s"}`, goodWebhookDefaultServingCertEndpoint), fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":"test-name","endpoint":"%s","isOverwrite":true}`, goodWebhookDefaultServingCertEndpoint), }, wantActions: func() []coretesting.Action { @@ -916,7 +926,7 @@ func TestController(t *testing.T) { Spec: goodWebhookAuthenticatorSpecWithCA, }, }, - wantLogLines: []string{}, // wants no logs + wantLogLines: nil, // wants no logs wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -950,6 +960,7 @@ func TestController(t *testing.T) { }, }, wantLogLines: []string{ + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"%s"}`, goodWebhookDefaultServingCertEndpoint), fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":"test-name","endpoint":"%s","isOverwrite":false}`, goodWebhookDefaultServingCertEndpoint), }, wantActions: func() []coretesting.Action { @@ -990,6 +1001,7 @@ func TestController(t *testing.T) { }, }, wantLogLines: []string{ + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"%s"}`, hostLocalIPv6Server.URL), fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":"test-name","endpoint":"%s","isOverwrite":false}`, hostLocalIPv6Server.URL), }, wantActions: func() []coretesting.Action { @@ -1031,6 +1043,7 @@ func TestController(t *testing.T) { }, wantLogLines: []string{ fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":"test-name","endpoint":"%s","removedFromCache":false}`, goodWebhookAuthenticatorSpecWithoutCA.Endpoint), + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"%s"}`, goodWebhookAuthenticatorSpecWithoutCA.Endpoint), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -1073,6 +1086,7 @@ func TestController(t *testing.T) { }, wantLogLines: []string{ fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":"test-name","endpoint":"%s","removedFromCache":false}`, badWebhookAuthenticatorSpecInvalidTLS.Endpoint), + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"%s"}`, badWebhookAuthenticatorSpecInvalidTLS.Endpoint), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -1128,6 +1142,7 @@ func TestController(t *testing.T) { }, wantLogLines: []string{ fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":"test-name","endpoint":"%s","removedFromCache":true}`, badEndpointInvalidURL), + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"%s"}`, badEndpointInvalidURL), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -1242,6 +1257,7 @@ func TestController(t *testing.T) { }, wantLogLines: []string{ fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":"test-name","endpoint":"%s","removedFromCache":false}`, badEndpointInvalidURL), + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"%s"}`, badEndpointInvalidURL), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -1288,6 +1304,7 @@ func TestController(t *testing.T) { }, wantLogLines: []string{ fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":"test-name","endpoint":"%s","removedFromCache":false}`, badEndpointNoHTTPS), + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"%s"}`, badEndpointNoHTTPS), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -1337,6 +1354,7 @@ func TestController(t *testing.T) { }, wantLogLines: []string{ `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":"test-name","endpoint":"https://[0:0:0:0:0:0:0:1]:69999/some/fake/path","removedFromCache":false}`, + `{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"https://[0:0:0:0:0:0:0:1]:69999/some/fake/path"}`, }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -1384,6 +1402,7 @@ func TestController(t *testing.T) { wantSyncErr: testutil.WantExactErrorString("error for WebhookAuthenticator test-name: cannot dial server: tls: failed to verify certificate: x509: certificate signed by unknown authority"), wantLogLines: []string{ fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":"test-name","endpoint":"%s","removedFromCache":false}`, badWebhookAuthenticatorSpecGoodEndpointButUnknownCA.Endpoint), + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"%s"}`, badWebhookAuthenticatorSpecGoodEndpointButUnknownCA.Endpoint), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -1426,6 +1445,7 @@ func TestController(t *testing.T) { }, }, wantLogLines: []string{ + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"%s"}`, goodWebhookDefaultServingCertEndpointBut404), fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":"test-name","endpoint":"%s","isOverwrite":false}`, goodWebhookDefaultServingCertEndpointBut404), }, wantActions: func() []coretesting.Action { @@ -1496,6 +1516,7 @@ func TestController(t *testing.T) { }, wantLogLines: []string{ `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":"test-name","endpoint":"https://[0:0:0:0:0:0:0:1]:4242/some/fake/path","removedFromCache":false}`, + `{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"https://[0:0:0:0:0:0:0:1]:4242/some/fake/path"}`, }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -1547,6 +1568,7 @@ func TestController(t *testing.T) { }, wantLogLines: []string{ `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":"test-name","endpoint":"https://[0:0:0:0:0:0:0:1]/some/fake/path","removedFromCache":false}`, + `{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"https://[0:0:0:0:0:0:0:1]/some/fake/path"}`, }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -1627,6 +1649,7 @@ func TestController(t *testing.T) { }, wantLogLines: []string{ fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":"test-name","endpoint":"%s","removedFromCache":false}`, localWithExampleDotComWeebhookAuthenticatorSpec.Endpoint), + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"%s"}`, localWithExampleDotComWeebhookAuthenticatorSpec.Endpoint), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -1673,6 +1696,7 @@ func TestController(t *testing.T) { }, wantLogLines: []string{ `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":"test-name","endpoint":"https://0:0:0:0:0:0:0:1/some/fake/path","removedFromCache":false}`, + `{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"https://0:0:0:0:0:0:0:1/some/fake/path"}`, }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -1752,6 +1776,7 @@ func TestController(t *testing.T) { }, }, wantLogLines: []string{ + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"%s"}`, goodWebhookDefaultServingCertEndpoint), fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":"test-name","endpoint":"%s","isOverwrite":false}`, goodWebhookDefaultServingCertEndpoint), }, wantActions: func() []coretesting.Action { @@ -1793,7 +1818,7 @@ func TestController(t *testing.T) { Spec: goodWebhookAuthenticatorSpecWithCA, }, }, - wantLogLines: []string{}, // wants no logs + wantLogLines: nil, // wants no logs wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -1865,12 +1890,16 @@ func TestController(t *testing.T) { require.Equal(t, tt.wantActions(), pinnipedAPIClient.Actions()) require.Equal(t, len(tt.wantNamesOfWebhookAuthenticatorsInCache), len(cache.Keys()), fmt.Sprintf("expected cache entries is incorrect. wanted:%d, got: %d, keys: %v", len(tt.wantNamesOfWebhookAuthenticatorsInCache), len(cache.Keys()), cache.Keys())) - if len(tt.wantLogLines) == 0 { - require.Empty(t, log.String()) - } else { - actualLog, _ := strings.CutSuffix(log.String(), "\n") - require.Equal(t, tt.wantLogLines, strings.Split(actualLog, "\n")) + actualLog, _ := strings.CutSuffix(log.String(), "\n") + actualLogLines := strings.Split(actualLog, "\n") + var webhookLogLines []string + for _, line := range actualLogLines { + if strings.Contains(line, "webhookcachefiller/webhookcachefiller.go") { + webhookLogLines = append(webhookLogLines, line) + } } + + require.Equal(t, tt.wantLogLines, webhookLogLines) }) } } From 1438f06c12d0f1e81b20f446c43b6d219cb99720 Mon Sep 17 00:00:00 2001 From: Joshua Casey Date: Tue, 30 Jul 2024 16:33:40 -0500 Subject: [PATCH 74/99] webhookcachefiller adds more detail when it chooses to update or not update status conditions --- .../webhookcachefiller/webhookcachefiller.go | 5 +- .../webhookcachefiller_test.go | 54 ++++++++++--------- 2 files changed, 33 insertions(+), 26 deletions(-) diff --git a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go index 82b7f1674..9112ab55d 100644 --- a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go +++ b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go @@ -479,11 +479,14 @@ func (c *webhookCacheFillerController) updateStatus( ) if equality.Semantic.DeepEqual(original, updated) { + logger.Debug("choosing to not update the webhookauthenticator status since there is no update to make", + "phase", updated.Status.Phase) return nil } _, err := c.client.AuthenticationV1alpha1().WebhookAuthenticators().UpdateStatus(ctx, updated, metav1.UpdateOptions{}) if err == nil { - logger.Debug("webhookauthenticator status successfully updated") + logger.Debug("webhookauthenticator status successfully updated", + "phase", updated.Status.Phase) } return err } diff --git a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go index 06f2f63eb..9d365f7bc 100644 --- a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go +++ b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go @@ -447,6 +447,7 @@ func TestController(t *testing.T) { }, }, wantLogLines: []string{ + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"choosing to not update the webhookauthenticator status since there is no update to make","webhookAuthenticator":"test-name","endpoint":"%s","phase":"Ready"}`, goodWebhookDefaultServingCertEndpoint), fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":"test-name","endpoint":"%s","isOverwrite":false}`, goodWebhookDefaultServingCertEndpoint), }, wantActions: func() []coretesting.Action { @@ -491,11 +492,12 @@ func TestController(t *testing.T) { }, wantLogLines: []string{ fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":"another-invalid-webhook-authenticator","endpoint":"%s","removedFromCache":false}`, goodWebhookDefaultServingCertEndpoint), - fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"another-invalid-webhook-authenticator","endpoint":"%s"}`, goodWebhookDefaultServingCertEndpoint), + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"another-invalid-webhook-authenticator","endpoint":"%s","phase":"Error"}`, goodWebhookDefaultServingCertEndpoint), + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"choosing to not update the webhookauthenticator status since there is no update to make","webhookAuthenticator":"existing-webhook-authenticator","endpoint":"%s","phase":"Ready"}`, goodWebhookDefaultServingCertEndpoint), fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":"existing-webhook-authenticator","endpoint":"%s","isOverwrite":false}`, goodWebhookDefaultServingCertEndpoint), fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":"invalid-webhook-authenticator","endpoint":"%s","removedFromCache":false}`, goodWebhookDefaultServingCertEndpoint), - fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"invalid-webhook-authenticator","endpoint":"%s"}`, goodWebhookDefaultServingCertEndpoint), - fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"new-webhook-authenticator","endpoint":"%s"}`, goodWebhookDefaultServingCertEndpoint), + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"invalid-webhook-authenticator","endpoint":"%s","phase":"Error"}`, goodWebhookDefaultServingCertEndpoint), + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"new-webhook-authenticator","endpoint":"%s","phase":"Ready"}`, goodWebhookDefaultServingCertEndpoint), fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":"new-webhook-authenticator","endpoint":"%s","isOverwrite":false}`, goodWebhookDefaultServingCertEndpoint), }, wantActions: func() []coretesting.Action { @@ -576,7 +578,7 @@ func TestController(t *testing.T) { someSecretWithCA, }, wantLogLines: []string{ - fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"%s"}`, goodWebhookDefaultServingCertEndpoint), + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"%s","phase":"Ready"}`, goodWebhookDefaultServingCertEndpoint), fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":"test-name","endpoint":"%s","isOverwrite":false}`, goodWebhookDefaultServingCertEndpoint), }, wantActions: func() []coretesting.Action { @@ -613,7 +615,7 @@ func TestController(t *testing.T) { someConfigMapWithCA, }, wantLogLines: []string{ - fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"%s"}`, goodWebhookDefaultServingCertEndpoint), + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"%s","phase":"Ready"}`, goodWebhookDefaultServingCertEndpoint), fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":"test-name","endpoint":"%s","isOverwrite":false}`, goodWebhookDefaultServingCertEndpoint), }, wantActions: func() []coretesting.Action { @@ -660,7 +662,7 @@ func TestController(t *testing.T) { someConfigMapWithCA, }, wantLogLines: []string{ - fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"%s"}`, goodWebhookDefaultServingCertEndpoint), + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"%s","phase":"Ready"}`, goodWebhookDefaultServingCertEndpoint), fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":"test-name","endpoint":"%s","isOverwrite":true}`, goodWebhookDefaultServingCertEndpoint), }, wantActions: func() []coretesting.Action { @@ -709,7 +711,7 @@ func TestController(t *testing.T) { }, wantLogLines: []string{ fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":"test-name","endpoint":"%s","removedFromCache":true}`, badWebhookAuthenticatorSpecInvalidTLS.Endpoint), - fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"%s"}`, goodWebhookDefaultServingCertEndpoint), + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"%s","phase":"Error"}`, goodWebhookDefaultServingCertEndpoint), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -767,7 +769,6 @@ func TestController(t *testing.T) { }, wantLogLines: []string{ fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"cached webhook authenticator and desired webhook authenticator are the same: already cached, so skipping validations","webhookAuthenticator":"test-name","endpoint":"%s"}`, goodWebhookDefaultServingCertEndpoint), - fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":"test-name","endpoint":"%s","isOverwrite":true}`, goodWebhookDefaultServingCertEndpoint), }, wantActions: func() []coretesting.Action { return []coretesting.Action{ @@ -808,7 +809,7 @@ func TestController(t *testing.T) { }, wantLogLines: []string{ fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).cacheValueAsWebhookAuthenticator","message":"wrong webhook authenticator type in cache","webhookAuthenticator":"test-name","endpoint":"%s","actualType":"*mockcachevalue.MockValue"}`, goodWebhookDefaultServingCertEndpoint), - fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"%s"}`, goodWebhookDefaultServingCertEndpoint), + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"%s","phase":"Ready"}`, goodWebhookDefaultServingCertEndpoint), fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":"test-name","endpoint":"%s","isOverwrite":false}`, goodWebhookDefaultServingCertEndpoint), }, wantActions: func() []coretesting.Action { @@ -865,7 +866,7 @@ func TestController(t *testing.T) { }, }, wantLogLines: []string{ - fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"%s"}`, goodWebhookDefaultServingCertEndpoint), + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"%s","phase":"Ready"}`, goodWebhookDefaultServingCertEndpoint), fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":"test-name","endpoint":"%s","isOverwrite":true}`, goodWebhookDefaultServingCertEndpoint), }, wantActions: func() []coretesting.Action { @@ -960,7 +961,7 @@ func TestController(t *testing.T) { }, }, wantLogLines: []string{ - fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"%s"}`, goodWebhookDefaultServingCertEndpoint), + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"%s","phase":"Ready"}`, goodWebhookDefaultServingCertEndpoint), fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":"test-name","endpoint":"%s","isOverwrite":false}`, goodWebhookDefaultServingCertEndpoint), }, wantActions: func() []coretesting.Action { @@ -1001,7 +1002,7 @@ func TestController(t *testing.T) { }, }, wantLogLines: []string{ - fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"%s"}`, hostLocalIPv6Server.URL), + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"%s","phase":"Ready"}`, hostLocalIPv6Server.URL), fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":"test-name","endpoint":"%s","isOverwrite":false}`, hostLocalIPv6Server.URL), }, wantActions: func() []coretesting.Action { @@ -1043,7 +1044,7 @@ func TestController(t *testing.T) { }, wantLogLines: []string{ fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":"test-name","endpoint":"%s","removedFromCache":false}`, goodWebhookAuthenticatorSpecWithoutCA.Endpoint), - fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"%s"}`, goodWebhookAuthenticatorSpecWithoutCA.Endpoint), + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"%s","phase":"Error"}`, goodWebhookAuthenticatorSpecWithoutCA.Endpoint), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -1086,7 +1087,7 @@ func TestController(t *testing.T) { }, wantLogLines: []string{ fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":"test-name","endpoint":"%s","removedFromCache":false}`, badWebhookAuthenticatorSpecInvalidTLS.Endpoint), - fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"%s"}`, badWebhookAuthenticatorSpecInvalidTLS.Endpoint), + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"%s","phase":"Error"}`, badWebhookAuthenticatorSpecInvalidTLS.Endpoint), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -1142,7 +1143,7 @@ func TestController(t *testing.T) { }, wantLogLines: []string{ fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":"test-name","endpoint":"%s","removedFromCache":true}`, badEndpointInvalidURL), - fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"%s"}`, badEndpointInvalidURL), + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"%s","phase":"Error"}`, badEndpointInvalidURL), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -1257,7 +1258,7 @@ func TestController(t *testing.T) { }, wantLogLines: []string{ fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":"test-name","endpoint":"%s","removedFromCache":false}`, badEndpointInvalidURL), - fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"%s"}`, badEndpointInvalidURL), + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"%s","phase":"Error"}`, badEndpointInvalidURL), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -1304,7 +1305,7 @@ func TestController(t *testing.T) { }, wantLogLines: []string{ fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":"test-name","endpoint":"%s","removedFromCache":false}`, badEndpointNoHTTPS), - fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"%s"}`, badEndpointNoHTTPS), + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"%s","phase":"Error"}`, badEndpointNoHTTPS), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -1354,7 +1355,7 @@ func TestController(t *testing.T) { }, wantLogLines: []string{ `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":"test-name","endpoint":"https://[0:0:0:0:0:0:0:1]:69999/some/fake/path","removedFromCache":false}`, - `{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"https://[0:0:0:0:0:0:0:1]:69999/some/fake/path"}`, + `{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"https://[0:0:0:0:0:0:0:1]:69999/some/fake/path","phase":"Error"}`, }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -1402,7 +1403,7 @@ func TestController(t *testing.T) { wantSyncErr: testutil.WantExactErrorString("error for WebhookAuthenticator test-name: cannot dial server: tls: failed to verify certificate: x509: certificate signed by unknown authority"), wantLogLines: []string{ fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":"test-name","endpoint":"%s","removedFromCache":false}`, badWebhookAuthenticatorSpecGoodEndpointButUnknownCA.Endpoint), - fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"%s"}`, badWebhookAuthenticatorSpecGoodEndpointButUnknownCA.Endpoint), + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"%s","phase":"Error"}`, badWebhookAuthenticatorSpecGoodEndpointButUnknownCA.Endpoint), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -1445,7 +1446,7 @@ func TestController(t *testing.T) { }, }, wantLogLines: []string{ - fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"%s"}`, goodWebhookDefaultServingCertEndpointBut404), + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"%s","phase":"Ready"}`, goodWebhookDefaultServingCertEndpointBut404), fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":"test-name","endpoint":"%s","isOverwrite":false}`, goodWebhookDefaultServingCertEndpointBut404), }, wantActions: func() []coretesting.Action { @@ -1489,6 +1490,7 @@ func TestController(t *testing.T) { }, }, wantLogLines: []string{ + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"choosing to not update the webhookauthenticator status since there is no update to make","webhookAuthenticator":"test-name","endpoint":"%s","phase":"Ready"}`, fmt.Sprintf("https://localhost:%s", localhostURL.Port())), fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":"test-name","endpoint":"%s","isOverwrite":false}`, fmt.Sprintf("https://localhost:%s", localhostURL.Port())), }, wantActions: func() []coretesting.Action { @@ -1516,7 +1518,7 @@ func TestController(t *testing.T) { }, wantLogLines: []string{ `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":"test-name","endpoint":"https://[0:0:0:0:0:0:0:1]:4242/some/fake/path","removedFromCache":false}`, - `{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"https://[0:0:0:0:0:0:0:1]:4242/some/fake/path"}`, + `{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"https://[0:0:0:0:0:0:0:1]:4242/some/fake/path","phase":"Error"}`, }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -1568,7 +1570,7 @@ func TestController(t *testing.T) { }, wantLogLines: []string{ `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":"test-name","endpoint":"https://[0:0:0:0:0:0:0:1]/some/fake/path","removedFromCache":false}`, - `{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"https://[0:0:0:0:0:0:0:1]/some/fake/path"}`, + `{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"https://[0:0:0:0:0:0:0:1]/some/fake/path","phase":"Error"}`, }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -1623,6 +1625,7 @@ func TestController(t *testing.T) { }, }, wantLogLines: []string{ + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"choosing to not update the webhookauthenticator status since there is no update to make","webhookAuthenticator":"test-name","endpoint":"%s","phase":"Ready"}`, hostAs127001WebhookServer.URL), fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":"test-name","endpoint":"%s","isOverwrite":false}`, hostAs127001WebhookServer.URL), }, wantActions: func() []coretesting.Action { @@ -1649,7 +1652,7 @@ func TestController(t *testing.T) { }, wantLogLines: []string{ fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":"test-name","endpoint":"%s","removedFromCache":false}`, localWithExampleDotComWeebhookAuthenticatorSpec.Endpoint), - fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"%s"}`, localWithExampleDotComWeebhookAuthenticatorSpec.Endpoint), + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"%s","phase":"Error"}`, localWithExampleDotComWeebhookAuthenticatorSpec.Endpoint), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -1696,7 +1699,7 @@ func TestController(t *testing.T) { }, wantLogLines: []string{ `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"invalid webhook authenticator","webhookAuthenticator":"test-name","endpoint":"https://0:0:0:0:0:0:0:1/some/fake/path","removedFromCache":false}`, - `{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"https://0:0:0:0:0:0:0:1/some/fake/path"}`, + `{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"https://0:0:0:0:0:0:0:1/some/fake/path","phase":"Error"}`, }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ @@ -1746,6 +1749,7 @@ func TestController(t *testing.T) { }, }, wantLogLines: []string{ + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"choosing to not update the webhookauthenticator status since there is no update to make","webhookAuthenticator":"test-name","endpoint":"%s","phase":"Ready"}`, goodWebhookDefaultServingCertEndpoint), fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":"test-name","endpoint":"%s","isOverwrite":false}`, goodWebhookDefaultServingCertEndpoint), }, wantActions: func() []coretesting.Action { @@ -1776,7 +1780,7 @@ func TestController(t *testing.T) { }, }, wantLogLines: []string{ - fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"%s"}`, goodWebhookDefaultServingCertEndpoint), + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"%s","phase":"Ready"}`, goodWebhookDefaultServingCertEndpoint), fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"added or updated webhook authenticator in cache","webhookAuthenticator":"test-name","endpoint":"%s","isOverwrite":false}`, goodWebhookDefaultServingCertEndpoint), }, wantActions: func() []coretesting.Action { From 15c84fcc940e574a83e6241bc231f0b88d66cca0 Mon Sep 17 00:00:00 2001 From: Ryan Richard Date: Tue, 30 Jul 2024 16:41:50 -0700 Subject: [PATCH 75/99] extract helper func in jwtcachefiller and webhookcachefiller --- .../jwtcachefiller/jwtcachefiller.go | 93 +++++++++++-------- .../jwtcachefiller/jwtcachefiller_test.go | 6 +- .../webhookcachefiller/webhookcachefiller.go | 76 +++++++++------ .../webhookcachefiller_test.go | 4 +- 4 files changed, 112 insertions(+), 67 deletions(-) diff --git a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go index e1682d72c..e3eedaeeb 100644 --- a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go +++ b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go @@ -111,7 +111,7 @@ type tokenAuthenticatorCloser interface { type cachedJWTAuthenticator struct { authenticator.Token - spec *authenticationv1alpha1.JWTAuthenticatorSpec + issuer string caBundleHash tlsconfigutil.CABundleHash cancel context.CancelFunc } @@ -220,9 +220,18 @@ func (c *jwtCacheFillerController) syncIndividualJWTAuthenticator(ctx context.Co Name: jwtAuthenticator.Name, } + logger := c.log.WithValues( + "jwtAuthenticator", klog.KObj(jwtAuthenticator), + "issuer", jwtAuthenticator.Spec.Issuer) + + var errs []error conditions := make([]*metav1.Condition, 0) + caBundle, conditions, tlsBundleOk := c.validateTLSBundle(jwtAuthenticator.Spec.TLS, conditions) + conditions, issuerOk := c.validateIssuer(jwtAuthenticator.Spec.Issuer, conditions) + okSoFar := tlsBundleOk && issuerOk + // Only revalidate and update the cache if the cached authenticator is different from the desired authenticator. // There is no need to repeat validations for a spec that was already successfully validated. We are making a // design decision to avoid repeating the validation which dials the server, even though the server's TLS @@ -231,24 +240,15 @@ func (c *jwtCacheFillerController) syncIndividualJWTAuthenticator(ctx context.Co // rather than trying to show the most up-to-date status possible. These validations are for administrator // convenience at the time of a configuration change, to catch typos and blatant misconfigurations, rather // than to constantly monitor for external issues. - var oldJWTAuthenticatorFromCache *cachedJWTAuthenticator - if valueFromCache := c.cache.Get(cacheKey); valueFromCache != nil { - oldJWTAuthenticatorFromCache = c.cacheValueAsJWTAuthenticator(valueFromCache) - if oldJWTAuthenticatorFromCache != nil && - reflect.DeepEqual(oldJWTAuthenticatorFromCache.spec, &jwtAuthenticator.Spec) && - tlsBundleOk && // if there was any error while validating the CA bundle, then run remaining validations and update status - oldJWTAuthenticatorFromCache.caBundleHash.Equal(caBundle.Hash()) { - c.log.WithValues("jwtAuthenticator", klog.KObj(jwtAuthenticator), "issuer", jwtAuthenticator.Spec.Issuer). - Info("cached jwt authenticator and desired jwt authenticator are the same: already cached, so skipping validations") - // Stop, no more work to be done. This authenticator is already validated and cached. - return nil - } + foundAuthenticatorInCache, alreadyValidatedTheseSettings := c.havePreviouslyValidated( + cacheKey, jwtAuthenticator.Spec.Issuer, tlsBundleOk, caBundle.Hash(), logger) + if alreadyValidatedTheseSettings { + // Stop, no more work to be done. This authenticator is already validated and cached. + // TODO: Append hardcoded list of remaining success conditions and skip redoing the remaining validations below. + // Make sure that the conditions array is still checked for any errors, and the status and cache are still updated, as below. + return nil } - var errs []error - _, conditions, issuerOk := c.validateIssuer(jwtAuthenticator.Spec.Issuer, conditions) - okSoFar := tlsBundleOk && issuerOk - client := phttp.Default(caBundle.CertPool()) client.Timeout = 30 * time.Second // copied from Kube OIDC code coreOSCtx := coreosoidc.ClientContext(context.Background(), client) @@ -283,11 +283,8 @@ func (c *jwtCacheFillerController) syncIndividualJWTAuthenticator(ctx context.Co // validated and cached. Do not allow an old, previously validated spec of the authenticator to continue // being used for authentication. c.cache.Delete(cacheKey) - c.log.WithValues( - "jwtAuthenticator", klog.KObj(jwtAuthenticator), - "issuer", jwtAuthenticator.Spec.Issuer, - "removedFromCache", oldJWTAuthenticatorFromCache != nil, - ).Info("invalid jwt authenticator") + logger.Info("invalid jwt authenticator", + "removedFromCache", foundAuthenticatorInCache) } updateErr := c.updateStatus(ctx, jwtAuthenticator, conditions) @@ -298,11 +295,8 @@ func (c *jwtCacheFillerController) syncIndividualJWTAuthenticator(ctx context.Co // and skip trying to update its status again, which would leave its old status permanently intact. if authenticatorValid && updateErr == nil { c.cache.Store(cacheKey, newJWTAuthenticatorForCache) - c.log.WithValues( - "jwtAuthenticator", klog.KObj(jwtAuthenticator), - "issuer", jwtAuthenticator.Spec.Issuer, - "isOverwrite", oldJWTAuthenticatorFromCache != nil, - ).Info("added or updated jwt authenticator in cache") + logger.Info("added or updated jwt authenticator in cache", + "isOverwrite", foundAuthenticatorInCache) } // Sync loop errors: @@ -313,14 +307,39 @@ func (c *jwtCacheFillerController) syncIndividualJWTAuthenticator(ctx context.Co return utilerrors.NewAggregate(errs) } -func (c *jwtCacheFillerController) cacheValueAsJWTAuthenticator(value authncache.Value) *cachedJWTAuthenticator { +func (c *jwtCacheFillerController) havePreviouslyValidated( + cacheKey authncache.Key, + issuer string, + tlsBundleOk bool, + caBundleHash tlsconfigutil.CABundleHash, + logger plog.Logger, +) (bool, bool) { + var authenticatorFromCache *cachedJWTAuthenticator + valueFromCache := c.cache.Get(cacheKey) + if valueFromCache == nil { + return false, false + } + authenticatorFromCache = c.cacheValueAsJWTAuthenticator(valueFromCache, logger) + if authenticatorFromCache == nil { + return false, false + } + if authenticatorFromCache.issuer == issuer && + tlsBundleOk && // if there was any error while validating the latest CA bundle, then do not consider it previously validated + authenticatorFromCache.caBundleHash.Equal(caBundleHash) { + logger.Info("cached jwt authenticator and desired jwt authenticator are the same: already cached, so skipping validations") + return true, true + } + return true, false // found the authenticator, but it had not been previously validated with these same settings +} + +func (c *jwtCacheFillerController) cacheValueAsJWTAuthenticator(value authncache.Value, logger plog.Logger) *cachedJWTAuthenticator { jwtAuthenticator, ok := value.(*cachedJWTAuthenticator) if !ok { actualType := "" if t := reflect.TypeOf(value); t != nil { actualType = t.String() } - c.log.WithValues("actualType", actualType).Info("wrong JWT authenticator type in cache") + logger.WithValues("actualType", actualType).Info("wrong JWT authenticator type in cache") return nil } return jwtAuthenticator @@ -338,7 +357,7 @@ func (c *jwtCacheFillerController) validateTLSBundle(tlsSpec *authenticationv1al return caBundle, conditions, condition.Status == metav1.ConditionTrue } -func (c *jwtCacheFillerController) validateIssuer(issuer string, conditions []*metav1.Condition) (*url.URL, []*metav1.Condition, bool) { +func (c *jwtCacheFillerController) validateIssuer(issuer string, conditions []*metav1.Condition) ([]*metav1.Condition, bool) { issuerURL, err := url.Parse(issuer) if err != nil { msg := fmt.Sprintf("%s: %s", "spec.issuer URL is invalid", err.Error()) @@ -348,7 +367,7 @@ func (c *jwtCacheFillerController) validateIssuer(issuer string, conditions []*m Reason: conditionsutil.ReasonInvalidIssuerURL, Message: msg, }) - return nil, conditions, false + return conditions, false } if issuerURL.Scheme != "https" { @@ -359,7 +378,7 @@ func (c *jwtCacheFillerController) validateIssuer(issuer string, conditions []*m Reason: reasonInvalidIssuerURLScheme, Message: msg, }) - return nil, conditions, false + return conditions, false } if strings.HasSuffix(issuerURL.Path, "/.well-known/openid-configuration") { @@ -370,7 +389,7 @@ func (c *jwtCacheFillerController) validateIssuer(issuer string, conditions []*m Reason: reasonInvalidIssuerURLContainsWellKnownEndpoint, Message: msg, }) - return nil, conditions, false + return conditions, false } if len(issuerURL.Query()) != 0 { @@ -381,7 +400,7 @@ func (c *jwtCacheFillerController) validateIssuer(issuer string, conditions []*m Reason: reasonInvalidIssuerURLQueryParams, Message: msg, }) - return nil, conditions, false + return conditions, false } if issuerURL.Fragment != "" { @@ -392,7 +411,7 @@ func (c *jwtCacheFillerController) validateIssuer(issuer string, conditions []*m Reason: reasonInvalidIssuerURLFragment, Message: msg, }) - return nil, conditions, false + return conditions, false } conditions = append(conditions, &metav1.Condition{ @@ -401,7 +420,7 @@ func (c *jwtCacheFillerController) validateIssuer(issuer string, conditions []*m Reason: conditionsutil.ReasonSuccess, Message: "issuer is a valid URL", }) - return issuerURL, conditions, true + return conditions, true } func (c *jwtCacheFillerController) validateProviderDiscovery(ctx context.Context, issuer string, conditions []*metav1.Condition, prereqOk bool) (*providerJSON, *coreosoidc.Provider, []*metav1.Condition, error) { @@ -646,7 +665,7 @@ func (c *jwtCacheFillerController) newCachedJWTAuthenticator( }) return &cachedJWTAuthenticator{ Token: oidcAuthenticator, - spec: spec, + issuer: spec.Issuer, caBundleHash: caBundleHash, cancel: cancel, }, conditions, nil diff --git a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller_test.go b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller_test.go index 8c2e16b08..b69c90137 100644 --- a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller_test.go +++ b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller_test.go @@ -1444,6 +1444,10 @@ func TestController(t *testing.T) { "logger": "jwtcachefiller-controller", "message": "wrong JWT authenticator type in cache", "actualType": "*mockcachevalue.MockValue", + "issuer": goodIssuer, + "jwtAuthenticator": map[string]any{ + "name": "test-name", + }, }, { "level": "info", @@ -2914,7 +2918,7 @@ func newCacheValue(t *testing.T, spec authenticationv1alpha1.JWTAuthenticatorSpe }) return &cachedJWTAuthenticator{ - spec: &spec, + issuer: spec.Issuer, caBundleHash: tlsconfigutil.NewCABundleHash([]byte(caBundle)), cancel: func() { wasClosed = true diff --git a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go index 9112ab55d..1856ab96e 100644 --- a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go +++ b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go @@ -60,7 +60,7 @@ const ( type cachedWebhookAuthenticator struct { authenticator.Token - spec *authenticationv1alpha1.WebhookAuthenticatorSpec + endpoint string caBundleHash tlsconfigutil.CABundleHash } @@ -163,13 +163,18 @@ func (c *webhookCacheFillerController) syncIndividualWebhookAuthenticator(ctx co Name: webhookAuthenticator.Name, } - conditions := make([]*metav1.Condition, 0) - caBundle, conditions, tlsBundleOk := c.validateTLSBundle(webhookAuthenticator.Spec.TLS, conditions) - - webhookSpecificLogger := c.log.WithValues( + logger := c.log.WithValues( "webhookAuthenticator", webhookAuthenticator.Name, "endpoint", webhookAuthenticator.Spec.Endpoint) + var errs []error + conditions := make([]*metav1.Condition, 0) + + caBundle, conditions, tlsBundleOk := c.validateTLSBundle(webhookAuthenticator.Spec.TLS, conditions) + + endpointHostPort, conditions, endpointOk := c.validateEndpoint(webhookAuthenticator.Spec.Endpoint, conditions) + okSoFar := tlsBundleOk && endpointOk + // Only revalidate and update the cache if the cached authenticator is different from the desired authenticator. // There is no need to repeat validations for a spec that was already successfully validated. We are making a // design decision to avoid repeating the validation which dials the server, even though the server's TLS @@ -178,24 +183,16 @@ func (c *webhookCacheFillerController) syncIndividualWebhookAuthenticator(ctx co // rather than trying to show the most up-to-date status possible. These validations are for administrator // convenience at the time of a configuration change, to catch typos and blatant misconfigurations, rather // than to constantly monitor for external issues. - var oldWebhookAuthenticatorFromCache *cachedWebhookAuthenticator - if valueFromCache := c.cache.Get(cacheKey); valueFromCache != nil { - oldWebhookAuthenticatorFromCache = c.cacheValueAsWebhookAuthenticator(valueFromCache, webhookSpecificLogger) - if oldWebhookAuthenticatorFromCache != nil && - reflect.DeepEqual(oldWebhookAuthenticatorFromCache.spec, &webhookAuthenticator.Spec) && - tlsBundleOk && // if there was any error while validating the CA bundle, then run remaining validations and update status - oldWebhookAuthenticatorFromCache.caBundleHash.Equal(caBundle.Hash()) { - webhookSpecificLogger.Info("cached webhook authenticator and desired webhook authenticator are the same: already cached, so skipping validations") - // Stop, no more work to be done. This authenticator is already validated and cached. - return nil - } + foundAuthenticatorInCache, alreadyValidatedTheseSettings := c.havePreviouslyValidated( + cacheKey, webhookAuthenticator.Spec.Endpoint, tlsBundleOk, caBundle.Hash(), logger) + if alreadyValidatedTheseSettings { + // Stop, no more work to be done. This authenticator is already validated and cached. + // TODO: Append hardcoded list of remaining success conditions and skip redoing the remaining validations below. + // Make sure that the conditions array is still checked for any errors, and the status and cache are still updated, as below. + return nil } - var errs []error - endpointHostPort, conditions, endpointOk := c.validateEndpoint(webhookAuthenticator.Spec.Endpoint, conditions) - okSoFar := tlsBundleOk && endpointOk - - conditions, tlsNegotiateErr := c.validateConnection(caBundle.CertPool(), endpointHostPort, conditions, okSoFar, webhookSpecificLogger) + conditions, tlsNegotiateErr := c.validateConnection(caBundle.CertPool(), endpointHostPort, conditions, okSoFar, logger) errs = append(errs, tlsNegotiateErr) okSoFar = okSoFar && tlsNegotiateErr == nil @@ -218,11 +215,11 @@ func (c *webhookCacheFillerController) syncIndividualWebhookAuthenticator(ctx co // validated and cached. Do not allow an old, previously validated spec of the authenticator to continue // being used for authentication. c.cache.Delete(cacheKey) - webhookSpecificLogger.Info("invalid webhook authenticator", - "removedFromCache", oldWebhookAuthenticatorFromCache != nil) + logger.Info("invalid webhook authenticator", + "removedFromCache", foundAuthenticatorInCache) } - updateErr := c.updateStatus(ctx, webhookAuthenticator, conditions, webhookSpecificLogger) + updateErr := c.updateStatus(ctx, webhookAuthenticator, conditions, logger) errs = append(errs, updateErr) // Only add this WebhookAuthenticator to the cache if the status update succeeds. @@ -231,11 +228,11 @@ func (c *webhookCacheFillerController) syncIndividualWebhookAuthenticator(ctx co if authenticatorValid && updateErr == nil { c.cache.Store(cacheKey, &cachedWebhookAuthenticator{ Token: newWebhookAuthenticatorForCache, - spec: webhookAuthenticator.Spec.DeepCopy(), // deep copy to avoid caching original object + endpoint: webhookAuthenticator.Spec.Endpoint, caBundleHash: caBundle.Hash(), }) - webhookSpecificLogger.Info("added or updated webhook authenticator in cache", - "isOverwrite", oldWebhookAuthenticatorFromCache != nil) + logger.Info("added or updated webhook authenticator in cache", + "isOverwrite", foundAuthenticatorInCache) } // Sync loop errors: @@ -246,6 +243,31 @@ func (c *webhookCacheFillerController) syncIndividualWebhookAuthenticator(ctx co return utilerrors.NewAggregate(errs) } +func (c *webhookCacheFillerController) havePreviouslyValidated( + cacheKey authncache.Key, + endpoint string, + tlsBundleOk bool, + caBundleHash tlsconfigutil.CABundleHash, + logger plog.Logger, +) (bool, bool) { + var authenticatorFromCache *cachedWebhookAuthenticator + valueFromCache := c.cache.Get(cacheKey) + if valueFromCache == nil { + return false, false + } + authenticatorFromCache = c.cacheValueAsWebhookAuthenticator(valueFromCache, logger) + if authenticatorFromCache == nil { + return false, false + } + if authenticatorFromCache.endpoint == endpoint && + tlsBundleOk && // if there was any error while validating the latest CA bundle, then do not consider it previously validated + authenticatorFromCache.caBundleHash.Equal(caBundleHash) { + logger.Info("cached webhook authenticator and desired webhook authenticator are the same: already cached, so skipping validations") + return true, true + } + return true, false // found the authenticator, but it had not been previously validated with these same settings +} + func (c *webhookCacheFillerController) cacheValueAsWebhookAuthenticator(value authncache.Value, log plog.Logger) *cachedWebhookAuthenticator { webhookAuthenticator, ok := value.(*cachedWebhookAuthenticator) if !ok { diff --git a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go index 9d365f7bc..86fc352ac 100644 --- a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go +++ b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go @@ -768,7 +768,7 @@ func TestController(t *testing.T) { }, }, wantLogLines: []string{ - fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"cached webhook authenticator and desired webhook authenticator are the same: already cached, so skipping validations","webhookAuthenticator":"test-name","endpoint":"%s"}`, goodWebhookDefaultServingCertEndpoint), + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).havePreviouslyValidated","message":"cached webhook authenticator and desired webhook authenticator are the same: already cached, so skipping validations","webhookAuthenticator":"test-name","endpoint":"%s"}`, goodWebhookDefaultServingCertEndpoint), }, wantActions: func() []coretesting.Action { return []coretesting.Action{ @@ -2038,7 +2038,7 @@ func newCacheValue(t *testing.T, spec authenticationv1alpha1.WebhookAuthenticato t.Helper() return &cachedWebhookAuthenticator{ - spec: &spec, + endpoint: spec.Endpoint, caBundleHash: tlsconfigutil.NewCABundleHash([]byte(caBundle)), } } From d6d66faae3143054e808444091eddefab36ebd09 Mon Sep 17 00:00:00 2001 From: Joshua Casey Date: Tue, 30 Jul 2024 22:07:17 -0500 Subject: [PATCH 76/99] jwtcachefiller now tests for exact log lines and prints when it chooses to not update the status --- .../jwtcachefiller/jwtcachefiller.go | 19 +- .../jwtcachefiller/jwtcachefiller_test.go | 539 ++++-------------- 2 files changed, 129 insertions(+), 429 deletions(-) diff --git a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go index e3eedaeeb..e587a3f6d 100644 --- a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go +++ b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go @@ -27,7 +27,6 @@ import ( "k8s.io/apiserver/pkg/authentication/authenticator" "k8s.io/apiserver/plugin/pkg/authenticator/token/oidc" corev1informers "k8s.io/client-go/informers/core/v1" - "k8s.io/klog/v2" "k8s.io/utils/clock" "k8s.io/utils/ptr" @@ -221,7 +220,7 @@ func (c *jwtCacheFillerController) syncIndividualJWTAuthenticator(ctx context.Co } logger := c.log.WithValues( - "jwtAuthenticator", klog.KObj(jwtAuthenticator), + "jwtAuthenticator", jwtAuthenticator.Name, "issuer", jwtAuthenticator.Spec.Issuer) var errs []error @@ -287,7 +286,7 @@ func (c *jwtCacheFillerController) syncIndividualJWTAuthenticator(ctx context.Co "removedFromCache", foundAuthenticatorInCache) } - updateErr := c.updateStatus(ctx, jwtAuthenticator, conditions) + updateErr := c.updateStatus(ctx, jwtAuthenticator, conditions, logger) errs = append(errs, updateErr) // Only add this JWTAuthenticator to the cache if the status update succeeds. @@ -339,7 +338,8 @@ func (c *jwtCacheFillerController) cacheValueAsJWTAuthenticator(value authncache if t := reflect.TypeOf(value); t != nil { actualType = t.String() } - logger.WithValues("actualType", actualType).Info("wrong JWT authenticator type in cache") + logger.Info("wrong JWT authenticator type in cache", + "actualType", actualType) return nil } return jwtAuthenticator @@ -675,6 +675,7 @@ func (c *jwtCacheFillerController) updateStatus( ctx context.Context, original *authenticationv1alpha1.JWTAuthenticator, conditions []*metav1.Condition, + logger plog.Logger, ) error { updated := original.DeepCopy() @@ -696,23 +697,23 @@ func (c *jwtCacheFillerController) updateStatus( }) } - // TODO: this should use c.log.WithValues("jwtAuthenticator", original.Name) - log := plog.New().WithName(controllerName).WithValues("jwtAuthenticator", original.Name) - _ = conditionsutil.MergeConditions( conditions, original.Generation, &updated.Status.Conditions, - log, + logger, metav1.NewTime(c.clock.Now()), ) if equality.Semantic.DeepEqual(original, updated) { + logger.Debug("choosing to not update the jwtauthenticator status since there is no update to make", + "phase", updated.Status.Phase) return nil } _, err := c.client.AuthenticationV1alpha1().JWTAuthenticators().UpdateStatus(ctx, updated, metav1.UpdateOptions{}) if err == nil { - log.Debug("jwtauthenticator status successfully updated") + logger.Debug("jwtauthenticator status successfully updated", + "phase", updated.Status.Phase) } return err } diff --git a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller_test.go b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller_test.go index b69c90137..36c613e51 100644 --- a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller_test.go +++ b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller_test.go @@ -32,7 +32,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/apiserver/pkg/authentication/authenticator" "k8s.io/apiserver/pkg/authentication/user" @@ -716,7 +715,7 @@ func TestController(t *testing.T) { // Since these errors trigger a resync, we are careful only to return an error when // something can be automatically corrected on a retry (ie an error that might be networking). wantSyncErr testutil.RequireErrorStringFunc - wantLogs []map[string]any + wantLogLines []string wantActions func() []coretesting.Action wantUsernameClaim string wantGroupsClaim string @@ -725,13 +724,8 @@ func TestController(t *testing.T) { }{ { name: "Sync: no JWTAuthenticators found results in no errors and no status conditions", - wantLogs: []map[string]any{ - { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "jwtcachefiller-controller", - "message": "No JWTAuthenticators found", - }, + wantLogLines: []string{ + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).Sync","message":"No JWTAuthenticators found"}`, }, wantActions: func() []coretesting.Action { return []coretesting.Action{ @@ -754,17 +748,10 @@ func TestController(t *testing.T) { }, }, }, - wantLogs: []map[string]any{{ - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "jwtcachefiller-controller", - "message": "added or updated jwt authenticator in cache", - "isOverwrite": false, - "issuer": goodIssuer, - "jwtAuthenticator": map[string]any{ - "name": "test-name", - }, - }}, + wantLogLines: []string{ + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).updateStatus","message":"choosing to not update the jwtauthenticator status since there is no update to make","jwtAuthenticator":"test-name","issuer":"%s","phase":"Ready"}`, goodIssuer), + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).syncIndividualJWTAuthenticator","message":"added or updated jwt authenticator in cache","jwtAuthenticator":"test-name","issuer":"%s","isOverwrite":false}`, goodIssuer), + }, wantActions: func() []coretesting.Action { return []coretesting.Action{ coretesting.NewListAction(jwtAuthenticatorsGVR, jwtAUthenticatorGVK, "", metav1.ListOptions{}), @@ -811,51 +798,15 @@ func TestController(t *testing.T) { `error for JWTAuthenticator invalid-jwt-authenticator: could not parse provider jwks_uri: parse "https://.café .com/café/café/café/coffee/jwks.json": invalid character " " in host name` + "]", ), - wantLogs: []map[string]any{ - { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "jwtcachefiller-controller", - "message": "invalid jwt authenticator", - "removedFromCache": false, - "issuer": badIssuerJWKSURIJWTAuthenticatorSpec.Issuer, - "jwtAuthenticator": map[string]any{ - "name": "another-invalid-jwt-authenticator", - }, - }, - { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "jwtcachefiller-controller", - "message": "added or updated jwt authenticator in cache", - "isOverwrite": false, - "issuer": goodIssuer, - "jwtAuthenticator": map[string]any{ - "name": "existing-jwt-authenticator", - }, - }, - { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "jwtcachefiller-controller", - "message": "invalid jwt authenticator", - "removedFromCache": false, - "issuer": badIssuerJWKSURIJWTAuthenticatorSpec.Issuer, - "jwtAuthenticator": map[string]any{ - "name": "invalid-jwt-authenticator", - }, - }, - { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "jwtcachefiller-controller", - "message": "added or updated jwt authenticator in cache", - "isOverwrite": false, - "issuer": goodIssuer, - "jwtAuthenticator": map[string]any{ - "name": "new-jwt-authenticator", - }, - }, + wantLogLines: []string{ + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).syncIndividualJWTAuthenticator","message":"invalid jwt authenticator","jwtAuthenticator":"another-invalid-jwt-authenticator","issuer":"%s","removedFromCache":false}`, badIssuerJWKSURIJWTAuthenticatorSpec.Issuer), + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).updateStatus","message":"jwtauthenticator status successfully updated","jwtAuthenticator":"another-invalid-jwt-authenticator","issuer":"%s","phase":"Error"}`, badIssuerJWKSURIJWTAuthenticatorSpec.Issuer), + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).updateStatus","message":"choosing to not update the jwtauthenticator status since there is no update to make","jwtAuthenticator":"existing-jwt-authenticator","issuer":"%s","phase":"Ready"}`, goodIssuer), + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).syncIndividualJWTAuthenticator","message":"added or updated jwt authenticator in cache","jwtAuthenticator":"existing-jwt-authenticator","issuer":"%s","isOverwrite":false}`, goodIssuer), + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).syncIndividualJWTAuthenticator","message":"invalid jwt authenticator","jwtAuthenticator":"invalid-jwt-authenticator","issuer":"%s","removedFromCache":false}`, badIssuerJWKSURIJWTAuthenticatorSpec.Issuer), + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).updateStatus","message":"jwtauthenticator status successfully updated","jwtAuthenticator":"invalid-jwt-authenticator","issuer":"%s","phase":"Error"}`, badIssuerJWKSURIJWTAuthenticatorSpec.Issuer), + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).updateStatus","message":"jwtauthenticator status successfully updated","jwtAuthenticator":"new-jwt-authenticator","issuer":"%s","phase":"Ready"}`, goodIssuer), + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).syncIndividualJWTAuthenticator","message":"added or updated jwt authenticator in cache","jwtAuthenticator":"new-jwt-authenticator","issuer":"%s","isOverwrite":false}`, goodIssuer), }, wantActions: func() []coretesting.Action { updateValidStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ @@ -950,17 +901,10 @@ func TestController(t *testing.T) { }, }, }, - wantLogs: []map[string]any{{ - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "jwtcachefiller-controller", - "message": "added or updated jwt authenticator in cache", - "isOverwrite": false, - "issuer": goodIssuer, - "jwtAuthenticator": map[string]any{ - "name": "test-name", - }, - }}, + wantLogLines: []string{ + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).updateStatus","message":"jwtauthenticator status successfully updated","jwtAuthenticator":"test-name","issuer":"%s","phase":"Ready"}`, goodIssuer), + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).syncIndividualJWTAuthenticator","message":"added or updated jwt authenticator in cache","jwtAuthenticator":"test-name","issuer":"%s","isOverwrite":false}`, goodIssuer), + }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -998,17 +942,10 @@ func TestController(t *testing.T) { Spec: *someJWTAuthenticatorSpec, }, }, - wantLogs: []map[string]any{{ - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "jwtcachefiller-controller", - "message": "added or updated jwt authenticator in cache", - "isOverwrite": false, - "issuer": goodIssuer, - "jwtAuthenticator": map[string]any{ - "name": "test-name", - }, - }}, + wantLogLines: []string{ + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).updateStatus","message":"jwtauthenticator status successfully updated","jwtAuthenticator":"test-name","issuer":"%s","phase":"Ready"}`, goodIssuer), + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).syncIndividualJWTAuthenticator","message":"added or updated jwt authenticator in cache","jwtAuthenticator":"test-name","issuer":"%s","isOverwrite":false}`, goodIssuer), + }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -1042,17 +979,10 @@ func TestController(t *testing.T) { secretsAndConfigMaps: []runtime.Object{ someSecretWithCA, }, - wantLogs: []map[string]any{{ - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "jwtcachefiller-controller", - "message": "added or updated jwt authenticator in cache", - "isOverwrite": false, - "issuer": goodIssuer, - "jwtAuthenticator": map[string]any{ - "name": "test-name", - }, - }}, + wantLogLines: []string{ + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).updateStatus","message":"jwtauthenticator status successfully updated","jwtAuthenticator":"test-name","issuer":"%s","phase":"Ready"}`, goodIssuer), + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).syncIndividualJWTAuthenticator","message":"added or updated jwt authenticator in cache","jwtAuthenticator":"test-name","issuer":"%s","isOverwrite":false}`, goodIssuer), + }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -1086,17 +1016,10 @@ func TestController(t *testing.T) { secretsAndConfigMaps: []runtime.Object{ someConfigMapWithCA, }, - wantLogs: []map[string]any{{ - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "jwtcachefiller-controller", - "message": "added or updated jwt authenticator in cache", - "isOverwrite": false, - "issuer": goodIssuer, - "jwtAuthenticator": map[string]any{ - "name": "test-name", - }, - }}, + wantLogLines: []string{ + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).updateStatus","message":"jwtauthenticator status successfully updated","jwtAuthenticator":"test-name","issuer":"%s","phase":"Ready"}`, goodIssuer), + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).syncIndividualJWTAuthenticator","message":"added or updated jwt authenticator in cache","jwtAuthenticator":"test-name","issuer":"%s","isOverwrite":false}`, goodIssuer), + }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -1127,17 +1050,10 @@ func TestController(t *testing.T) { Spec: *someJWTAuthenticatorSpecWithUsernameClaim, }, }, - wantLogs: []map[string]any{{ - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "jwtcachefiller-controller", - "message": "added or updated jwt authenticator in cache", - "isOverwrite": false, - "issuer": goodIssuer, - "jwtAuthenticator": map[string]any{ - "name": "test-name", - }, - }}, + wantLogLines: []string{ + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).updateStatus","message":"jwtauthenticator status successfully updated","jwtAuthenticator":"test-name","issuer":"%s","phase":"Ready"}`, goodIssuer), + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).syncIndividualJWTAuthenticator","message":"added or updated jwt authenticator in cache","jwtAuthenticator":"test-name","issuer":"%s","isOverwrite":false}`, goodIssuer), + }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -1169,17 +1085,10 @@ func TestController(t *testing.T) { Spec: *someJWTAuthenticatorSpecWithGroupsClaim, }, }, - wantLogs: []map[string]any{{ - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "jwtcachefiller-controller", - "message": "added or updated jwt authenticator in cache", - "isOverwrite": false, - "issuer": goodIssuer, - "jwtAuthenticator": map[string]any{ - "name": "test-name", - }, - }}, + wantLogLines: []string{ + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).updateStatus","message":"jwtauthenticator status successfully updated","jwtAuthenticator":"test-name","issuer":"%s","phase":"Ready"}`, goodIssuer), + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).syncIndividualJWTAuthenticator","message":"added or updated jwt authenticator in cache","jwtAuthenticator":"test-name","issuer":"%s","isOverwrite":false}`, goodIssuer), + }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -1223,17 +1132,10 @@ func TestController(t *testing.T) { Spec: *someJWTAuthenticatorSpec, }, }, - wantLogs: []map[string]any{{ - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "jwtcachefiller-controller", - "message": "added or updated jwt authenticator in cache", - "isOverwrite": true, - "issuer": goodIssuer, - "jwtAuthenticator": map[string]any{ - "name": "test-name", - }, - }}, + wantLogLines: []string{ + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).updateStatus","message":"jwtauthenticator status successfully updated","jwtAuthenticator":"test-name","issuer":"%s","phase":"Ready"}`, goodIssuer), + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).syncIndividualJWTAuthenticator","message":"added or updated jwt authenticator in cache","jwtAuthenticator":"test-name","issuer":"%s","isOverwrite":true}`, goodIssuer), + }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -1286,7 +1188,7 @@ func TestController(t *testing.T) { Spec: *someJWTAuthenticatorSpec, }, }, - wantLogs: []map[string]any{}, + wantLogLines: nil, // wants no logs wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -1333,17 +1235,10 @@ func TestController(t *testing.T) { secretsAndConfigMaps: []runtime.Object{ someSecretWithCA, }, - wantLogs: []map[string]any{{ - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "jwtcachefiller-controller", - "message": "added or updated jwt authenticator in cache", - "isOverwrite": true, - "issuer": goodIssuer, - "jwtAuthenticator": map[string]any{ - "name": "test-name", - }, - }}, + wantLogLines: []string{ + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).updateStatus","message":"jwtauthenticator status successfully updated","jwtAuthenticator":"test-name","issuer":"%s","phase":"Ready"}`, goodIssuer), + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).syncIndividualJWTAuthenticator","message":"added or updated jwt authenticator in cache","jwtAuthenticator":"test-name","issuer":"%s","isOverwrite":true}`, goodIssuer), + }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -1387,16 +1282,9 @@ func TestController(t *testing.T) { Spec: *someJWTAuthenticatorSpec, }, }, - wantLogs: []map[string]any{{ - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "jwtcachefiller-controller", - "message": "cached jwt authenticator and desired jwt authenticator are the same: already cached, so skipping validations", - "issuer": goodIssuer, - "jwtAuthenticator": map[string]any{ - "name": "test-name", - }, - }}, + wantLogLines: []string{ + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).havePreviouslyValidated","message":"cached jwt authenticator and desired jwt authenticator are the same: already cached, so skipping validations","jwtAuthenticator":"test-name","issuer":"%s"}`, goodIssuer), + }, wantActions: func() []coretesting.Action { return []coretesting.Action{ coretesting.NewListAction(jwtAuthenticatorsGVR, jwtAUthenticatorGVK, "", metav1.ListOptions{}), @@ -1437,29 +1325,10 @@ func TestController(t *testing.T) { Spec: *someJWTAuthenticatorSpec, }, }, - wantLogs: []map[string]any{ - { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "jwtcachefiller-controller", - "message": "wrong JWT authenticator type in cache", - "actualType": "*mockcachevalue.MockValue", - "issuer": goodIssuer, - "jwtAuthenticator": map[string]any{ - "name": "test-name", - }, - }, - { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "jwtcachefiller-controller", - "message": "added or updated jwt authenticator in cache", - "isOverwrite": false, - "issuer": goodIssuer, - "jwtAuthenticator": map[string]any{ - "name": "test-name", - }, - }, + wantLogLines: []string{ + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).cacheValueAsJWTAuthenticator","message":"wrong JWT authenticator type in cache","jwtAuthenticator":"test-name","issuer":"%s","actualType":"*mockcachevalue.MockValue"}`, goodIssuer), + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).updateStatus","message":"jwtauthenticator status successfully updated","jwtAuthenticator":"test-name","issuer":"%s","phase":"Ready"}`, goodIssuer), + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).syncIndividualJWTAuthenticator","message":"added or updated jwt authenticator in cache","jwtAuthenticator":"test-name","issuer":"%s","isOverwrite":false}`, goodIssuer), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ @@ -1491,18 +1360,9 @@ func TestController(t *testing.T) { Spec: *missingTLSJWTAuthenticatorSpec, }, }, - wantLogs: []map[string]any{ - { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "jwtcachefiller-controller", - "message": "invalid jwt authenticator", - "removedFromCache": false, - "issuer": missingTLSJWTAuthenticatorSpec.Issuer, - "jwtAuthenticator": map[string]any{ - "name": "test-name", - }, - }, + wantLogLines: []string{ + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).syncIndividualJWTAuthenticator","message":"invalid jwt authenticator","jwtAuthenticator":"test-name","issuer":"%s","removedFromCache":false}`, missingTLSJWTAuthenticatorSpec.Issuer), + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).updateStatus","message":"jwtauthenticator status successfully updated","jwtAuthenticator":"test-name","issuer":"%s","phase":"Error"}`, missingTLSJWTAuthenticatorSpec.Issuer), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ @@ -1546,18 +1406,9 @@ func TestController(t *testing.T) { Spec: *invalidTLSJWTAuthenticatorSpec, }, }, - wantLogs: []map[string]any{ - { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "jwtcachefiller-controller", - "message": "invalid jwt authenticator", - "removedFromCache": false, - "issuer": invalidTLSJWTAuthenticatorSpec.Issuer, - "jwtAuthenticator": map[string]any{ - "name": "test-name", - }, - }, + wantLogLines: []string{ + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).syncIndividualJWTAuthenticator","message":"invalid jwt authenticator","jwtAuthenticator":"test-name","issuer":"%s","removedFromCache":false}`, invalidTLSJWTAuthenticatorSpec.Issuer), + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).updateStatus","message":"jwtauthenticator status successfully updated","jwtAuthenticator":"test-name","issuer":"%s","phase":"Error"}`, invalidTLSJWTAuthenticatorSpec.Issuer), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ @@ -1612,18 +1463,9 @@ func TestController(t *testing.T) { Spec: *invalidTLSJWTAuthenticatorSpec, }, }, - wantLogs: []map[string]any{ - { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "jwtcachefiller-controller", - "message": "invalid jwt authenticator", - "removedFromCache": true, - "issuer": invalidTLSJWTAuthenticatorSpec.Issuer, - "jwtAuthenticator": map[string]any{ - "name": "test-name", - }, - }, + wantLogLines: []string{ + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).syncIndividualJWTAuthenticator","message":"invalid jwt authenticator","jwtAuthenticator":"test-name","issuer":"%s","removedFromCache":true}`, invalidTLSJWTAuthenticatorSpec.Issuer), + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).updateStatus","message":"jwtauthenticator status successfully updated","jwtAuthenticator":"test-name","issuer":"%s","phase":"Error"}`, invalidTLSJWTAuthenticatorSpec.Issuer), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ @@ -1677,18 +1519,9 @@ func TestController(t *testing.T) { Spec: *invalidIssuerJWTAuthenticatorSpec, }, }, - wantLogs: []map[string]any{ - { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "jwtcachefiller-controller", - "message": "invalid jwt authenticator", - "removedFromCache": true, - "issuer": invalidIssuerJWTAuthenticatorSpec.Issuer, - "jwtAuthenticator": map[string]any{ - "name": "test-name", - }, - }, + wantLogLines: []string{ + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).syncIndividualJWTAuthenticator","message":"invalid jwt authenticator","jwtAuthenticator":"test-name","issuer":"%s","removedFromCache":true}`, invalidIssuerJWTAuthenticatorSpec.Issuer), + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).updateStatus","message":"jwtauthenticator status successfully updated","jwtAuthenticator":"test-name","issuer":"%s","phase":"Error"}`, invalidIssuerJWTAuthenticatorSpec.Issuer), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ @@ -1731,18 +1564,9 @@ func TestController(t *testing.T) { Spec: *invalidIssuerJWTAuthenticatorSpec, }, }, - wantLogs: []map[string]any{ - { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "jwtcachefiller-controller", - "message": "invalid jwt authenticator", - "removedFromCache": false, - "issuer": invalidIssuerJWTAuthenticatorSpec.Issuer, - "jwtAuthenticator": map[string]any{ - "name": "test-name", - }, - }, + wantLogLines: []string{ + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).syncIndividualJWTAuthenticator","message":"invalid jwt authenticator","jwtAuthenticator":"test-name","issuer":"%s","removedFromCache":false}`, invalidIssuerJWTAuthenticatorSpec.Issuer), + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).updateStatus","message":"jwtauthenticator status successfully updated","jwtAuthenticator":"test-name","issuer":"%s","phase":"Error"}`, invalidIssuerJWTAuthenticatorSpec.Issuer), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ @@ -1783,18 +1607,9 @@ func TestController(t *testing.T) { Spec: *invalidIssuerSchemeJWTAuthenticatorSpec, }, }, - wantLogs: []map[string]any{ - { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "jwtcachefiller-controller", - "message": "invalid jwt authenticator", - "removedFromCache": false, - "issuer": invalidIssuerSchemeJWTAuthenticatorSpec.Issuer, - "jwtAuthenticator": map[string]any{ - "name": "test-name", - }, - }, + wantLogLines: []string{ + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).syncIndividualJWTAuthenticator","message":"invalid jwt authenticator","jwtAuthenticator":"test-name","issuer":"%s","removedFromCache":false}`, invalidIssuerSchemeJWTAuthenticatorSpec.Issuer), + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).updateStatus","message":"jwtauthenticator status successfully updated","jwtAuthenticator":"test-name","issuer":"%s","phase":"Error"}`, invalidIssuerSchemeJWTAuthenticatorSpec.Issuer), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ @@ -1839,18 +1654,9 @@ func TestController(t *testing.T) { }, }, }, - wantLogs: []map[string]any{ - { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "jwtcachefiller-controller", - "message": "invalid jwt authenticator", - "removedFromCache": false, - "issuer": "https://www.example.com/foo/bar/#do-not-include-fragment", - "jwtAuthenticator": map[string]any{ - "name": "test-name", - }, - }, + wantLogLines: []string{ + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).syncIndividualJWTAuthenticator","message":"invalid jwt authenticator","jwtAuthenticator":"test-name","issuer":"https://www.example.com/foo/bar/#do-not-include-fragment","removedFromCache":false}`, + `{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).updateStatus","message":"jwtauthenticator status successfully updated","jwtAuthenticator":"test-name","issuer":"https://www.example.com/foo/bar/#do-not-include-fragment","phase":"Error"}`, }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ @@ -1899,18 +1705,9 @@ func TestController(t *testing.T) { }, }, }, - wantLogs: []map[string]any{ - { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "jwtcachefiller-controller", - "message": "invalid jwt authenticator", - "removedFromCache": false, - "issuer": "https://www.example.com/foo/bar/?query-params=not-allowed", - "jwtAuthenticator": map[string]any{ - "name": "test-name", - }, - }, + wantLogLines: []string{ + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).syncIndividualJWTAuthenticator","message":"invalid jwt authenticator","jwtAuthenticator":"test-name","issuer":"https://www.example.com/foo/bar/?query-params=not-allowed","removedFromCache":false}`, + `{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).updateStatus","message":"jwtauthenticator status successfully updated","jwtAuthenticator":"test-name","issuer":"https://www.example.com/foo/bar/?query-params=not-allowed","phase":"Error"}`, }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ @@ -1959,18 +1756,9 @@ func TestController(t *testing.T) { }, }, }, - wantLogs: []map[string]any{ - { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "jwtcachefiller-controller", - "message": "invalid jwt authenticator", - "removedFromCache": false, - "issuer": "https://www.example.com/foo/bar/.well-known/openid-configuration", - "jwtAuthenticator": map[string]any{ - "name": "test-name", - }, - }, + wantLogLines: []string{ + `{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).syncIndividualJWTAuthenticator","message":"invalid jwt authenticator","jwtAuthenticator":"test-name","issuer":"https://www.example.com/foo/bar/.well-known/openid-configuration","removedFromCache":false}`, + `{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).updateStatus","message":"jwtauthenticator status successfully updated","jwtAuthenticator":"test-name","issuer":"https://www.example.com/foo/bar/.well-known/openid-configuration","phase":"Error"}`, }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ @@ -2015,18 +1803,9 @@ func TestController(t *testing.T) { Spec: *validIssuerURLButDoesNotExistJWTAuthenticatorSpec, }, }, - wantLogs: []map[string]any{ - { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "jwtcachefiller-controller", - "message": "invalid jwt authenticator", - "removedFromCache": false, - "issuer": validIssuerURLButDoesNotExistJWTAuthenticatorSpec.Issuer, - "jwtAuthenticator": map[string]any{ - "name": "test-name", - }, - }, + wantLogLines: []string{ + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).syncIndividualJWTAuthenticator","message":"invalid jwt authenticator","jwtAuthenticator":"test-name","issuer":"%s","removedFromCache":false}`, validIssuerURLButDoesNotExistJWTAuthenticatorSpec.Issuer), + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).updateStatus","message":"jwtauthenticator status successfully updated","jwtAuthenticator":"test-name","issuer":"%s","phase":"Error"}`, validIssuerURLButDoesNotExistJWTAuthenticatorSpec.Issuer), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ @@ -2073,18 +1852,9 @@ func TestController(t *testing.T) { }, }, }, - wantLogs: []map[string]any{ - { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "jwtcachefiller-controller", - "message": "invalid jwt authenticator", - "removedFromCache": false, - "issuer": goodIssuer + "/path/to/not/found", - "jwtAuthenticator": map[string]any{ - "name": "test-name", - }, - }, + wantLogLines: []string{ + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).syncIndividualJWTAuthenticator","message":"invalid jwt authenticator","jwtAuthenticator":"test-name","issuer":"%s","removedFromCache":false}`, goodIssuer+"/path/to/not/found"), + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).updateStatus","message":"jwtauthenticator status successfully updated","jwtAuthenticator":"test-name","issuer":"%s","phase":"Error"}`, goodIssuer+"/path/to/not/found"), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ @@ -2136,18 +1906,9 @@ func TestController(t *testing.T) { Spec: *badIssuerJWKSURIJWTAuthenticatorSpec, }, }, - wantLogs: []map[string]any{ - { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "jwtcachefiller-controller", - "message": "invalid jwt authenticator", - "removedFromCache": false, - "issuer": badIssuerJWKSURIJWTAuthenticatorSpec.Issuer, - "jwtAuthenticator": map[string]any{ - "name": "test-name", - }, - }, + wantLogLines: []string{ + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).syncIndividualJWTAuthenticator","message":"invalid jwt authenticator","jwtAuthenticator":"test-name","issuer":"%s","removedFromCache":false}`, badIssuerJWKSURIJWTAuthenticatorSpec.Issuer), + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).updateStatus","message":"jwtauthenticator status successfully updated","jwtAuthenticator":"test-name","issuer":"%s","phase":"Error"}`, badIssuerJWKSURIJWTAuthenticatorSpec.Issuer), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ @@ -2188,18 +1949,9 @@ func TestController(t *testing.T) { Spec: *badIssuerJWKSURISchemeJWTAuthenticatorSpec, }, }, - wantLogs: []map[string]any{ - { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "jwtcachefiller-controller", - "message": "invalid jwt authenticator", - "removedFromCache": false, - "issuer": badIssuerJWKSURISchemeJWTAuthenticatorSpec.Issuer, - "jwtAuthenticator": map[string]any{ - "name": "test-name", - }, - }, + wantLogLines: []string{ + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).syncIndividualJWTAuthenticator","message":"invalid jwt authenticator","jwtAuthenticator":"test-name","issuer":"%s","removedFromCache":false}`, badIssuerJWKSURISchemeJWTAuthenticatorSpec.Issuer), + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).updateStatus","message":"jwtauthenticator status successfully updated","jwtAuthenticator":"test-name","issuer":"%s","phase":"Error"}`, badIssuerJWKSURISchemeJWTAuthenticatorSpec.Issuer), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ @@ -2240,18 +1992,9 @@ func TestController(t *testing.T) { Spec: *badIssuerUsesOurHardcodedPrivateKeyJWTAuthenticatorSpec, }, }, - wantLogs: []map[string]any{ - { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "jwtcachefiller-controller", - "message": "invalid jwt authenticator", - "removedFromCache": false, - "issuer": badIssuerUsesOurHardcodedPrivateKeyJWTAuthenticatorSpec.Issuer, - "jwtAuthenticator": map[string]any{ - "name": "test-name", - }, - }, + wantLogLines: []string{ + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).syncIndividualJWTAuthenticator","message":"invalid jwt authenticator","jwtAuthenticator":"test-name","issuer":"%s","removedFromCache":false}`, badIssuerUsesOurHardcodedPrivateKeyJWTAuthenticatorSpec.Issuer), + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).updateStatus","message":"jwtauthenticator status successfully updated","jwtAuthenticator":"test-name","issuer":"%s","phase":"Error"}`, badIssuerUsesOurHardcodedPrivateKeyJWTAuthenticatorSpec.Issuer), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ @@ -2293,18 +2036,9 @@ func TestController(t *testing.T) { Spec: *jwksFetchShouldFailJWTAuthenticatorSpec, }, }, - wantLogs: []map[string]any{ - { - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "jwtcachefiller-controller", - "message": "invalid jwt authenticator", - "removedFromCache": false, - "issuer": jwksFetchShouldFailJWTAuthenticatorSpec.Issuer, - "jwtAuthenticator": map[string]any{ - "name": "test-name", - }, - }, + wantLogLines: []string{ + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).syncIndividualJWTAuthenticator","message":"invalid jwt authenticator","jwtAuthenticator":"test-name","issuer":"%s","removedFromCache":false}`, jwksFetchShouldFailJWTAuthenticatorSpec.Issuer), + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).updateStatus","message":"jwtauthenticator status successfully updated","jwtAuthenticator":"test-name","issuer":"%s","phase":"Error"}`, jwksFetchShouldFailJWTAuthenticatorSpec.Issuer), }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ @@ -2349,17 +2083,10 @@ func TestController(t *testing.T) { }, }, }, - wantLogs: []map[string]any{{ - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "jwtcachefiller-controller", - "message": "added or updated jwt authenticator in cache", - "isOverwrite": false, - "issuer": goodIssuer, - "jwtAuthenticator": map[string]any{ - "name": "test-name", - }, - }}, + wantLogLines: []string{ + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).updateStatus","message":"choosing to not update the jwtauthenticator status since there is no update to make","jwtAuthenticator":"test-name","issuer":"%s","phase":"Ready"}`, goodIssuer), + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).syncIndividualJWTAuthenticator","message":"added or updated jwt authenticator in cache","jwtAuthenticator":"test-name","issuer":"%s","isOverwrite":false}`, goodIssuer), + }, wantActions: func() []coretesting.Action { return []coretesting.Action{ coretesting.NewListAction(jwtAuthenticatorsGVR, jwtAUthenticatorGVK, "", metav1.ListOptions{}), @@ -2387,17 +2114,10 @@ func TestController(t *testing.T) { }, }, }, - wantLogs: []map[string]any{{ - "level": "info", - "timestamp": "2099-08-08T13:57:36.123456Z", - "logger": "jwtcachefiller-controller", - "message": "added or updated jwt authenticator in cache", - "isOverwrite": false, - "issuer": goodIssuer, - "jwtAuthenticator": map[string]any{ - "name": "test-name", - }, - }}, + wantLogLines: []string{ + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).updateStatus","message":"jwtauthenticator status successfully updated","jwtAuthenticator":"test-name","issuer":"%s","phase":"Ready"}`, goodIssuer), + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).syncIndividualJWTAuthenticator","message":"added or updated jwt authenticator in cache","jwtAuthenticator":"test-name","issuer":"%s","isOverwrite":false}`, goodIssuer), + }, wantActions: func() []coretesting.Action { updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ ObjectMeta: metav1.ObjectMeta{ @@ -2458,7 +2178,7 @@ func TestController(t *testing.T) { updateStatusAction, } }, - wantLogs: []map[string]any{}, + wantLogLines: nil, // wants no logs wantSyncErr: testutil.WantExactErrorString("error for JWTAuthenticator test-name: some update error"), wantNamesOfJWTAuthenticatorsInCache: []string{}, // even though the authenticator was valid, do not cache it because the status update failed }, @@ -2523,38 +2243,17 @@ func TestController(t *testing.T) { require.Equal(t, len(tt.wantNamesOfJWTAuthenticatorsInCache), len(cache.Keys()), fmt.Sprintf("expected cache entries is incorrect. wanted:%d, got: %d, keys: %v", len(tt.wantNamesOfJWTAuthenticatorsInCache), len(cache.Keys()), cache.Keys())) - wantLogsAsJSON, err := json.Marshal(tt.wantLogs) - require.NoError(t, err) - - actualLogLines := testutil.SplitByNewline(log.String()) - require.Equalf(t, len(tt.wantLogs), len(actualLogLines), - "log line count should be correct\nactual: %s\nwant: %s", actualLogLines, wantLogsAsJSON) - - for actualLogLineNum, actualLogLine := range actualLogLines { - wantLine := tt.wantLogs[actualLogLineNum] - require.NotNil(t, wantLine, "expected log line should never be empty") - - var actualParsedLine map[string]any - err := json.Unmarshal([]byte(actualLogLine), &actualParsedLine) - require.NoError(t, err) - - wantLineAsJSON, err := json.Marshal(wantLine) - require.NoError(t, err) - wantLine["caller"] = "we don't want to actually make equality comparisons about this" - require.Lenf(t, actualParsedLine, len(wantLine), "actual: %s\nwant: %s", actualLogLine, string(wantLineAsJSON)) - require.Equal(t, sets.StringKeySet(actualParsedLine), sets.StringKeySet(wantLine)) - - for k := range actualParsedLine { - if k == "caller" { - require.NotEmpty(t, actualParsedLine["caller"]) - } else { - require.Equal(t, wantLine[k], actualParsedLine[k], - fmt.Sprintf("log line (%d) key %q was not equal\nactual: %s\nwant: %s", - actualLogLineNum, k, actualParsedLine[k], wantLine[k])) - } + actualLog, _ := strings.CutSuffix(log.String(), "\n") + actualLogLines := strings.Split(actualLog, "\n") + var jwtLogLines []string + for _, line := range actualLogLines { + if strings.Contains(line, "jwtcachefiller/jwtcachefiller.go") { + jwtLogLines = append(jwtLogLines, line) } } + require.Equal(t, tt.wantLogLines, jwtLogLines) + for _, name := range tt.wantNamesOfJWTAuthenticatorsInCache { // We expected the cache to have an entry, so pull that entry from the cache and test it. expectedCacheKey := authncache.Key{ @@ -2795,7 +2494,7 @@ func testTableForAuthenticateTokenTests( jwtClaims: func(_ *josejwt.Claims, groups *any, username *string) { *groups = map[string]string{"not an array": "or a string"} }, - wantErr: testutil.WantMatchingErrorString("oidc: parse groups claim \"" + expectedGroupsClaim + "\": json: cannot unmarshal object into Go value of type string"), + wantErr: testutil.WantMatchingErrorString(`oidc: parse groups claim "` + expectedGroupsClaim + `": json: cannot unmarshal object into Go value of type string`), }, { name: "bad token with wrong issuer", From a0c259ffbcd86d8de1da15ca0ea5642b7d19d9b9 Mon Sep 17 00:00:00 2001 From: Ashish Amarnath Date: Wed, 31 Jul 2024 11:04:20 -0700 Subject: [PATCH 77/99] update expectation conditions message when CA bundle is not configured fix a typo where we intended to use a configmap instead of a secret Signed-off-by: Ashish Amarnath Co-authored-by: Ryan Richard --- test/integration/supervisor_login_test.go | 68 +++++++++++++++----- test/integration/supervisor_upstream_test.go | 39 +++++------ 2 files changed, 74 insertions(+), 33 deletions(-) diff --git a/test/integration/supervisor_login_test.go b/test/integration/supervisor_login_test.go index d6482e36c..4c6c662e2 100644 --- a/test/integration/supervisor_login_test.go +++ b/test/integration/supervisor_login_test.go @@ -244,7 +244,7 @@ func TestSupervisorLogin_Browser(t *testing.T) { spec.Host, env.SupervisorUpstreamActiveDirectory.BindUsername, secret.Name, secret.ResourceVersion, ) - requireSuccessfulActiveDirectoryIdentityProviderConditions(t, adIDP, expectedMsg) + requireSuccessfulActiveDirectoryIdentityProviderConditions(t, adIDP, expectedMsg, env.SupervisorUpstreamActiveDirectory.CABundle != "") return adIDP, secret } @@ -297,7 +297,7 @@ func TestSupervisorLogin_Browser(t *testing.T) { spec.Host, env.SupervisorUpstreamLDAP.BindUsername, secret.Name, secret.ResourceVersion, ) - requireSuccessfulLDAPIdentityProviderConditions(t, ldapIDP, expectedMsg) + requireSuccessfulLDAPIdentityProviderConditions(t, ldapIDP, expectedMsg, len(env.SupervisorUpstreamLDAP.CABundle) != 0) return ldapIDP, secret } @@ -1135,7 +1135,7 @@ func TestSupervisorLogin_Browser(t *testing.T) { defer cancel() idp, err = supervisorClient.IDPV1alpha1().LDAPIdentityProviders(env.SupervisorNamespace).Get(ctx, idp.Name, metav1.GetOptions{}) requireEventually.NoError(err) - requireEventuallySuccessfulLDAPIdentityProviderConditions(t, requireEventually, idp, expectedMsg) + requireEventuallySuccessfulLDAPIdentityProviderConditions(t, requireEventually, idp, expectedMsg, len(env.SupervisorUpstreamLDAP.CABundle) != 0) }, time.Minute, 500*time.Millisecond) return idp.Name }, @@ -1201,7 +1201,7 @@ func TestSupervisorLogin_Browser(t *testing.T) { defer cancel() idp, err = supervisorClient.IDPV1alpha1().LDAPIdentityProviders(env.SupervisorNamespace).Get(ctx, idp.Name, metav1.GetOptions{}) requireEventually.NoError(err) - requireEventuallySuccessfulLDAPIdentityProviderConditions(t, requireEventually, idp, expectedMsg) + requireEventuallySuccessfulLDAPIdentityProviderConditions(t, requireEventually, idp, expectedMsg, len(env.SupervisorUpstreamLDAP.CABundle) != 0) }, time.Minute, 500*time.Millisecond) return idp.Name }, @@ -1349,7 +1349,7 @@ func TestSupervisorLogin_Browser(t *testing.T) { }) spec.TLS.CertificateAuthorityData = "" spec.TLS.CertificateAuthorityDataSource = &idpv1alpha1.CABundleSource{ - Kind: "Secret", + Kind: "ConfigMap", Name: caConfigMap.Name, Key: "ca.crt", } @@ -1492,7 +1492,7 @@ func TestSupervisorLogin_Browser(t *testing.T) { defer cancel() idp, err = supervisorClient.IDPV1alpha1().ActiveDirectoryIdentityProviders(env.SupervisorNamespace).Get(ctx, idp.Name, metav1.GetOptions{}) requireEventually.NoError(err) - requireEventuallySuccessfulActiveDirectoryIdentityProviderConditions(t, requireEventually, idp, expectedMsg) + requireEventuallySuccessfulActiveDirectoryIdentityProviderConditions(t, requireEventually, idp, expectedMsg, len(env.SupervisorUpstreamActiveDirectory.CABundle) != 0) }, time.Minute, 500*time.Millisecond) return idp.Name }, @@ -1559,7 +1559,7 @@ func TestSupervisorLogin_Browser(t *testing.T) { defer cancel() idp, err = supervisorClient.IDPV1alpha1().ActiveDirectoryIdentityProviders(env.SupervisorNamespace).Get(ctx, idp.Name, metav1.GetOptions{}) requireEventually.NoError(err) - requireEventuallySuccessfulActiveDirectoryIdentityProviderConditions(t, requireEventually, idp, expectedMsg) + requireEventuallySuccessfulActiveDirectoryIdentityProviderConditions(t, requireEventually, idp, expectedMsg, len(env.SupervisorUpstreamActiveDirectory.CABundle) != 0) }, time.Minute, 500*time.Millisecond) return idp.Name }, @@ -2812,7 +2812,12 @@ func wantGroupsInAdditionalClaimsIfGroupsExist(additionalClaims map[string]any, return additionalClaims } -func requireSuccessfulLDAPIdentityProviderConditions(t *testing.T, ldapIDP *idpv1alpha1.LDAPIdentityProvider, expectedLDAPConnectionValidMessage string) { +func requireSuccessfulLDAPIdentityProviderConditions( + t *testing.T, + ldapIDP *idpv1alpha1.LDAPIdentityProvider, + expectedLDAPConnectionValidMessage string, + caBundleConfigured bool, +) { require.Len(t, ldapIDP.Status.Conditions, 3) conditionsSummary := [][]string{} @@ -2824,7 +2829,11 @@ func requireSuccessfulLDAPIdentityProviderConditions(t *testing.T, ldapIDP *idpv case "BindSecretValid": require.Equal(t, "loaded bind secret", condition.Message) case "TLSConfigurationValid": - require.Equal(t, "spec.tls is valid: using configured CA bundle", condition.Message) + if caBundleConfigured { + require.Equal(t, "spec.tls is valid: using configured CA bundle", condition.Message) + } else { + require.Equal(t, "spec.tls is valid: no TLS configuration provided: using default root CA bundle from container image", condition.Message) + } case "LDAPConnectionValid": require.Equal(t, expectedLDAPConnectionValidMessage, condition.Message) } @@ -2837,7 +2846,12 @@ func requireSuccessfulLDAPIdentityProviderConditions(t *testing.T, ldapIDP *idpv }, conditionsSummary) } -func requireSuccessfulActiveDirectoryIdentityProviderConditions(t *testing.T, adIDP *idpv1alpha1.ActiveDirectoryIdentityProvider, expectedActiveDirectoryConnectionValidMessage string) { +func requireSuccessfulActiveDirectoryIdentityProviderConditions( + t *testing.T, + adIDP *idpv1alpha1.ActiveDirectoryIdentityProvider, + expectedActiveDirectoryConnectionValidMessage string, + caBundleConfigured bool, +) { require.Len(t, adIDP.Status.Conditions, 4) conditionsSummary := [][]string{} @@ -2849,7 +2863,11 @@ func requireSuccessfulActiveDirectoryIdentityProviderConditions(t *testing.T, ad case "BindSecretValid": require.Equal(t, "loaded bind secret", condition.Message) case "TLSConfigurationValid": - require.Equal(t, "spec.tls is valid: using configured CA bundle", condition.Message) + if caBundleConfigured { + require.Equal(t, "spec.tls is valid: using configured CA bundle", condition.Message) + } else { + require.Equal(t, "spec.tls is valid: no TLS configuration provided: using default root CA bundle from container image", condition.Message) + } case "LDAPConnectionValid": require.Equal(t, expectedActiveDirectoryConnectionValidMessage, condition.Message) } @@ -2870,7 +2888,13 @@ func requireSuccessfulActiveDirectoryIdentityProviderConditions(t *testing.T, ad }, conditionsSummary) } -func requireEventuallySuccessfulLDAPIdentityProviderConditions(t *testing.T, requireEventually *require.Assertions, ldapIDP *idpv1alpha1.LDAPIdentityProvider, expectedLDAPConnectionValidMessage string) { +func requireEventuallySuccessfulLDAPIdentityProviderConditions( + t *testing.T, + requireEventually *require.Assertions, + ldapIDP *idpv1alpha1.LDAPIdentityProvider, + expectedLDAPConnectionValidMessage string, + caBundleConfigured bool, +) { t.Helper() requireEventually.Len(ldapIDP.Status.Conditions, 3) @@ -2883,7 +2907,11 @@ func requireEventuallySuccessfulLDAPIdentityProviderConditions(t *testing.T, req case "BindSecretValid": requireEventually.Equal("loaded bind secret", condition.Message) case "TLSConfigurationValid": - requireEventually.Equal("spec.tls is valid: using configured CA bundle", condition.Message) + if caBundleConfigured { + require.Equal(t, "spec.tls is valid: using configured CA bundle", condition.Message) + } else { + require.Equal(t, "spec.tls is valid: no TLS configuration provided: using default root CA bundle from container image", condition.Message) + } case "LDAPConnectionValid": requireEventually.Equal(expectedLDAPConnectionValidMessage, condition.Message) } @@ -2896,7 +2924,13 @@ func requireEventuallySuccessfulLDAPIdentityProviderConditions(t *testing.T, req }, conditionsSummary) } -func requireEventuallySuccessfulActiveDirectoryIdentityProviderConditions(t *testing.T, requireEventually *require.Assertions, adIDP *idpv1alpha1.ActiveDirectoryIdentityProvider, expectedActiveDirectoryConnectionValidMessage string) { +func requireEventuallySuccessfulActiveDirectoryIdentityProviderConditions( + t *testing.T, + requireEventually *require.Assertions, + adIDP *idpv1alpha1.ActiveDirectoryIdentityProvider, + expectedActiveDirectoryConnectionValidMessage string, + caBundleConfigured bool, +) { t.Helper() requireEventually.Len(adIDP.Status.Conditions, 4) @@ -2909,7 +2943,11 @@ func requireEventuallySuccessfulActiveDirectoryIdentityProviderConditions(t *tes case "BindSecretValid": requireEventually.Equal("loaded bind secret", condition.Message) case "TLSConfigurationValid": - requireEventually.Equal("spec.tls is valid: using configured CA bundle", condition.Message) + if caBundleConfigured { + require.Equal(t, "spec.tls is valid: using configured CA bundle", condition.Message) + } else { + require.Equal(t, "spec.tls is valid: no TLS configuration provided: using default root CA bundle from container image", condition.Message) + } case "LDAPConnectionValid": requireEventually.Equal(expectedActiveDirectoryConnectionValidMessage, condition.Message) } diff --git a/test/integration/supervisor_upstream_test.go b/test/integration/supervisor_upstream_test.go index 69f66f198..876839f07 100644 --- a/test/integration/supervisor_upstream_test.go +++ b/test/integration/supervisor_upstream_test.go @@ -46,12 +46,7 @@ Get "https://127.0.0.1:444444/invalid-url-that-is-really-really-long-nananananan Reason: "Success", Message: "additionalAuthorizeParameters parameter names are allowed", }, - { - Type: "TLSConfigurationValid", - Status: "True", - Reason: "Success", - Message: `spec.tls is valid: no TLS configuration provided: using default root CA bundle from container image`, - }, + expectedTLSConfigValidCondition(false), // we are not configuring a CA bundle on the OIDCIdentityProvider in this test }) }) @@ -90,12 +85,7 @@ oidc: issuer did not match the issuer returned by provider, expected "` + env.Su Reason: "Success", Message: "additionalAuthorizeParameters parameter names are allowed", }, - { - Type: "TLSConfigurationValid", - Status: "True", - Reason: "Success", - Message: `spec.tls is valid: using configured CA bundle`, - }, + expectedTLSConfigValidCondition(env.SupervisorUpstreamOIDC.CABundle != ""), }) }) @@ -133,12 +123,7 @@ oidc: issuer did not match the issuer returned by provider, expected "` + env.Su Reason: "Success", Message: "additionalAuthorizeParameters parameter names are allowed", }, - { - Type: "TLSConfigurationValid", - Status: "True", - Reason: "Success", - Message: `spec.tls is valid: using configured CA bundle`, - }, + expectedTLSConfigValidCondition(env.SupervisorUpstreamOIDC.CABundle != ""), }) }) } @@ -153,3 +138,21 @@ func expectUpstreamConditions(t *testing.T, upstream *idpv1alpha1.OIDCIdentityPr } require.ElementsMatch(t, expected, normalized) } + +func expectedTLSConfigValidCondition(caBundleConfigured bool) metav1.Condition { + if caBundleConfigured { + return metav1.Condition{ + Type: "TLSConfigurationValid", + Status: "True", + Reason: "Success", + Message: `spec.tls is valid: using configured CA bundle`, + } + } + + return metav1.Condition{ + Type: "TLSConfigurationValid", + Status: "True", + Reason: "Success", + Message: `spec.tls is valid: no TLS configuration provided: using default root CA bundle from container image`, + } +} From ed502949dd7e0b9871381d9d3a4e16d5bf83653e Mon Sep 17 00:00:00 2001 From: Ryan Richard Date: Wed, 31 Jul 2024 11:45:27 -0700 Subject: [PATCH 78/99] webhookcachefiller and jwtcachefiller always update status when needed Even when the authenticator is found in the cache, try to update its status. Failing to do so would mean that the actual status will not be overwritten by the controller's newly computed desired status. Co-authored-by: Ashish Amarnath --- .../jwtcachefiller/jwtcachefiller.go | 174 +++++++++++------- .../jwtcachefiller/jwtcachefiller_test.go | 74 +++++++- .../webhookcachefiller/webhookcachefiller.go | 133 ++++++++----- .../webhookcachefiller_test.go | 61 ++++++ .../concierge_jwtauthenticator_status_test.go | 42 +++++ ...cierge_webhookauthenticator_status_test.go | 36 ++++ 6 files changed, 399 insertions(+), 121 deletions(-) diff --git a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go index e587a3f6d..5d11b83bb 100644 --- a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go +++ b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go @@ -225,6 +225,7 @@ func (c *jwtCacheFillerController) syncIndividualJWTAuthenticator(ctx context.Co var errs []error conditions := make([]*metav1.Condition, 0) + var newJWTAuthenticatorForCache *cachedJWTAuthenticator caBundle, conditions, tlsBundleOk := c.validateTLSBundle(jwtAuthenticator.Spec.TLS, conditions) @@ -232,22 +233,75 @@ func (c *jwtCacheFillerController) syncIndividualJWTAuthenticator(ctx context.Co okSoFar := tlsBundleOk && issuerOk // Only revalidate and update the cache if the cached authenticator is different from the desired authenticator. - // There is no need to repeat validations for a spec that was already successfully validated. We are making a - // design decision to avoid repeating the validation which dials the server, even though the server's TLS - // configuration could have changed, because it is also possible that the network could be flaky. We are choosing - // to prefer to keep the authenticator cached (available for end-user auth attempts) during times of network flakes - // rather than trying to show the most up-to-date status possible. These validations are for administrator - // convenience at the time of a configuration change, to catch typos and blatant misconfigurations, rather - // than to constantly monitor for external issues. - foundAuthenticatorInCache, alreadyValidatedTheseSettings := c.havePreviouslyValidated( + // There is no need to repeat connection probe validations for a URL and CA bundle combination that was already + // successfully validated. We are making a design decision to avoid repeating the validation which dials the server, + // even though the server's TLS configuration could have changed, because it is also possible that the network + // could be flaky. We are choosing to prefer to keep the authenticator cached (available for end-user auth attempts) + // during times of network flakes rather than trying to show the most up-to-date status possible. These validations + // are for administrator convenience at the time of a configuration change, to catch typos and blatant + // misconfigurations, rather than to constantly monitor for external issues. + foundAuthenticatorInCache, previouslyValidatedWithSameEndpointAndBundle := c.havePreviouslyValidated( cacheKey, jwtAuthenticator.Spec.Issuer, tlsBundleOk, caBundle.Hash(), logger) - if alreadyValidatedTheseSettings { - // Stop, no more work to be done. This authenticator is already validated and cached. - // TODO: Append hardcoded list of remaining success conditions and skip redoing the remaining validations below. - // Make sure that the conditions array is still checked for any errors, and the status and cache are still updated, as below. - return nil + if previouslyValidatedWithSameEndpointAndBundle { + // Because the authenticator was previously cached, that implies that the following conditions were + // previously validated. These are the expensive validations to repeat, so skip them this time. + // However, the status may be lagging behind due to the informer cache being slow to catch up + // after previous status updates, so always calculate the new status conditions again and check + // if they need to be updated. + conditions = append(conditions, + successfulDiscoveryValidCondition(), + successfulJWKSURLValidCondition(), + successfulJWKSFetchValidCondition(), + successfulAuthenticatorValidCondition(), + ) + } else { + // Run all remaining validations. + a, moreConditions, moreErrs := c.doExpensiveValidations(jwtAuthenticator, caBundle, okSoFar) + newJWTAuthenticatorForCache = a + conditions = append(conditions, moreConditions...) + errs = append(errs, moreErrs...) } + authenticatorValid := !conditionsutil.HadErrorCondition(conditions) + + // If we calculated a failed status condition, then remove it from the cache even before we try to write + // the status, because writing the status can fail for various reasons. + if !authenticatorValid { + // The authenticator was determined to be invalid. Remove it from the cache, in case it was previously + // validated and cached. Do not allow an old, previously validated spec of the authenticator to continue + // being used for authentication. + c.cache.Delete(cacheKey) + logger.Info("invalid jwt authenticator", + "removedFromCache", foundAuthenticatorInCache) + } + + // Always try to update the status, even when we found it in the authenticator cache. + updateErr := c.updateStatus(ctx, jwtAuthenticator, conditions, logger) + errs = append(errs, updateErr) + + // Only add/update this authenticator to the cache when we have a new one and the status update succeeded. + if newJWTAuthenticatorForCache != nil && authenticatorValid && updateErr == nil { + c.cache.Store(cacheKey, newJWTAuthenticatorForCache) + logger.Info("added or updated jwt authenticator in cache", + "isOverwrite", foundAuthenticatorInCache) + } + + // Sync loop errors: + // - Should not be configuration errors. Config errors a user must correct belong on the .Status + // object. The controller simply must wait for a user to correct before running again. + // - Other errors, such as networking errors, etc. are the types of errors that should return here + // and signal the controller to retry the sync loop. These may be corrected by machines. + return utilerrors.NewAggregate(errs) +} + +func (c *jwtCacheFillerController) doExpensiveValidations( + jwtAuthenticator *authenticationv1alpha1.JWTAuthenticator, + caBundle *tlsconfigutil.CABundle, + okSoFar bool, +) (*cachedJWTAuthenticator, []*metav1.Condition, []error) { + var conditions []*metav1.Condition + var errs []error + client := phttp.Default(caBundle.CertPool()) client.Timeout = 30 * time.Second // copied from Kube OIDC code coreOSCtx := coreosoidc.ClientContext(context.Background(), client) @@ -273,37 +327,7 @@ func (c *jwtCacheFillerController) syncIndividualJWTAuthenticator(ctx context.Co okSoFar) errs = append(errs, err) - authenticatorValid := !conditionsutil.HadErrorCondition(conditions) - - // If we calculated a failed status condition, then remove it from the cache even before we try to write - // the status, because writing the status can fail for various reasons. - if !authenticatorValid { - // The authenticator was determined to be invalid. Remove it from the cache, in case it was previously - // validated and cached. Do not allow an old, previously validated spec of the authenticator to continue - // being used for authentication. - c.cache.Delete(cacheKey) - logger.Info("invalid jwt authenticator", - "removedFromCache", foundAuthenticatorInCache) - } - - updateErr := c.updateStatus(ctx, jwtAuthenticator, conditions, logger) - errs = append(errs, updateErr) - - // Only add this JWTAuthenticator to the cache if the status update succeeds. - // If it were in the cache after failing to update the status, then the next Sync loop would see it in the cache - // and skip trying to update its status again, which would leave its old status permanently intact. - if authenticatorValid && updateErr == nil { - c.cache.Store(cacheKey, newJWTAuthenticatorForCache) - logger.Info("added or updated jwt authenticator in cache", - "isOverwrite", foundAuthenticatorInCache) - } - - // Sync loop errors: - // - Should not be configuration errors. Config errors a user must correct belong on the .Status - // object. The controller simply must wait for a user to correct before running again. - // - Other errors, such as networking errors, etc. are the types of errors that should return here - // and signal the controller to retry the sync loop. These may be corrected by machines. - return utilerrors.NewAggregate(errs) + return newJWTAuthenticatorForCache, conditions, errs } func (c *jwtCacheFillerController) havePreviouslyValidated( @@ -423,6 +447,15 @@ func (c *jwtCacheFillerController) validateIssuer(issuer string, conditions []*m return conditions, true } +func successfulDiscoveryValidCondition() *metav1.Condition { + return &metav1.Condition{ + Type: typeDiscoveryValid, + Status: metav1.ConditionTrue, + Reason: conditionsutil.ReasonSuccess, + Message: "discovery performed successfully", + } +} + func (c *jwtCacheFillerController) validateProviderDiscovery(ctx context.Context, issuer string, conditions []*metav1.Condition, prereqOk bool) (*providerJSON, *coreosoidc.Provider, []*metav1.Condition, error) { if !prereqOk { conditions = append(conditions, &metav1.Condition{ @@ -448,14 +481,17 @@ func (c *jwtCacheFillerController) validateProviderDiscovery(ctx context.Context // resync err, may be machine or other types of non-config error return nil, nil, conditions, fmt.Errorf("%s: %s", errText, err) } - msg := "discovery performed successfully" - conditions = append(conditions, &metav1.Condition{ - Type: typeDiscoveryValid, + conditions = append(conditions, successfulDiscoveryValidCondition()) + return pJSON, provider, conditions, nil +} + +func successfulJWKSURLValidCondition() *metav1.Condition { + return &metav1.Condition{ + Type: typeJWKSURLValid, Status: metav1.ConditionTrue, Reason: conditionsutil.ReasonSuccess, - Message: msg, - }) - return pJSON, provider, conditions, nil + Message: "jwks_uri is a valid URL", + } } func (c *jwtCacheFillerController) validateProviderJWKSURL(provider *coreosoidc.Provider, pJSON *providerJSON, conditions []*metav1.Condition, prereqOk bool) (string, []*metav1.Condition, error) { @@ -508,13 +544,17 @@ func (c *jwtCacheFillerController) validateProviderJWKSURL(provider *coreosoidc. return pJSON.JWKSURL, conditions, fmt.Errorf("%s", msg) } - conditions = append(conditions, &metav1.Condition{ - Type: typeJWKSURLValid, + conditions = append(conditions, successfulJWKSURLValidCondition()) + return pJSON.JWKSURL, conditions, nil +} + +func successfulJWKSFetchValidCondition() *metav1.Condition { + return &metav1.Condition{ + Type: typeJWKSFetchValid, Status: metav1.ConditionTrue, Reason: conditionsutil.ReasonSuccess, - Message: "jwks_uri is a valid URL", - }) - return pJSON.JWKSURL, conditions, nil + Message: "successfully fetched jwks", + } } // validateJWKSFetch deliberately takes an unsigned JWT to trigger coreosoidc.NewRemoteKeySet to @@ -567,12 +607,7 @@ func (c *jwtCacheFillerController) validateJWKSFetch(ctx context.Context, jwksUR // This error indicates success of this check. We only wanted to test if we could fetch, we aren't actually // testing for valid signature verification. if strings.Contains(verifyErrString, "failed to verify id token signature") { - conditions = append(conditions, &metav1.Condition{ - Type: typeJWKSFetchValid, - Status: metav1.ConditionTrue, - Reason: conditionsutil.ReasonSuccess, - Message: "successfully fetched jwks", - }) + conditions = append(conditions, successfulJWKSFetchValidCondition()) return keySet, conditions, nil } @@ -588,6 +623,15 @@ func (c *jwtCacheFillerController) validateJWKSFetch(ctx context.Context, jwksUR return nil, conditions, fmt.Errorf("%s: %w", errText, verifyWithKeySetErr) } +func successfulAuthenticatorValidCondition() *metav1.Condition { + return &metav1.Condition{ + Type: typeAuthenticatorValid, + Status: metav1.ConditionTrue, + Reason: conditionsutil.ReasonSuccess, + Message: "authenticator initialized", + } +} + // newCachedJWTAuthenticator creates a jwt authenticator from the provided spec. func (c *jwtCacheFillerController) newCachedJWTAuthenticator( client *http.Client, @@ -656,13 +700,7 @@ func (c *jwtCacheFillerController) newCachedJWTAuthenticator( // resync err, lots of possible issues that may or may not be machine related return nil, conditions, fmt.Errorf("%s: %w", errText, err) } - msg := "authenticator initialized" - conditions = append(conditions, &metav1.Condition{ - Type: typeAuthenticatorValid, - Status: metav1.ConditionTrue, - Reason: conditionsutil.ReasonSuccess, - Message: msg, - }) + conditions = append(conditions, successfulAuthenticatorValidCondition()) return &cachedJWTAuthenticator{ Token: oidcAuthenticator, issuer: spec.Issuer, diff --git a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller_test.go b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller_test.go index 36c613e51..28cf8a590 100644 --- a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller_test.go +++ b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller_test.go @@ -1158,7 +1158,7 @@ func TestController(t *testing.T) { wantClose: true, }, { - name: "Sync: previously cached JWTAuthenticator gets new spec fields, but status update fails: loop will leave it in the cache and avoid calling close", + name: "Sync: previously cached authenticator gets new spec fields, but status update fails: loop will leave it in the cache and avoid calling close", cache: func(t *testing.T, cache *authncache.Cache, wantClose bool) { oldCA, err := base64.StdEncoding.DecodeString(otherJWTAuthenticatorSpec.TLS.CertificateAuthorityData) require.NoError(t, err) @@ -1207,11 +1207,72 @@ func TestController(t *testing.T) { updateStatusAction, } }, + // skip the tests because the authenticator preloaded into the cache is the mock version that was added above skipTestingCachedAuthenticator: true, wantSyncErr: testutil.WantExactErrorString("error for JWTAuthenticator test-name: some update error"), wantNamesOfJWTAuthenticatorsInCache: []string{"test-name"}, // keeps the old entry in the cache wantClose: false, }, + { + name: "Sync: previously cached valid authenticator with unchanged issuer URL and CA bundle hash has invalid status conditions in informer cache, as can happen on subsequent sync soon after multiple quick status updates (when the informer cache finally catches up): should update status in current sync", + cache: func(t *testing.T, cache *authncache.Cache, wantClose bool) { + oldCA, err := base64.StdEncoding.DecodeString(someJWTAuthenticatorSpec.TLS.CertificateAuthorityData) + require.NoError(t, err) + cache.Store( + authncache.Key{ + Name: "test-name", + Kind: "JWTAuthenticator", + APIGroup: authenticationv1alpha1.SchemeGroupVersion.Group, + }, + newCacheValue(t, *someJWTAuthenticatorSpec, string(oldCA), wantClose), + ) + }, + jwtAuthenticators: []runtime.Object{ + &authenticationv1alpha1.JWTAuthenticator{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-name", + }, + Spec: *someJWTAuthenticatorSpec, + Status: authenticationv1alpha1.JWTAuthenticatorStatus{ + Conditions: conditionstestutil.Replace( + allHappyConditionsSuccess(goodIssuer, frozenMetav1Now, 0), + []metav1.Condition{ + sadReadyCondition(frozenMetav1Now, 0), + unknownAuthenticatorValid(frozenMetav1Now, 0), + sadJWKSFetch("some remote jwks error", frozenMetav1Now, 0), + }, + ), + Phase: "Error", + }, + }, + }, + wantLogLines: []string{ + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).havePreviouslyValidated","message":"cached jwt authenticator and desired jwt authenticator are the same: already cached, so skipping validations","jwtAuthenticator":"test-name","issuer":"%s"}`, someJWTAuthenticatorSpec.Issuer), + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).updateStatus","message":"jwtauthenticator status successfully updated","jwtAuthenticator":"test-name","issuer":"%s","phase":"Ready"}`, someJWTAuthenticatorSpec.Issuer), + }, + wantActions: func() []coretesting.Action { + updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-name", + }, + Spec: *someJWTAuthenticatorSpec, + Status: authenticationv1alpha1.JWTAuthenticatorStatus{ // updates the status to ready + Conditions: allHappyConditionsSuccess(goodIssuer, frozenMetav1Now, 0), + Phase: "Ready", + }, + }) + updateStatusAction.Subresource = "status" + return []coretesting.Action{ + coretesting.NewListAction(jwtAuthenticatorsGVR, jwtAUthenticatorGVK, "", metav1.ListOptions{}), + coretesting.NewWatchAction(jwtAuthenticatorsGVR, "", metav1.ListOptions{}), + updateStatusAction, + } + }, + // skip the tests because the authenticator preloaded into the cache is the mock version that was added above + skipTestingCachedAuthenticator: true, + wantNamesOfJWTAuthenticatorsInCache: []string{"test-name"}, // keeps the old entry in the cache + wantClose: false, + }, { name: "Sync: JWTAuthenticator with external and changed CA bundle: loop will close previous instance of JWTAuthenticator and complete successfully and update status conditions", cache: func(t *testing.T, cache *authncache.Cache, wantClose bool) { @@ -1261,7 +1322,7 @@ func TestController(t *testing.T) { wantNamesOfJWTAuthenticatorsInCache: []string{"test-name"}, }, { - name: "Sync: JWTAuthenticator with no change: loop will abort early and not update status conditions", + name: "Sync: previously cached JWTAuthenticator with no change: will not update status conditions", cache: func(t *testing.T, cache *authncache.Cache, wantClose bool) { oldCA, err := base64.StdEncoding.DecodeString(someJWTAuthenticatorSpec.TLS.CertificateAuthorityData) require.NoError(t, err) @@ -1280,10 +1341,15 @@ func TestController(t *testing.T) { Name: "test-name", }, Spec: *someJWTAuthenticatorSpec, + Status: authenticationv1alpha1.JWTAuthenticatorStatus{ + Conditions: allHappyConditionsSuccess(goodIssuer, frozenMetav1Now, 0), + Phase: "Ready", + }, }, }, wantLogLines: []string{ fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).havePreviouslyValidated","message":"cached jwt authenticator and desired jwt authenticator are the same: already cached, so skipping validations","jwtAuthenticator":"test-name","issuer":"%s"}`, goodIssuer), + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).updateStatus","message":"choosing to not update the jwtauthenticator status since there is no update to make","jwtAuthenticator":"test-name","issuer":"%s","phase":"Ready"}`, goodIssuer), }, wantActions: func() []coretesting.Action { return []coretesting.Action{ @@ -1291,10 +1357,10 @@ func TestController(t *testing.T) { coretesting.NewWatchAction(jwtAuthenticatorsGVR, "", metav1.ListOptions{}), } }, + // skip the tests because the authenticator preloaded into the cache is the mock version that was added above + skipTestingCachedAuthenticator: true, wantClose: false, wantNamesOfJWTAuthenticatorsInCache: []string{"test-name"}, - // skip the tests because the authenticator pre-loaded into the cache is the mock version that was added above - skipTestingCachedAuthenticator: true, }, { name: "Sync: authenticator update when cached authenticator is the wrong data type, which should never really happen: loop will complete successfully and update status conditions", diff --git a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go index 1856ab96e..94e1c21dc 100644 --- a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go +++ b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go @@ -169,6 +169,7 @@ func (c *webhookCacheFillerController) syncIndividualWebhookAuthenticator(ctx co var errs []error conditions := make([]*metav1.Condition, 0) + var newWebhookAuthenticatorForCache *cachedWebhookAuthenticator caBundle, conditions, tlsBundleOk := c.validateTLSBundle(webhookAuthenticator.Spec.TLS, conditions) @@ -176,36 +177,33 @@ func (c *webhookCacheFillerController) syncIndividualWebhookAuthenticator(ctx co okSoFar := tlsBundleOk && endpointOk // Only revalidate and update the cache if the cached authenticator is different from the desired authenticator. - // There is no need to repeat validations for a spec that was already successfully validated. We are making a - // design decision to avoid repeating the validation which dials the server, even though the server's TLS - // configuration could have changed, because it is also possible that the network could be flaky. We are choosing - // to prefer to keep the authenticator cached (available for end-user auth attempts) during times of network flakes - // rather than trying to show the most up-to-date status possible. These validations are for administrator - // convenience at the time of a configuration change, to catch typos and blatant misconfigurations, rather - // than to constantly monitor for external issues. - foundAuthenticatorInCache, alreadyValidatedTheseSettings := c.havePreviouslyValidated( + // There is no need to repeat connection probe validations for a URL and CA bundle combination that was already + // successfully validated. We are making a design decision to avoid repeating the validation which dials the server, + // even though the server's TLS configuration could have changed, because it is also possible that the network + // could be flaky. We are choosing to prefer to keep the authenticator cached (available for end-user auth attempts) + // during times of network flakes rather than trying to show the most up-to-date status possible. These validations + // are for administrator convenience at the time of a configuration change, to catch typos and blatant + // misconfigurations, rather than to constantly monitor for external issues. + foundAuthenticatorInCache, previouslyValidatedWithSameEndpointAndBundle := c.havePreviouslyValidated( cacheKey, webhookAuthenticator.Spec.Endpoint, tlsBundleOk, caBundle.Hash(), logger) - if alreadyValidatedTheseSettings { - // Stop, no more work to be done. This authenticator is already validated and cached. - // TODO: Append hardcoded list of remaining success conditions and skip redoing the remaining validations below. - // Make sure that the conditions array is still checked for any errors, and the status and cache are still updated, as below. - return nil + if previouslyValidatedWithSameEndpointAndBundle { + // Because the authenticator was previously cached, that implies that the following conditions were + // previously validated. These are the expensive validations to repeat, so skip them this time. + // However, the status may be lagging behind due to the informer cache being slow to catch up + // after previous status updates, so always calculate the new status conditions again and check + // if they need to be updated. + conditions = append(conditions, + successfulWebhookConnectionValidCondition(), + successfulAuthenticatorValidCondition(), + ) + } else { + // Run all remaining validations. + a, moreConditions, moreErrs := c.doExpensiveValidations(webhookAuthenticator, endpointHostPort, caBundle, okSoFar, logger) + newWebhookAuthenticatorForCache = a + conditions = append(conditions, moreConditions...) + errs = append(errs, moreErrs...) } - conditions, tlsNegotiateErr := c.validateConnection(caBundle.CertPool(), endpointHostPort, conditions, okSoFar, logger) - errs = append(errs, tlsNegotiateErr) - okSoFar = okSoFar && tlsNegotiateErr == nil - - newWebhookAuthenticatorForCache, conditions, err := newWebhookAuthenticator( - // Note that we use the whole URL when constructing the webhook client, - // not just the host and port that we validated above. We need the path, etc. - webhookAuthenticator.Spec.Endpoint, - caBundle.PEMBytes(), - conditions, - okSoFar, - ) - errs = append(errs, err) - authenticatorValid := !conditionsutil.HadErrorCondition(conditions) // If we calculated a failed status condition, then remove it from the cache even before we try to write @@ -219,18 +217,13 @@ func (c *webhookCacheFillerController) syncIndividualWebhookAuthenticator(ctx co "removedFromCache", foundAuthenticatorInCache) } + // Always try to update the status, even when we found it in the authenticator cache. updateErr := c.updateStatus(ctx, webhookAuthenticator, conditions, logger) errs = append(errs, updateErr) - // Only add this WebhookAuthenticator to the cache if the status update succeeds. - // If it were in the cache after failing to update the status, then the next Sync loop would see it in the cache - // and skip trying to update its status again, which would leave its old status permanently intact. - if authenticatorValid && updateErr == nil { - c.cache.Store(cacheKey, &cachedWebhookAuthenticator{ - Token: newWebhookAuthenticatorForCache, - endpoint: webhookAuthenticator.Spec.Endpoint, - caBundleHash: caBundle.Hash(), - }) + // Only add/update this authenticator to the cache when we have a new one and the status update succeeded. + if newWebhookAuthenticatorForCache != nil && authenticatorValid && updateErr == nil { + c.cache.Store(cacheKey, newWebhookAuthenticatorForCache) logger.Info("added or updated webhook authenticator in cache", "isOverwrite", foundAuthenticatorInCache) } @@ -243,6 +236,41 @@ func (c *webhookCacheFillerController) syncIndividualWebhookAuthenticator(ctx co return utilerrors.NewAggregate(errs) } +func (c *webhookCacheFillerController) doExpensiveValidations( + webhookAuthenticator *authenticationv1alpha1.WebhookAuthenticator, + endpointHostPort *endpointaddr.HostPort, + caBundle *tlsconfigutil.CABundle, + okSoFar bool, + logger plog.Logger, +) (*cachedWebhookAuthenticator, []*metav1.Condition, []error) { + var newWebhookAuthenticatorForCache *cachedWebhookAuthenticator + var conditions []*metav1.Condition + var errs []error + + conditions, tlsNegotiateErr := c.validateConnection(caBundle.CertPool(), endpointHostPort, conditions, okSoFar, logger) + errs = append(errs, tlsNegotiateErr) + okSoFar = okSoFar && tlsNegotiateErr == nil + + newAuthenticator, conditions, err := newWebhookAuthenticator( + // Note that we use the whole URL when constructing the webhook client, + // not just the host and port that we validated above. We need the path, etc. + webhookAuthenticator.Spec.Endpoint, + caBundle.PEMBytes(), + conditions, + okSoFar, + ) + errs = append(errs, err) + + if newAuthenticator != nil { + newWebhookAuthenticatorForCache = &cachedWebhookAuthenticator{ + Token: newAuthenticator, + endpoint: webhookAuthenticator.Spec.Endpoint, + caBundleHash: caBundle.Hash(), + } + } + return newWebhookAuthenticatorForCache, conditions, errs +} + func (c *webhookCacheFillerController) havePreviouslyValidated( cacheKey authncache.Key, endpoint string, @@ -294,6 +322,15 @@ func (c *webhookCacheFillerController) validateTLSBundle(tlsSpec *authentication return caBundle, conditions, condition.Status == metav1.ConditionTrue } +func successfulAuthenticatorValidCondition() *metav1.Condition { + return &metav1.Condition{ + Type: typeAuthenticatorValid, + Status: metav1.ConditionTrue, + Reason: conditionsutil.ReasonSuccess, + Message: "authenticator initialized", + } +} + // newWebhookAuthenticator creates a webhook from the provided API server url and caBundle // used to validate TLS connections. func newWebhookAuthenticator( @@ -362,17 +399,20 @@ func newWebhookAuthenticator( return nil, conditions, fmt.Errorf("%s: %w", errText, err) } - msg := "authenticator initialized" - conditions = append(conditions, &metav1.Condition{ - Type: typeAuthenticatorValid, - Status: metav1.ConditionTrue, - Reason: conditionsutil.ReasonSuccess, - Message: msg, - }) + conditions = append(conditions, successfulAuthenticatorValidCondition()) return webhookAuthenticator, conditions, nil } +func successfulWebhookConnectionValidCondition() *metav1.Condition { + return &metav1.Condition{ + Type: typeWebhookConnectionValid, + Status: metav1.ConditionTrue, + Reason: conditionsutil.ReasonSuccess, + Message: "successfully dialed webhook server", + } +} + func (c *webhookCacheFillerController) validateConnection( certPool *x509.CertPool, endpointHostPort *endpointaddr.HostPort, @@ -411,12 +451,7 @@ func (c *webhookCacheFillerController) validateConnection( logger.Error("error closing dialer", err) } - conditions = append(conditions, &metav1.Condition{ - Type: typeWebhookConnectionValid, - Status: metav1.ConditionTrue, - Reason: conditionsutil.ReasonSuccess, - Message: "successfully dialed webhook server", - }) + conditions = append(conditions, successfulWebhookConnectionValidCondition()) return conditions, nil } diff --git a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go index 86fc352ac..a40287eb4 100644 --- a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go +++ b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go @@ -769,6 +769,7 @@ func TestController(t *testing.T) { }, wantLogLines: []string{ fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).havePreviouslyValidated","message":"cached webhook authenticator and desired webhook authenticator are the same: already cached, so skipping validations","webhookAuthenticator":"test-name","endpoint":"%s"}`, goodWebhookDefaultServingCertEndpoint), + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"choosing to not update the webhookauthenticator status since there is no update to make","webhookAuthenticator":"test-name","endpoint":"%s","phase":"Ready"}`, goodWebhookDefaultServingCertEndpoint), }, wantActions: func() []coretesting.Action { return []coretesting.Action{ @@ -950,6 +951,66 @@ func TestController(t *testing.T) { wantSyncErr: testutil.WantExactErrorString("error for WebhookAuthenticator test-name: some update error"), wantNamesOfWebhookAuthenticatorsInCache: []string{"test-name"}, // keeps the old entry in the cache }, + { + name: "Sync: previously cached valid authenticator with unchanged endpoint URL and CA bundle hash has invalid status conditions in informer cache, as can happen on subsequent sync soon after multiple quick status updates (when the informer cache finally catches up): should update status in current sync", + cache: func(t *testing.T, cache *authncache.Cache) { + oldCA, err := base64.StdEncoding.DecodeString(goodWebhookAuthenticatorSpecWithCA.TLS.CertificateAuthorityData) + require.NoError(t, err) + cache.Store( + authncache.Key{ + Name: "test-name", + Kind: "WebhookAuthenticator", + APIGroup: authenticationv1alpha1.SchemeGroupVersion.Group, + }, + newCacheValue(t, goodWebhookAuthenticatorSpecWithCA, string(oldCA)), + ) + }, + webhookAuthenticators: []runtime.Object{ + &authenticationv1alpha1.WebhookAuthenticator{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-name", + Generation: 1234, + }, + Spec: goodWebhookAuthenticatorSpecWithCA, + Status: authenticationv1alpha1.WebhookAuthenticatorStatus{ + Conditions: conditionstestutil.Replace( + allHappyConditionsSuccess(goodWebhookDefaultServingCertEndpoint, frozenMetav1Now, 0), + []metav1.Condition{ + sadTLSConfigurationValid(frozenMetav1Now, 0), + unknownWebhookConnectionValid(frozenMetav1Now, 0), + unknownAuthenticatorValid(frozenMetav1Now, 0), + sadReadyCondition(frozenMetav1Now, 0), + }, + ), + Phase: "Error", + }, + }, + }, + wantLogLines: []string{ + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).havePreviouslyValidated","message":"cached webhook authenticator and desired webhook authenticator are the same: already cached, so skipping validations","webhookAuthenticator":"test-name","endpoint":"%s"}`, goodWebhookAuthenticatorSpecWithCA.Endpoint), + fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"%s","phase":"Ready"}`, goodWebhookAuthenticatorSpecWithCA.Endpoint), + }, + wantActions: func() []coretesting.Action { + updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &authenticationv1alpha1.WebhookAuthenticator{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-name", + Generation: 1234, + }, + Spec: goodWebhookAuthenticatorSpecWithCA, + Status: authenticationv1alpha1.WebhookAuthenticatorStatus{ // updates the status to ready + Conditions: allHappyConditionsSuccess(goodWebhookDefaultServingCertEndpoint, frozenMetav1Now, 1234), + Phase: "Ready", + }, + }) + updateStatusAction.Subresource = "status" + return []coretesting.Action{ + coretesting.NewListAction(webhookAuthenticatorGVR, webhookAuthenticatorGVK, "", metav1.ListOptions{}), + coretesting.NewWatchAction(webhookAuthenticatorGVR, "", metav1.ListOptions{}), + updateStatusAction, + } + }, + wantNamesOfWebhookAuthenticatorsInCache: []string{"test-name"}, // keeps the old entry in the cache + }, { name: "Sync: valid WebhookAuthenticator with CA: will complete sync loop successfully with success conditions and ready phase", webhookAuthenticators: []runtime.Object{ diff --git a/test/integration/concierge_jwtauthenticator_status_test.go b/test/integration/concierge_jwtauthenticator_status_test.go index dfc14565d..09f0d56a2 100644 --- a/test/integration/concierge_jwtauthenticator_status_test.go +++ b/test/integration/concierge_jwtauthenticator_status_test.go @@ -115,6 +115,48 @@ func TestConciergeJWTAuthenticatorWithExternalCABundleStatusIsUpdatedWhenExterna } } +func TestConciergeJWTAuthenticatorStatusShouldBeOverwrittenByControllerAfterAnyManualEdits_Parallel(t *testing.T) { + env := testlib.IntegrationEnv(t) + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute) + t.Cleanup(cancel) + + conciergeClient := testlib.NewConciergeClientset(t) + + // Run several times because there is always a chance that the test could pass because the controller + // will resync every 3 minutes even if it does not pay attention to changes in status. + for i := range 3 { + t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { + t.Parallel() + + authenticator := testlib.CreateTestJWTAuthenticator(ctx, t, authenticationv1alpha1.JWTAuthenticatorSpec{ + Issuer: env.SupervisorUpstreamOIDC.Issuer, + Audience: "does-not-matter", + TLS: &authenticationv1alpha1.TLSSpec{ + CertificateAuthorityData: base64.StdEncoding.EncodeToString([]byte(env.SupervisorUpstreamOIDC.CABundle)), + }, + }, authenticationv1alpha1.JWTAuthenticatorPhaseReady) + + updatedAuthenticator, err := conciergeClient.AuthenticationV1alpha1().JWTAuthenticators().Get(ctx, authenticator.Name, metav1.GetOptions{}) + require.NoError(t, err) + + updatedAuthenticator.Status.Phase = "Pending" + originalFirstConditionMessage := updatedAuthenticator.Status.Conditions[0].Message + updatedAuthenticator.Status.Conditions[0].Message = "this is a manually edited message that should go away" + _, err = conciergeClient.AuthenticationV1alpha1().JWTAuthenticators().UpdateStatus(ctx, updatedAuthenticator, metav1.UpdateOptions{}) + require.NoError(t, err) + + testlib.RequireEventually(t, func(requireEventually *require.Assertions) { + gotAuthenticator, err := conciergeClient.AuthenticationV1alpha1().JWTAuthenticators().Get(ctx, authenticator.Name, metav1.GetOptions{}) + requireEventually.NoError(err) + requireEventually.Equal(authenticationv1alpha1.JWTAuthenticatorPhaseReady, gotAuthenticator.Status.Phase, + "the controller should have changed the phase back to Ready") + requireEventually.Equal(originalFirstConditionMessage, gotAuthenticator.Status.Conditions[0].Message, + "the controller should have changed the message back to the correct value but it didn't") + }, 30*time.Second, 250*time.Millisecond) + }) + } +} + func TestConciergeJWTAuthenticatorStatus_Parallel(t *testing.T) { env := testlib.IntegrationEnv(t) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute) diff --git a/test/integration/concierge_webhookauthenticator_status_test.go b/test/integration/concierge_webhookauthenticator_status_test.go index 357e766e0..119909f6c 100644 --- a/test/integration/concierge_webhookauthenticator_status_test.go +++ b/test/integration/concierge_webhookauthenticator_status_test.go @@ -112,6 +112,42 @@ func TestConciergeWebhookAuthenticatorWithExternalCABundleStatusIsUpdatedWhenExt } } +func TestConciergeWebhookAuthenticatorStatusShouldBeOverwrittenByControllerAfterAnyManualEdits_Parallel(t *testing.T) { + env := testlib.IntegrationEnv(t) + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute) + t.Cleanup(cancel) + + conciergeClient := testlib.NewConciergeClientset(t) + + // Run several times because there is always a chance that the test could pass because the controller + // will resync every 3 minutes even if it does not pay attention to changes in status. + for i := range 3 { + t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { + t.Parallel() + + authenticator := testlib.CreateTestWebhookAuthenticator(ctx, t, &env.TestWebhook, authenticationv1alpha1.WebhookAuthenticatorPhaseReady) + + updatedAuthenticator, err := conciergeClient.AuthenticationV1alpha1().WebhookAuthenticators().Get(ctx, authenticator.Name, metav1.GetOptions{}) + require.NoError(t, err) + + updatedAuthenticator.Status.Phase = "Pending" + originalFirstConditionMessage := updatedAuthenticator.Status.Conditions[0].Message + updatedAuthenticator.Status.Conditions[0].Message = "this is a manually edited message that should go away" + _, err = conciergeClient.AuthenticationV1alpha1().WebhookAuthenticators().UpdateStatus(ctx, updatedAuthenticator, metav1.UpdateOptions{}) + require.NoError(t, err) + + testlib.RequireEventually(t, func(requireEventually *require.Assertions) { + gotAuthenticator, err := conciergeClient.AuthenticationV1alpha1().WebhookAuthenticators().Get(ctx, authenticator.Name, metav1.GetOptions{}) + requireEventually.NoError(err) + requireEventually.Equal(authenticationv1alpha1.WebhookAuthenticatorPhaseReady, gotAuthenticator.Status.Phase, + "the controller should have changed the phase back to Ready") + requireEventually.Equal(originalFirstConditionMessage, gotAuthenticator.Status.Conditions[0].Message, + "the controller should have changed the message back to the correct value but it didn't") + }, 30*time.Second, 250*time.Millisecond) + }) + } +} + func TestConciergeWebhookAuthenticatorStatus_Parallel(t *testing.T) { env := testlib.IntegrationEnv(t) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute) From 19c4acf3919fdc1a89921398ae127da6978543a7 Mon Sep 17 00:00:00 2001 From: Ashish Amarnath Date: Wed, 31 Jul 2024 12:14:45 -0700 Subject: [PATCH 79/99] secret/configmap with CA bundle to be created in namespace where pinniped is installed Signed-off-by: Ashish Amarnath --- apis/concierge/authentication/v1alpha1/types_tls.go.tmpl | 1 + apis/supervisor/idp/v1alpha1/types_tls.go.tmpl | 1 + 2 files changed, 2 insertions(+) diff --git a/apis/concierge/authentication/v1alpha1/types_tls.go.tmpl b/apis/concierge/authentication/v1alpha1/types_tls.go.tmpl index ad2278985..cc823a05e 100644 --- a/apis/concierge/authentication/v1alpha1/types_tls.go.tmpl +++ b/apis/concierge/authentication/v1alpha1/types_tls.go.tmpl @@ -11,6 +11,7 @@ type CABundleSource struct { // +kubebuilder:validation:Enum=Secret;ConfigMap Kind string `json:"kind"` // Name of the secret or configmap from which to read the CA bundle. + // The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. // +kubebuilder:validation:MinLength=1 Name string `json:"name"` // Key within the secret or configmap from which to read the CA bundle. diff --git a/apis/supervisor/idp/v1alpha1/types_tls.go.tmpl b/apis/supervisor/idp/v1alpha1/types_tls.go.tmpl index c0fc606f6..19d6d863a 100644 --- a/apis/supervisor/idp/v1alpha1/types_tls.go.tmpl +++ b/apis/supervisor/idp/v1alpha1/types_tls.go.tmpl @@ -12,6 +12,7 @@ type CABundleSource struct { // +kubebuilder:validation:Enum=Secret;ConfigMap Kind string `json:"kind"` // Name of the secret or configmap from which to read the CA bundle. + // The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. // +kubebuilder:validation:MinLength=1 Name string `json:"name"` // Key within the secret or configmap from which to read the CA bundle. From 43964ff7a2c999a70fd5fd5f3068e9fd29678a3f Mon Sep 17 00:00:00 2001 From: Ashish Amarnath Date: Wed, 31 Jul 2024 13:11:16 -0700 Subject: [PATCH 80/99] update generated api docs Signed-off-by: Ashish Amarnath --- ...hentication.concierge.pinniped.dev_jwtauthenticators.yaml | 5 +++-- ...ication.concierge.pinniped.dev_webhookauthenticators.yaml | 5 +++-- ...rvisor.pinniped.dev_activedirectoryidentityproviders.yaml | 5 +++-- .../idp.supervisor.pinniped.dev_githubidentityproviders.yaml | 5 +++-- .../idp.supervisor.pinniped.dev_ldapidentityproviders.yaml | 5 +++-- .../idp.supervisor.pinniped.dev_oidcidentityproviders.yaml | 5 +++-- generated/1.24/README.adoc | 2 ++ .../1.24/apis/concierge/authentication/v1alpha1/types_tls.go | 1 + generated/1.24/apis/supervisor/idp/v1alpha1/types_tls.go | 1 + ...hentication.concierge.pinniped.dev_jwtauthenticators.yaml | 5 +++-- ...ication.concierge.pinniped.dev_webhookauthenticators.yaml | 5 +++-- ...rvisor.pinniped.dev_activedirectoryidentityproviders.yaml | 5 +++-- .../idp.supervisor.pinniped.dev_githubidentityproviders.yaml | 5 +++-- .../idp.supervisor.pinniped.dev_ldapidentityproviders.yaml | 5 +++-- .../idp.supervisor.pinniped.dev_oidcidentityproviders.yaml | 5 +++-- generated/1.25/README.adoc | 2 ++ .../1.25/apis/concierge/authentication/v1alpha1/types_tls.go | 1 + generated/1.25/apis/supervisor/idp/v1alpha1/types_tls.go | 1 + ...hentication.concierge.pinniped.dev_jwtauthenticators.yaml | 5 +++-- ...ication.concierge.pinniped.dev_webhookauthenticators.yaml | 5 +++-- ...rvisor.pinniped.dev_activedirectoryidentityproviders.yaml | 5 +++-- .../idp.supervisor.pinniped.dev_githubidentityproviders.yaml | 5 +++-- .../idp.supervisor.pinniped.dev_ldapidentityproviders.yaml | 5 +++-- .../idp.supervisor.pinniped.dev_oidcidentityproviders.yaml | 5 +++-- generated/1.26/README.adoc | 2 ++ .../1.26/apis/concierge/authentication/v1alpha1/types_tls.go | 1 + generated/1.26/apis/supervisor/idp/v1alpha1/types_tls.go | 1 + ...hentication.concierge.pinniped.dev_jwtauthenticators.yaml | 5 +++-- ...ication.concierge.pinniped.dev_webhookauthenticators.yaml | 5 +++-- ...rvisor.pinniped.dev_activedirectoryidentityproviders.yaml | 5 +++-- .../idp.supervisor.pinniped.dev_githubidentityproviders.yaml | 5 +++-- .../idp.supervisor.pinniped.dev_ldapidentityproviders.yaml | 5 +++-- .../idp.supervisor.pinniped.dev_oidcidentityproviders.yaml | 5 +++-- generated/1.27/README.adoc | 2 ++ .../1.27/apis/concierge/authentication/v1alpha1/types_tls.go | 1 + generated/1.27/apis/supervisor/idp/v1alpha1/types_tls.go | 1 + ...hentication.concierge.pinniped.dev_jwtauthenticators.yaml | 5 +++-- ...ication.concierge.pinniped.dev_webhookauthenticators.yaml | 5 +++-- ...rvisor.pinniped.dev_activedirectoryidentityproviders.yaml | 5 +++-- .../idp.supervisor.pinniped.dev_githubidentityproviders.yaml | 5 +++-- .../idp.supervisor.pinniped.dev_ldapidentityproviders.yaml | 5 +++-- .../idp.supervisor.pinniped.dev_oidcidentityproviders.yaml | 5 +++-- generated/1.28/README.adoc | 2 ++ .../1.28/apis/concierge/authentication/v1alpha1/types_tls.go | 1 + generated/1.28/apis/supervisor/idp/v1alpha1/types_tls.go | 1 + ...hentication.concierge.pinniped.dev_jwtauthenticators.yaml | 5 +++-- ...ication.concierge.pinniped.dev_webhookauthenticators.yaml | 5 +++-- ...rvisor.pinniped.dev_activedirectoryidentityproviders.yaml | 5 +++-- .../idp.supervisor.pinniped.dev_githubidentityproviders.yaml | 5 +++-- .../idp.supervisor.pinniped.dev_ldapidentityproviders.yaml | 5 +++-- .../idp.supervisor.pinniped.dev_oidcidentityproviders.yaml | 5 +++-- generated/1.29/README.adoc | 2 ++ .../1.29/apis/concierge/authentication/v1alpha1/types_tls.go | 1 + generated/1.29/apis/supervisor/idp/v1alpha1/types_tls.go | 1 + ...hentication.concierge.pinniped.dev_jwtauthenticators.yaml | 5 +++-- ...ication.concierge.pinniped.dev_webhookauthenticators.yaml | 5 +++-- ...rvisor.pinniped.dev_activedirectoryidentityproviders.yaml | 5 +++-- .../idp.supervisor.pinniped.dev_githubidentityproviders.yaml | 5 +++-- .../idp.supervisor.pinniped.dev_ldapidentityproviders.yaml | 5 +++-- .../idp.supervisor.pinniped.dev_oidcidentityproviders.yaml | 5 +++-- generated/1.30/README.adoc | 2 ++ .../1.30/apis/concierge/authentication/v1alpha1/types_tls.go | 1 + generated/1.30/apis/supervisor/idp/v1alpha1/types_tls.go | 1 + ...hentication.concierge.pinniped.dev_jwtauthenticators.yaml | 5 +++-- ...ication.concierge.pinniped.dev_webhookauthenticators.yaml | 5 +++-- ...rvisor.pinniped.dev_activedirectoryidentityproviders.yaml | 5 +++-- .../idp.supervisor.pinniped.dev_githubidentityproviders.yaml | 5 +++-- .../idp.supervisor.pinniped.dev_ldapidentityproviders.yaml | 5 +++-- .../idp.supervisor.pinniped.dev_oidcidentityproviders.yaml | 5 +++-- generated/latest/README.adoc | 2 ++ .../apis/concierge/authentication/v1alpha1/types_tls.go | 1 + generated/latest/apis/supervisor/idp/v1alpha1/types_tls.go | 1 + 72 files changed, 176 insertions(+), 96 deletions(-) diff --git a/deploy/concierge/authentication.concierge.pinniped.dev_jwtauthenticators.yaml b/deploy/concierge/authentication.concierge.pinniped.dev_jwtauthenticators.yaml index 5b85a8bd3..88a7b3d8b 100644 --- a/deploy/concierge/authentication.concierge.pinniped.dev_jwtauthenticators.yaml +++ b/deploy/concierge/authentication.concierge.pinniped.dev_jwtauthenticators.yaml @@ -113,8 +113,9 @@ spec: - ConfigMap type: string name: - description: Name of the secret or configmap from which to - read the CA bundle. + description: |- + Name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. minLength: 1 type: string required: diff --git a/deploy/concierge/authentication.concierge.pinniped.dev_webhookauthenticators.yaml b/deploy/concierge/authentication.concierge.pinniped.dev_webhookauthenticators.yaml index c2dce0c76..8576dabbf 100644 --- a/deploy/concierge/authentication.concierge.pinniped.dev_webhookauthenticators.yaml +++ b/deploy/concierge/authentication.concierge.pinniped.dev_webhookauthenticators.yaml @@ -84,8 +84,9 @@ spec: - ConfigMap type: string name: - description: Name of the secret or configmap from which to - read the CA bundle. + description: |- + Name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. minLength: 1 type: string required: diff --git a/deploy/supervisor/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml b/deploy/supervisor/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml index 8940aebb9..36d7edcd4 100644 --- a/deploy/supervisor/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml +++ b/deploy/supervisor/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml @@ -188,8 +188,9 @@ spec: - ConfigMap type: string name: - description: Name of the secret or configmap from which to - read the CA bundle. + description: |- + Name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string required: diff --git a/deploy/supervisor/idp.supervisor.pinniped.dev_githubidentityproviders.yaml b/deploy/supervisor/idp.supervisor.pinniped.dev_githubidentityproviders.yaml index 39db773a2..1a5dea759 100644 --- a/deploy/supervisor/idp.supervisor.pinniped.dev_githubidentityproviders.yaml +++ b/deploy/supervisor/idp.supervisor.pinniped.dev_githubidentityproviders.yaml @@ -243,8 +243,9 @@ spec: - ConfigMap type: string name: - description: Name of the secret or configmap from which - to read the CA bundle. + description: |- + Name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string required: diff --git a/deploy/supervisor/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml b/deploy/supervisor/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml index 34f0cb92a..bcd798dba 100644 --- a/deploy/supervisor/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml +++ b/deploy/supervisor/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml @@ -179,8 +179,9 @@ spec: - ConfigMap type: string name: - description: Name of the secret or configmap from which to - read the CA bundle. + description: |- + Name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string required: diff --git a/deploy/supervisor/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml b/deploy/supervisor/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml index ff69c2d8c..330984a89 100644 --- a/deploy/supervisor/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml +++ b/deploy/supervisor/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml @@ -229,8 +229,9 @@ spec: - ConfigMap type: string name: - description: Name of the secret or configmap from which to - read the CA bundle. + description: |- + Name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string required: diff --git a/generated/1.24/README.adoc b/generated/1.24/README.adoc index 9c8e11adf..c61e4de01 100644 --- a/generated/1.24/README.adoc +++ b/generated/1.24/README.adoc @@ -40,6 +40,7 @@ CABundleSource provides a source for CA bundle used for client-side TLS verifica Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + | *`name`* __string__ | Name of the secret or configmap from which to read the CA bundle. + +The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. + | *`key`* __string__ | Key within the secret or configmap from which to read the CA bundle. + |=== @@ -1684,6 +1685,7 @@ CABundleSource provides a source for CA bundle used for client-side TLS verifica Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + | *`name`* __string__ | Name of the secret or configmap from which to read the CA bundle. + +The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. + | *`key`* __string__ | Key within the secret or configmap from which to read the CA bundle. + |=== diff --git a/generated/1.24/apis/concierge/authentication/v1alpha1/types_tls.go b/generated/1.24/apis/concierge/authentication/v1alpha1/types_tls.go index ad2278985..cc823a05e 100644 --- a/generated/1.24/apis/concierge/authentication/v1alpha1/types_tls.go +++ b/generated/1.24/apis/concierge/authentication/v1alpha1/types_tls.go @@ -11,6 +11,7 @@ type CABundleSource struct { // +kubebuilder:validation:Enum=Secret;ConfigMap Kind string `json:"kind"` // Name of the secret or configmap from which to read the CA bundle. + // The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. // +kubebuilder:validation:MinLength=1 Name string `json:"name"` // Key within the secret or configmap from which to read the CA bundle. diff --git a/generated/1.24/apis/supervisor/idp/v1alpha1/types_tls.go b/generated/1.24/apis/supervisor/idp/v1alpha1/types_tls.go index c0fc606f6..19d6d863a 100644 --- a/generated/1.24/apis/supervisor/idp/v1alpha1/types_tls.go +++ b/generated/1.24/apis/supervisor/idp/v1alpha1/types_tls.go @@ -12,6 +12,7 @@ type CABundleSource struct { // +kubebuilder:validation:Enum=Secret;ConfigMap Kind string `json:"kind"` // Name of the secret or configmap from which to read the CA bundle. + // The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. // +kubebuilder:validation:MinLength=1 Name string `json:"name"` // Key within the secret or configmap from which to read the CA bundle. diff --git a/generated/1.24/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml b/generated/1.24/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml index 5b85a8bd3..88a7b3d8b 100644 --- a/generated/1.24/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml +++ b/generated/1.24/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml @@ -113,8 +113,9 @@ spec: - ConfigMap type: string name: - description: Name of the secret or configmap from which to - read the CA bundle. + description: |- + Name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. minLength: 1 type: string required: diff --git a/generated/1.24/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml b/generated/1.24/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml index c2dce0c76..8576dabbf 100644 --- a/generated/1.24/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml +++ b/generated/1.24/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml @@ -84,8 +84,9 @@ spec: - ConfigMap type: string name: - description: Name of the secret or configmap from which to - read the CA bundle. + description: |- + Name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. minLength: 1 type: string required: diff --git a/generated/1.24/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml b/generated/1.24/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml index 8940aebb9..36d7edcd4 100644 --- a/generated/1.24/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml +++ b/generated/1.24/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml @@ -188,8 +188,9 @@ spec: - ConfigMap type: string name: - description: Name of the secret or configmap from which to - read the CA bundle. + description: |- + Name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string required: diff --git a/generated/1.24/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml b/generated/1.24/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml index 39db773a2..1a5dea759 100644 --- a/generated/1.24/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml +++ b/generated/1.24/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml @@ -243,8 +243,9 @@ spec: - ConfigMap type: string name: - description: Name of the secret or configmap from which - to read the CA bundle. + description: |- + Name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string required: diff --git a/generated/1.24/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml b/generated/1.24/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml index 34f0cb92a..bcd798dba 100644 --- a/generated/1.24/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml +++ b/generated/1.24/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml @@ -179,8 +179,9 @@ spec: - ConfigMap type: string name: - description: Name of the secret or configmap from which to - read the CA bundle. + description: |- + Name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string required: diff --git a/generated/1.24/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml b/generated/1.24/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml index ff69c2d8c..330984a89 100644 --- a/generated/1.24/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml +++ b/generated/1.24/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml @@ -229,8 +229,9 @@ spec: - ConfigMap type: string name: - description: Name of the secret or configmap from which to - read the CA bundle. + description: |- + Name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string required: diff --git a/generated/1.25/README.adoc b/generated/1.25/README.adoc index e3cc6695a..e876a4f6c 100644 --- a/generated/1.25/README.adoc +++ b/generated/1.25/README.adoc @@ -40,6 +40,7 @@ CABundleSource provides a source for CA bundle used for client-side TLS verifica Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + | *`name`* __string__ | Name of the secret or configmap from which to read the CA bundle. + +The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. + | *`key`* __string__ | Key within the secret or configmap from which to read the CA bundle. + |=== @@ -1684,6 +1685,7 @@ CABundleSource provides a source for CA bundle used for client-side TLS verifica Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + | *`name`* __string__ | Name of the secret or configmap from which to read the CA bundle. + +The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. + | *`key`* __string__ | Key within the secret or configmap from which to read the CA bundle. + |=== diff --git a/generated/1.25/apis/concierge/authentication/v1alpha1/types_tls.go b/generated/1.25/apis/concierge/authentication/v1alpha1/types_tls.go index ad2278985..cc823a05e 100644 --- a/generated/1.25/apis/concierge/authentication/v1alpha1/types_tls.go +++ b/generated/1.25/apis/concierge/authentication/v1alpha1/types_tls.go @@ -11,6 +11,7 @@ type CABundleSource struct { // +kubebuilder:validation:Enum=Secret;ConfigMap Kind string `json:"kind"` // Name of the secret or configmap from which to read the CA bundle. + // The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. // +kubebuilder:validation:MinLength=1 Name string `json:"name"` // Key within the secret or configmap from which to read the CA bundle. diff --git a/generated/1.25/apis/supervisor/idp/v1alpha1/types_tls.go b/generated/1.25/apis/supervisor/idp/v1alpha1/types_tls.go index c0fc606f6..19d6d863a 100644 --- a/generated/1.25/apis/supervisor/idp/v1alpha1/types_tls.go +++ b/generated/1.25/apis/supervisor/idp/v1alpha1/types_tls.go @@ -12,6 +12,7 @@ type CABundleSource struct { // +kubebuilder:validation:Enum=Secret;ConfigMap Kind string `json:"kind"` // Name of the secret or configmap from which to read the CA bundle. + // The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. // +kubebuilder:validation:MinLength=1 Name string `json:"name"` // Key within the secret or configmap from which to read the CA bundle. diff --git a/generated/1.25/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml b/generated/1.25/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml index 5b85a8bd3..88a7b3d8b 100644 --- a/generated/1.25/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml +++ b/generated/1.25/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml @@ -113,8 +113,9 @@ spec: - ConfigMap type: string name: - description: Name of the secret or configmap from which to - read the CA bundle. + description: |- + Name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. minLength: 1 type: string required: diff --git a/generated/1.25/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml b/generated/1.25/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml index c2dce0c76..8576dabbf 100644 --- a/generated/1.25/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml +++ b/generated/1.25/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml @@ -84,8 +84,9 @@ spec: - ConfigMap type: string name: - description: Name of the secret or configmap from which to - read the CA bundle. + description: |- + Name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. minLength: 1 type: string required: diff --git a/generated/1.25/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml b/generated/1.25/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml index 8940aebb9..36d7edcd4 100644 --- a/generated/1.25/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml +++ b/generated/1.25/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml @@ -188,8 +188,9 @@ spec: - ConfigMap type: string name: - description: Name of the secret or configmap from which to - read the CA bundle. + description: |- + Name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string required: diff --git a/generated/1.25/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml b/generated/1.25/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml index 39db773a2..1a5dea759 100644 --- a/generated/1.25/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml +++ b/generated/1.25/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml @@ -243,8 +243,9 @@ spec: - ConfigMap type: string name: - description: Name of the secret or configmap from which - to read the CA bundle. + description: |- + Name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string required: diff --git a/generated/1.25/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml b/generated/1.25/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml index 34f0cb92a..bcd798dba 100644 --- a/generated/1.25/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml +++ b/generated/1.25/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml @@ -179,8 +179,9 @@ spec: - ConfigMap type: string name: - description: Name of the secret or configmap from which to - read the CA bundle. + description: |- + Name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string required: diff --git a/generated/1.25/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml b/generated/1.25/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml index ff69c2d8c..330984a89 100644 --- a/generated/1.25/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml +++ b/generated/1.25/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml @@ -229,8 +229,9 @@ spec: - ConfigMap type: string name: - description: Name of the secret or configmap from which to - read the CA bundle. + description: |- + Name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string required: diff --git a/generated/1.26/README.adoc b/generated/1.26/README.adoc index 183d82ba9..0a22ec4c3 100644 --- a/generated/1.26/README.adoc +++ b/generated/1.26/README.adoc @@ -40,6 +40,7 @@ CABundleSource provides a source for CA bundle used for client-side TLS verifica Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + | *`name`* __string__ | Name of the secret or configmap from which to read the CA bundle. + +The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. + | *`key`* __string__ | Key within the secret or configmap from which to read the CA bundle. + |=== @@ -1684,6 +1685,7 @@ CABundleSource provides a source for CA bundle used for client-side TLS verifica Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + | *`name`* __string__ | Name of the secret or configmap from which to read the CA bundle. + +The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. + | *`key`* __string__ | Key within the secret or configmap from which to read the CA bundle. + |=== diff --git a/generated/1.26/apis/concierge/authentication/v1alpha1/types_tls.go b/generated/1.26/apis/concierge/authentication/v1alpha1/types_tls.go index ad2278985..cc823a05e 100644 --- a/generated/1.26/apis/concierge/authentication/v1alpha1/types_tls.go +++ b/generated/1.26/apis/concierge/authentication/v1alpha1/types_tls.go @@ -11,6 +11,7 @@ type CABundleSource struct { // +kubebuilder:validation:Enum=Secret;ConfigMap Kind string `json:"kind"` // Name of the secret or configmap from which to read the CA bundle. + // The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. // +kubebuilder:validation:MinLength=1 Name string `json:"name"` // Key within the secret or configmap from which to read the CA bundle. diff --git a/generated/1.26/apis/supervisor/idp/v1alpha1/types_tls.go b/generated/1.26/apis/supervisor/idp/v1alpha1/types_tls.go index c0fc606f6..19d6d863a 100644 --- a/generated/1.26/apis/supervisor/idp/v1alpha1/types_tls.go +++ b/generated/1.26/apis/supervisor/idp/v1alpha1/types_tls.go @@ -12,6 +12,7 @@ type CABundleSource struct { // +kubebuilder:validation:Enum=Secret;ConfigMap Kind string `json:"kind"` // Name of the secret or configmap from which to read the CA bundle. + // The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. // +kubebuilder:validation:MinLength=1 Name string `json:"name"` // Key within the secret or configmap from which to read the CA bundle. diff --git a/generated/1.26/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml b/generated/1.26/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml index 5b85a8bd3..88a7b3d8b 100644 --- a/generated/1.26/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml +++ b/generated/1.26/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml @@ -113,8 +113,9 @@ spec: - ConfigMap type: string name: - description: Name of the secret or configmap from which to - read the CA bundle. + description: |- + Name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. minLength: 1 type: string required: diff --git a/generated/1.26/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml b/generated/1.26/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml index c2dce0c76..8576dabbf 100644 --- a/generated/1.26/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml +++ b/generated/1.26/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml @@ -84,8 +84,9 @@ spec: - ConfigMap type: string name: - description: Name of the secret or configmap from which to - read the CA bundle. + description: |- + Name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. minLength: 1 type: string required: diff --git a/generated/1.26/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml b/generated/1.26/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml index 8940aebb9..36d7edcd4 100644 --- a/generated/1.26/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml +++ b/generated/1.26/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml @@ -188,8 +188,9 @@ spec: - ConfigMap type: string name: - description: Name of the secret or configmap from which to - read the CA bundle. + description: |- + Name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string required: diff --git a/generated/1.26/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml b/generated/1.26/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml index 39db773a2..1a5dea759 100644 --- a/generated/1.26/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml +++ b/generated/1.26/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml @@ -243,8 +243,9 @@ spec: - ConfigMap type: string name: - description: Name of the secret or configmap from which - to read the CA bundle. + description: |- + Name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string required: diff --git a/generated/1.26/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml b/generated/1.26/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml index 34f0cb92a..bcd798dba 100644 --- a/generated/1.26/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml +++ b/generated/1.26/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml @@ -179,8 +179,9 @@ spec: - ConfigMap type: string name: - description: Name of the secret or configmap from which to - read the CA bundle. + description: |- + Name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string required: diff --git a/generated/1.26/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml b/generated/1.26/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml index ff69c2d8c..330984a89 100644 --- a/generated/1.26/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml +++ b/generated/1.26/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml @@ -229,8 +229,9 @@ spec: - ConfigMap type: string name: - description: Name of the secret or configmap from which to - read the CA bundle. + description: |- + Name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string required: diff --git a/generated/1.27/README.adoc b/generated/1.27/README.adoc index 8b3958ac5..2a1868652 100644 --- a/generated/1.27/README.adoc +++ b/generated/1.27/README.adoc @@ -40,6 +40,7 @@ CABundleSource provides a source for CA bundle used for client-side TLS verifica Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + | *`name`* __string__ | Name of the secret or configmap from which to read the CA bundle. + +The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. + | *`key`* __string__ | Key within the secret or configmap from which to read the CA bundle. + |=== @@ -1684,6 +1685,7 @@ CABundleSource provides a source for CA bundle used for client-side TLS verifica Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + | *`name`* __string__ | Name of the secret or configmap from which to read the CA bundle. + +The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. + | *`key`* __string__ | Key within the secret or configmap from which to read the CA bundle. + |=== diff --git a/generated/1.27/apis/concierge/authentication/v1alpha1/types_tls.go b/generated/1.27/apis/concierge/authentication/v1alpha1/types_tls.go index ad2278985..cc823a05e 100644 --- a/generated/1.27/apis/concierge/authentication/v1alpha1/types_tls.go +++ b/generated/1.27/apis/concierge/authentication/v1alpha1/types_tls.go @@ -11,6 +11,7 @@ type CABundleSource struct { // +kubebuilder:validation:Enum=Secret;ConfigMap Kind string `json:"kind"` // Name of the secret or configmap from which to read the CA bundle. + // The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. // +kubebuilder:validation:MinLength=1 Name string `json:"name"` // Key within the secret or configmap from which to read the CA bundle. diff --git a/generated/1.27/apis/supervisor/idp/v1alpha1/types_tls.go b/generated/1.27/apis/supervisor/idp/v1alpha1/types_tls.go index c0fc606f6..19d6d863a 100644 --- a/generated/1.27/apis/supervisor/idp/v1alpha1/types_tls.go +++ b/generated/1.27/apis/supervisor/idp/v1alpha1/types_tls.go @@ -12,6 +12,7 @@ type CABundleSource struct { // +kubebuilder:validation:Enum=Secret;ConfigMap Kind string `json:"kind"` // Name of the secret or configmap from which to read the CA bundle. + // The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. // +kubebuilder:validation:MinLength=1 Name string `json:"name"` // Key within the secret or configmap from which to read the CA bundle. diff --git a/generated/1.27/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml b/generated/1.27/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml index 5b85a8bd3..88a7b3d8b 100644 --- a/generated/1.27/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml +++ b/generated/1.27/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml @@ -113,8 +113,9 @@ spec: - ConfigMap type: string name: - description: Name of the secret or configmap from which to - read the CA bundle. + description: |- + Name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. minLength: 1 type: string required: diff --git a/generated/1.27/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml b/generated/1.27/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml index c2dce0c76..8576dabbf 100644 --- a/generated/1.27/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml +++ b/generated/1.27/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml @@ -84,8 +84,9 @@ spec: - ConfigMap type: string name: - description: Name of the secret or configmap from which to - read the CA bundle. + description: |- + Name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. minLength: 1 type: string required: diff --git a/generated/1.27/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml b/generated/1.27/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml index 8940aebb9..36d7edcd4 100644 --- a/generated/1.27/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml +++ b/generated/1.27/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml @@ -188,8 +188,9 @@ spec: - ConfigMap type: string name: - description: Name of the secret or configmap from which to - read the CA bundle. + description: |- + Name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string required: diff --git a/generated/1.27/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml b/generated/1.27/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml index 39db773a2..1a5dea759 100644 --- a/generated/1.27/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml +++ b/generated/1.27/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml @@ -243,8 +243,9 @@ spec: - ConfigMap type: string name: - description: Name of the secret or configmap from which - to read the CA bundle. + description: |- + Name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string required: diff --git a/generated/1.27/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml b/generated/1.27/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml index 34f0cb92a..bcd798dba 100644 --- a/generated/1.27/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml +++ b/generated/1.27/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml @@ -179,8 +179,9 @@ spec: - ConfigMap type: string name: - description: Name of the secret or configmap from which to - read the CA bundle. + description: |- + Name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string required: diff --git a/generated/1.27/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml b/generated/1.27/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml index ff69c2d8c..330984a89 100644 --- a/generated/1.27/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml +++ b/generated/1.27/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml @@ -229,8 +229,9 @@ spec: - ConfigMap type: string name: - description: Name of the secret or configmap from which to - read the CA bundle. + description: |- + Name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string required: diff --git a/generated/1.28/README.adoc b/generated/1.28/README.adoc index 913c0b5f7..64f76af36 100644 --- a/generated/1.28/README.adoc +++ b/generated/1.28/README.adoc @@ -40,6 +40,7 @@ CABundleSource provides a source for CA bundle used for client-side TLS verifica Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + | *`name`* __string__ | Name of the secret or configmap from which to read the CA bundle. + +The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. + | *`key`* __string__ | Key within the secret or configmap from which to read the CA bundle. + |=== @@ -1684,6 +1685,7 @@ CABundleSource provides a source for CA bundle used for client-side TLS verifica Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + | *`name`* __string__ | Name of the secret or configmap from which to read the CA bundle. + +The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. + | *`key`* __string__ | Key within the secret or configmap from which to read the CA bundle. + |=== diff --git a/generated/1.28/apis/concierge/authentication/v1alpha1/types_tls.go b/generated/1.28/apis/concierge/authentication/v1alpha1/types_tls.go index ad2278985..cc823a05e 100644 --- a/generated/1.28/apis/concierge/authentication/v1alpha1/types_tls.go +++ b/generated/1.28/apis/concierge/authentication/v1alpha1/types_tls.go @@ -11,6 +11,7 @@ type CABundleSource struct { // +kubebuilder:validation:Enum=Secret;ConfigMap Kind string `json:"kind"` // Name of the secret or configmap from which to read the CA bundle. + // The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. // +kubebuilder:validation:MinLength=1 Name string `json:"name"` // Key within the secret or configmap from which to read the CA bundle. diff --git a/generated/1.28/apis/supervisor/idp/v1alpha1/types_tls.go b/generated/1.28/apis/supervisor/idp/v1alpha1/types_tls.go index c0fc606f6..19d6d863a 100644 --- a/generated/1.28/apis/supervisor/idp/v1alpha1/types_tls.go +++ b/generated/1.28/apis/supervisor/idp/v1alpha1/types_tls.go @@ -12,6 +12,7 @@ type CABundleSource struct { // +kubebuilder:validation:Enum=Secret;ConfigMap Kind string `json:"kind"` // Name of the secret or configmap from which to read the CA bundle. + // The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. // +kubebuilder:validation:MinLength=1 Name string `json:"name"` // Key within the secret or configmap from which to read the CA bundle. diff --git a/generated/1.28/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml b/generated/1.28/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml index 5b85a8bd3..88a7b3d8b 100644 --- a/generated/1.28/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml +++ b/generated/1.28/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml @@ -113,8 +113,9 @@ spec: - ConfigMap type: string name: - description: Name of the secret or configmap from which to - read the CA bundle. + description: |- + Name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. minLength: 1 type: string required: diff --git a/generated/1.28/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml b/generated/1.28/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml index c2dce0c76..8576dabbf 100644 --- a/generated/1.28/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml +++ b/generated/1.28/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml @@ -84,8 +84,9 @@ spec: - ConfigMap type: string name: - description: Name of the secret or configmap from which to - read the CA bundle. + description: |- + Name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. minLength: 1 type: string required: diff --git a/generated/1.28/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml b/generated/1.28/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml index 8940aebb9..36d7edcd4 100644 --- a/generated/1.28/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml +++ b/generated/1.28/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml @@ -188,8 +188,9 @@ spec: - ConfigMap type: string name: - description: Name of the secret or configmap from which to - read the CA bundle. + description: |- + Name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string required: diff --git a/generated/1.28/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml b/generated/1.28/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml index 39db773a2..1a5dea759 100644 --- a/generated/1.28/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml +++ b/generated/1.28/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml @@ -243,8 +243,9 @@ spec: - ConfigMap type: string name: - description: Name of the secret or configmap from which - to read the CA bundle. + description: |- + Name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string required: diff --git a/generated/1.28/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml b/generated/1.28/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml index 34f0cb92a..bcd798dba 100644 --- a/generated/1.28/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml +++ b/generated/1.28/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml @@ -179,8 +179,9 @@ spec: - ConfigMap type: string name: - description: Name of the secret or configmap from which to - read the CA bundle. + description: |- + Name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string required: diff --git a/generated/1.28/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml b/generated/1.28/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml index ff69c2d8c..330984a89 100644 --- a/generated/1.28/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml +++ b/generated/1.28/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml @@ -229,8 +229,9 @@ spec: - ConfigMap type: string name: - description: Name of the secret or configmap from which to - read the CA bundle. + description: |- + Name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string required: diff --git a/generated/1.29/README.adoc b/generated/1.29/README.adoc index c169c7ee8..a6e8fdf5b 100644 --- a/generated/1.29/README.adoc +++ b/generated/1.29/README.adoc @@ -40,6 +40,7 @@ CABundleSource provides a source for CA bundle used for client-side TLS verifica Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + | *`name`* __string__ | Name of the secret or configmap from which to read the CA bundle. + +The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. + | *`key`* __string__ | Key within the secret or configmap from which to read the CA bundle. + |=== @@ -1684,6 +1685,7 @@ CABundleSource provides a source for CA bundle used for client-side TLS verifica Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + | *`name`* __string__ | Name of the secret or configmap from which to read the CA bundle. + +The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. + | *`key`* __string__ | Key within the secret or configmap from which to read the CA bundle. + |=== diff --git a/generated/1.29/apis/concierge/authentication/v1alpha1/types_tls.go b/generated/1.29/apis/concierge/authentication/v1alpha1/types_tls.go index ad2278985..cc823a05e 100644 --- a/generated/1.29/apis/concierge/authentication/v1alpha1/types_tls.go +++ b/generated/1.29/apis/concierge/authentication/v1alpha1/types_tls.go @@ -11,6 +11,7 @@ type CABundleSource struct { // +kubebuilder:validation:Enum=Secret;ConfigMap Kind string `json:"kind"` // Name of the secret or configmap from which to read the CA bundle. + // The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. // +kubebuilder:validation:MinLength=1 Name string `json:"name"` // Key within the secret or configmap from which to read the CA bundle. diff --git a/generated/1.29/apis/supervisor/idp/v1alpha1/types_tls.go b/generated/1.29/apis/supervisor/idp/v1alpha1/types_tls.go index c0fc606f6..19d6d863a 100644 --- a/generated/1.29/apis/supervisor/idp/v1alpha1/types_tls.go +++ b/generated/1.29/apis/supervisor/idp/v1alpha1/types_tls.go @@ -12,6 +12,7 @@ type CABundleSource struct { // +kubebuilder:validation:Enum=Secret;ConfigMap Kind string `json:"kind"` // Name of the secret or configmap from which to read the CA bundle. + // The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. // +kubebuilder:validation:MinLength=1 Name string `json:"name"` // Key within the secret or configmap from which to read the CA bundle. diff --git a/generated/1.29/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml b/generated/1.29/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml index 5b85a8bd3..88a7b3d8b 100644 --- a/generated/1.29/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml +++ b/generated/1.29/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml @@ -113,8 +113,9 @@ spec: - ConfigMap type: string name: - description: Name of the secret or configmap from which to - read the CA bundle. + description: |- + Name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. minLength: 1 type: string required: diff --git a/generated/1.29/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml b/generated/1.29/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml index c2dce0c76..8576dabbf 100644 --- a/generated/1.29/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml +++ b/generated/1.29/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml @@ -84,8 +84,9 @@ spec: - ConfigMap type: string name: - description: Name of the secret or configmap from which to - read the CA bundle. + description: |- + Name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. minLength: 1 type: string required: diff --git a/generated/1.29/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml b/generated/1.29/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml index 8940aebb9..36d7edcd4 100644 --- a/generated/1.29/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml +++ b/generated/1.29/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml @@ -188,8 +188,9 @@ spec: - ConfigMap type: string name: - description: Name of the secret or configmap from which to - read the CA bundle. + description: |- + Name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string required: diff --git a/generated/1.29/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml b/generated/1.29/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml index 39db773a2..1a5dea759 100644 --- a/generated/1.29/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml +++ b/generated/1.29/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml @@ -243,8 +243,9 @@ spec: - ConfigMap type: string name: - description: Name of the secret or configmap from which - to read the CA bundle. + description: |- + Name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string required: diff --git a/generated/1.29/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml b/generated/1.29/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml index 34f0cb92a..bcd798dba 100644 --- a/generated/1.29/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml +++ b/generated/1.29/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml @@ -179,8 +179,9 @@ spec: - ConfigMap type: string name: - description: Name of the secret or configmap from which to - read the CA bundle. + description: |- + Name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string required: diff --git a/generated/1.29/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml b/generated/1.29/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml index ff69c2d8c..330984a89 100644 --- a/generated/1.29/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml +++ b/generated/1.29/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml @@ -229,8 +229,9 @@ spec: - ConfigMap type: string name: - description: Name of the secret or configmap from which to - read the CA bundle. + description: |- + Name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string required: diff --git a/generated/1.30/README.adoc b/generated/1.30/README.adoc index 8eb340a40..f91e3138c 100644 --- a/generated/1.30/README.adoc +++ b/generated/1.30/README.adoc @@ -40,6 +40,7 @@ CABundleSource provides a source for CA bundle used for client-side TLS verifica Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + | *`name`* __string__ | Name of the secret or configmap from which to read the CA bundle. + +The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. + | *`key`* __string__ | Key within the secret or configmap from which to read the CA bundle. + |=== @@ -1684,6 +1685,7 @@ CABundleSource provides a source for CA bundle used for client-side TLS verifica Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + | *`name`* __string__ | Name of the secret or configmap from which to read the CA bundle. + +The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. + | *`key`* __string__ | Key within the secret or configmap from which to read the CA bundle. + |=== diff --git a/generated/1.30/apis/concierge/authentication/v1alpha1/types_tls.go b/generated/1.30/apis/concierge/authentication/v1alpha1/types_tls.go index ad2278985..cc823a05e 100644 --- a/generated/1.30/apis/concierge/authentication/v1alpha1/types_tls.go +++ b/generated/1.30/apis/concierge/authentication/v1alpha1/types_tls.go @@ -11,6 +11,7 @@ type CABundleSource struct { // +kubebuilder:validation:Enum=Secret;ConfigMap Kind string `json:"kind"` // Name of the secret or configmap from which to read the CA bundle. + // The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. // +kubebuilder:validation:MinLength=1 Name string `json:"name"` // Key within the secret or configmap from which to read the CA bundle. diff --git a/generated/1.30/apis/supervisor/idp/v1alpha1/types_tls.go b/generated/1.30/apis/supervisor/idp/v1alpha1/types_tls.go index c0fc606f6..19d6d863a 100644 --- a/generated/1.30/apis/supervisor/idp/v1alpha1/types_tls.go +++ b/generated/1.30/apis/supervisor/idp/v1alpha1/types_tls.go @@ -12,6 +12,7 @@ type CABundleSource struct { // +kubebuilder:validation:Enum=Secret;ConfigMap Kind string `json:"kind"` // Name of the secret or configmap from which to read the CA bundle. + // The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. // +kubebuilder:validation:MinLength=1 Name string `json:"name"` // Key within the secret or configmap from which to read the CA bundle. diff --git a/generated/1.30/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml b/generated/1.30/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml index 5b85a8bd3..88a7b3d8b 100644 --- a/generated/1.30/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml +++ b/generated/1.30/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml @@ -113,8 +113,9 @@ spec: - ConfigMap type: string name: - description: Name of the secret or configmap from which to - read the CA bundle. + description: |- + Name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. minLength: 1 type: string required: diff --git a/generated/1.30/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml b/generated/1.30/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml index c2dce0c76..8576dabbf 100644 --- a/generated/1.30/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml +++ b/generated/1.30/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml @@ -84,8 +84,9 @@ spec: - ConfigMap type: string name: - description: Name of the secret or configmap from which to - read the CA bundle. + description: |- + Name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. minLength: 1 type: string required: diff --git a/generated/1.30/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml b/generated/1.30/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml index 8940aebb9..36d7edcd4 100644 --- a/generated/1.30/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml +++ b/generated/1.30/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml @@ -188,8 +188,9 @@ spec: - ConfigMap type: string name: - description: Name of the secret or configmap from which to - read the CA bundle. + description: |- + Name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string required: diff --git a/generated/1.30/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml b/generated/1.30/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml index 39db773a2..1a5dea759 100644 --- a/generated/1.30/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml +++ b/generated/1.30/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml @@ -243,8 +243,9 @@ spec: - ConfigMap type: string name: - description: Name of the secret or configmap from which - to read the CA bundle. + description: |- + Name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string required: diff --git a/generated/1.30/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml b/generated/1.30/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml index 34f0cb92a..bcd798dba 100644 --- a/generated/1.30/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml +++ b/generated/1.30/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml @@ -179,8 +179,9 @@ spec: - ConfigMap type: string name: - description: Name of the secret or configmap from which to - read the CA bundle. + description: |- + Name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string required: diff --git a/generated/1.30/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml b/generated/1.30/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml index ff69c2d8c..330984a89 100644 --- a/generated/1.30/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml +++ b/generated/1.30/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml @@ -229,8 +229,9 @@ spec: - ConfigMap type: string name: - description: Name of the secret or configmap from which to - read the CA bundle. + description: |- + Name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string required: diff --git a/generated/latest/README.adoc b/generated/latest/README.adoc index 8eb340a40..f91e3138c 100644 --- a/generated/latest/README.adoc +++ b/generated/latest/README.adoc @@ -40,6 +40,7 @@ CABundleSource provides a source for CA bundle used for client-side TLS verifica Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + | *`name`* __string__ | Name of the secret or configmap from which to read the CA bundle. + +The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. + | *`key`* __string__ | Key within the secret or configmap from which to read the CA bundle. + |=== @@ -1684,6 +1685,7 @@ CABundleSource provides a source for CA bundle used for client-side TLS verifica Secrets must be of type kubernetes.io/tls or Opaque. + For configmaps, the value associated with the key is not expected to be base64 encoded. + | *`name`* __string__ | Name of the secret or configmap from which to read the CA bundle. + +The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. + | *`key`* __string__ | Key within the secret or configmap from which to read the CA bundle. + |=== diff --git a/generated/latest/apis/concierge/authentication/v1alpha1/types_tls.go b/generated/latest/apis/concierge/authentication/v1alpha1/types_tls.go index ad2278985..cc823a05e 100644 --- a/generated/latest/apis/concierge/authentication/v1alpha1/types_tls.go +++ b/generated/latest/apis/concierge/authentication/v1alpha1/types_tls.go @@ -11,6 +11,7 @@ type CABundleSource struct { // +kubebuilder:validation:Enum=Secret;ConfigMap Kind string `json:"kind"` // Name of the secret or configmap from which to read the CA bundle. + // The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. // +kubebuilder:validation:MinLength=1 Name string `json:"name"` // Key within the secret or configmap from which to read the CA bundle. diff --git a/generated/latest/apis/supervisor/idp/v1alpha1/types_tls.go b/generated/latest/apis/supervisor/idp/v1alpha1/types_tls.go index c0fc606f6..19d6d863a 100644 --- a/generated/latest/apis/supervisor/idp/v1alpha1/types_tls.go +++ b/generated/latest/apis/supervisor/idp/v1alpha1/types_tls.go @@ -12,6 +12,7 @@ type CABundleSource struct { // +kubebuilder:validation:Enum=Secret;ConfigMap Kind string `json:"kind"` // Name of the secret or configmap from which to read the CA bundle. + // The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. // +kubebuilder:validation:MinLength=1 Name string `json:"name"` // Key within the secret or configmap from which to read the CA bundle. From 91ef68992c662d2e32b80c66efcb1ca2911849c5 Mon Sep 17 00:00:00 2001 From: Ryan Richard Date: Wed, 31 Jul 2024 14:55:11 -0700 Subject: [PATCH 81/99] document new CA bundle source option in howto docs --- .../howto/concierge/configure-concierge-supervisor-jwt.md | 3 +++ .../docs/howto/concierge/configure-concierge-webhook.md | 5 ++++- .../configure-supervisor-with-activedirectory.md | 6 ++++++ .../howto/supervisor/configure-supervisor-with-dex.md | 6 ++++++ .../howto/supervisor/configure-supervisor-with-github.md | 3 +++ .../howto/supervisor/configure-supervisor-with-gitlab.md | 3 +++ .../supervisor/configure-supervisor-with-openldap.md | 3 +++ .../configure-supervisor-with-workspace_one_access.md | 8 +++++++- 8 files changed, 35 insertions(+), 2 deletions(-) diff --git a/site/content/docs/howto/concierge/configure-concierge-supervisor-jwt.md b/site/content/docs/howto/concierge/configure-concierge-supervisor-jwt.md index d9089775b..05905096b 100644 --- a/site/content/docs/howto/concierge/configure-concierge-supervisor-jwt.md +++ b/site/content/docs/howto/concierge/configure-concierge-supervisor-jwt.md @@ -56,6 +56,9 @@ spec: # If the TLS certificate of your FederationDomain is not signed by # a standard CA trusted by the Concierge pods by default, then # specify its CA here as a base64-encoded PEM. + # Alternatively, the CA bundle can be specified in a Secret or + # ConfigMap that will be dynamically watched by Pinniped for + # changes to the CA bundle (see API docs for details). tls: certificateAuthorityData: LS0tLS1CRUdJTiBDRVJUSUZJQ0...0tLQo= ``` diff --git a/site/content/docs/howto/concierge/configure-concierge-webhook.md b/site/content/docs/howto/concierge/configure-concierge-webhook.md index 0b0b0fe4f..c38325371 100644 --- a/site/content/docs/howto/concierge/configure-concierge-webhook.md +++ b/site/content/docs/howto/concierge/configure-concierge-webhook.md @@ -37,7 +37,10 @@ spec: # HTTPS endpoint to be called as a webhook endpoint: https://my-webhook.example.com/any/path tls: - # base64-encoded PEM CA bundle (optional) + # Base64-encoded PEM CA bundle for connections to webhook (optional). + # Alternatively, the CA bundle can be specified in a Secret or + # ConfigMap that will be dynamically watched by Pinniped for + # changes to the CA bundle (see API docs for details). certificateAuthorityData: "LS0tLS1CRUdJTi[...]" ``` diff --git a/site/content/docs/howto/supervisor/configure-supervisor-with-activedirectory.md b/site/content/docs/howto/supervisor/configure-supervisor-with-activedirectory.md index 55c8fc416..ab088a411 100644 --- a/site/content/docs/howto/supervisor/configure-supervisor-with-activedirectory.md +++ b/site/content/docs/howto/supervisor/configure-supervisor-with-activedirectory.md @@ -97,6 +97,12 @@ spec: # Specify the host of the Active Directory server. host: "activedirectory.example.com:636" + tls: + # Base64-encoded PEM CA bundle for connections to AD (optional). + # Alternatively, the CA bundle can be specified in a Secret or + # ConfigMap that will be dynamically watched by Pinniped for + # changes to the CA bundle (see API docs for details). + certificateAuthorityData: "LS0tLS1CRUdJTi[...]" # Specify how to search for the username when an end-user tries to log in # using their username and password. diff --git a/site/content/docs/howto/supervisor/configure-supervisor-with-dex.md b/site/content/docs/howto/supervisor/configure-supervisor-with-dex.md index db5a8879c..81ff4f9aa 100644 --- a/site/content/docs/howto/supervisor/configure-supervisor-with-dex.md +++ b/site/content/docs/howto/supervisor/configure-supervisor-with-dex.md @@ -86,6 +86,12 @@ metadata: spec: # Specify the upstream issuer URL (no trailing slash). issuer: https:// + tls: + # Base64-encoded PEM CA bundle for connections to Dex (optional). + # Alternatively, the CA bundle can be specified in a Secret or + # ConfigMap that will be dynamically watched by Pinniped for + # changes to the CA bundle (see API docs for details). + certificateAuthorityData: "LS0tLS1CRUdJTi[...]" # Specify how to form authorization requests to Dex. authorizationConfig: diff --git a/site/content/docs/howto/supervisor/configure-supervisor-with-github.md b/site/content/docs/howto/supervisor/configure-supervisor-with-github.md index 60d85bed6..eb9bbcc83 100644 --- a/site/content/docs/howto/supervisor/configure-supervisor-with-github.md +++ b/site/content/docs/howto/supervisor/configure-supervisor-with-github.md @@ -221,6 +221,9 @@ spec: # This field is usually only used for GitHub Enterprise Server. # Specify the CA certificate of the server as a # base64-encoded PEM bundle. + # Alternatively, the CA bundle can be specified in a Secret or + # ConfigMap that will be dynamically watched by Pinniped for + # changes to the CA bundle (see API docs for details). certificateAuthorityData: LS0tLS1CRUdJTiBDRVJUSUZJQ0FU.... client: diff --git a/site/content/docs/howto/supervisor/configure-supervisor-with-gitlab.md b/site/content/docs/howto/supervisor/configure-supervisor-with-gitlab.md index d0871160b..18b56627b 100644 --- a/site/content/docs/howto/supervisor/configure-supervisor-with-gitlab.md +++ b/site/content/docs/howto/supervisor/configure-supervisor-with-gitlab.md @@ -158,6 +158,9 @@ spec: # Specify the CA bundle for the GitLab server as base64-encoded PEM # data. For example, the output of `cat my-ca-bundle.pem | base64`. + # Alternatively, the CA bundle can be specified in a Secret or + # ConfigMap that will be dynamically watched by Pinniped for + # changes to the CA bundle (see API docs for details). # # This is only necessary if your instance uses a custom CA. tls: diff --git a/site/content/docs/howto/supervisor/configure-supervisor-with-openldap.md b/site/content/docs/howto/supervisor/configure-supervisor-with-openldap.md index 46aff6716..996c11a31 100644 --- a/site/content/docs/howto/supervisor/configure-supervisor-with-openldap.md +++ b/site/content/docs/howto/supervisor/configure-supervisor-with-openldap.md @@ -210,6 +210,9 @@ spec: # Specify the CA certificate of the LDAP server as a # base64-encoded PEM bundle. + # Alternatively, the CA bundle can be specified in a Secret or + # ConfigMap that will be dynamically watched by Pinniped for + # changes to the CA bundle (see API docs for details). tls: certificateAuthorityData: $(cat ca.pem | base64) diff --git a/site/content/docs/howto/supervisor/configure-supervisor-with-workspace_one_access.md b/site/content/docs/howto/supervisor/configure-supervisor-with-workspace_one_access.md index 0a04e6111..54422a5a7 100644 --- a/site/content/docs/howto/supervisor/configure-supervisor-with-workspace_one_access.md +++ b/site/content/docs/howto/supervisor/configure-supervisor-with-workspace_one_access.md @@ -70,6 +70,12 @@ spec: # actual issuer of your Workspace ONE Access environment. Note that # the Workspace ONE Access issuer ends with the string "/SAAS/auth." issuer: https://ws1.my-company.com/SAAS/auth + tls: + # Base64-encoded PEM CA bundle for connections to WS1 (optional). + # Alternatively, the CA bundle can be specified in a Secret or + # ConfigMap that will be dynamically watched by Pinniped for + # changes to the CA bundle (see API docs for details). + certificateAuthorityData: "LS0tLS1CRUdJTi[...]" # Specify how to form authorization requests to Workspace ONE Access. authorizationConfig: @@ -138,7 +144,7 @@ remaining claims are always available. "Test Group" ], "iss": "https://ws1.my-company.com/SAAS/auth", - "sub": "my-username@WS1-ENV-NAME", + "sub": "my-username@WS1-ENV-NAME" } ``` From 02e41baa478259f8b6e0dbf5f03fe1796fe5adcf Mon Sep 17 00:00:00 2001 From: Ryan Richard Date: Thu, 1 Aug 2024 15:17:56 -0700 Subject: [PATCH 82/99] small refactors --- .../jwtcachefiller/jwtcachefiller.go | 4 +-- .../jwtcachefiller/jwtcachefiller_test.go | 4 +-- .../webhookcachefiller/webhookcachefiller.go | 2 +- .../webhookcachefiller_test.go | 4 +-- internal/controller/utils.go | 13 +------- test/integration/supervisor_login_test.go | 30 +++++++++++-------- test/integration/supervisor_upstream_test.go | 19 +++++------- 7 files changed, 34 insertions(+), 42 deletions(-) diff --git a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go index 5d11b83bb..297c0fbd1 100644 --- a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go +++ b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go @@ -248,6 +248,7 @@ func (c *jwtCacheFillerController) syncIndividualJWTAuthenticator(ctx context.Co // However, the status may be lagging behind due to the informer cache being slow to catch up // after previous status updates, so always calculate the new status conditions again and check // if they need to be updated. + logger.Info("cached jwt authenticator and desired jwt authenticator are the same: already cached, so skipping validations") conditions = append(conditions, successfulDiscoveryValidCondition(), successfulJWKSURLValidCondition(), @@ -320,7 +321,7 @@ func (c *jwtCacheFillerController) doExpensiveValidations( newJWTAuthenticatorForCache, conditions, err := c.newCachedJWTAuthenticator( client, - jwtAuthenticator.Spec.DeepCopy(), // deep copy to avoid caching original object + &jwtAuthenticator.Spec, keySet, caBundle.Hash(), conditions, @@ -349,7 +350,6 @@ func (c *jwtCacheFillerController) havePreviouslyValidated( if authenticatorFromCache.issuer == issuer && tlsBundleOk && // if there was any error while validating the latest CA bundle, then do not consider it previously validated authenticatorFromCache.caBundleHash.Equal(caBundleHash) { - logger.Info("cached jwt authenticator and desired jwt authenticator are the same: already cached, so skipping validations") return true, true } return true, false // found the authenticator, but it had not been previously validated with these same settings diff --git a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller_test.go b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller_test.go index 28cf8a590..bd5ea7c2b 100644 --- a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller_test.go +++ b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller_test.go @@ -1247,7 +1247,7 @@ func TestController(t *testing.T) { }, }, wantLogLines: []string{ - fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).havePreviouslyValidated","message":"cached jwt authenticator and desired jwt authenticator are the same: already cached, so skipping validations","jwtAuthenticator":"test-name","issuer":"%s"}`, someJWTAuthenticatorSpec.Issuer), + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).syncIndividualJWTAuthenticator","message":"cached jwt authenticator and desired jwt authenticator are the same: already cached, so skipping validations","jwtAuthenticator":"test-name","issuer":"%s"}`, someJWTAuthenticatorSpec.Issuer), fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).updateStatus","message":"jwtauthenticator status successfully updated","jwtAuthenticator":"test-name","issuer":"%s","phase":"Ready"}`, someJWTAuthenticatorSpec.Issuer), }, wantActions: func() []coretesting.Action { @@ -1348,7 +1348,7 @@ func TestController(t *testing.T) { }, }, wantLogLines: []string{ - fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).havePreviouslyValidated","message":"cached jwt authenticator and desired jwt authenticator are the same: already cached, so skipping validations","jwtAuthenticator":"test-name","issuer":"%s"}`, goodIssuer), + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).syncIndividualJWTAuthenticator","message":"cached jwt authenticator and desired jwt authenticator are the same: already cached, so skipping validations","jwtAuthenticator":"test-name","issuer":"%s"}`, goodIssuer), fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:$jwtcachefiller.(*jwtCacheFillerController).updateStatus","message":"choosing to not update the jwtauthenticator status since there is no update to make","jwtAuthenticator":"test-name","issuer":"%s","phase":"Ready"}`, goodIssuer), }, wantActions: func() []coretesting.Action { diff --git a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go index 94e1c21dc..2361381cc 100644 --- a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go +++ b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller.go @@ -192,6 +192,7 @@ func (c *webhookCacheFillerController) syncIndividualWebhookAuthenticator(ctx co // However, the status may be lagging behind due to the informer cache being slow to catch up // after previous status updates, so always calculate the new status conditions again and check // if they need to be updated. + logger.Info("cached webhook authenticator and desired webhook authenticator are the same: already cached, so skipping validations") conditions = append(conditions, successfulWebhookConnectionValidCondition(), successfulAuthenticatorValidCondition(), @@ -290,7 +291,6 @@ func (c *webhookCacheFillerController) havePreviouslyValidated( if authenticatorFromCache.endpoint == endpoint && tlsBundleOk && // if there was any error while validating the latest CA bundle, then do not consider it previously validated authenticatorFromCache.caBundleHash.Equal(caBundleHash) { - logger.Info("cached webhook authenticator and desired webhook authenticator are the same: already cached, so skipping validations") return true, true } return true, false // found the authenticator, but it had not been previously validated with these same settings diff --git a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go index a40287eb4..35c530240 100644 --- a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go +++ b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go @@ -768,7 +768,7 @@ func TestController(t *testing.T) { }, }, wantLogLines: []string{ - fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).havePreviouslyValidated","message":"cached webhook authenticator and desired webhook authenticator are the same: already cached, so skipping validations","webhookAuthenticator":"test-name","endpoint":"%s"}`, goodWebhookDefaultServingCertEndpoint), + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"cached webhook authenticator and desired webhook authenticator are the same: already cached, so skipping validations","webhookAuthenticator":"test-name","endpoint":"%s"}`, goodWebhookDefaultServingCertEndpoint), fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"choosing to not update the webhookauthenticator status since there is no update to make","webhookAuthenticator":"test-name","endpoint":"%s","phase":"Ready"}`, goodWebhookDefaultServingCertEndpoint), }, wantActions: func() []coretesting.Action { @@ -987,7 +987,7 @@ func TestController(t *testing.T) { }, }, wantLogLines: []string{ - fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).havePreviouslyValidated","message":"cached webhook authenticator and desired webhook authenticator are the same: already cached, so skipping validations","webhookAuthenticator":"test-name","endpoint":"%s"}`, goodWebhookAuthenticatorSpecWithCA.Endpoint), + fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).syncIndividualWebhookAuthenticator","message":"cached webhook authenticator and desired webhook authenticator are the same: already cached, so skipping validations","webhookAuthenticator":"test-name","endpoint":"%s"}`, goodWebhookAuthenticatorSpecWithCA.Endpoint), fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"webhookcachefiller-controller","caller":"webhookcachefiller/webhookcachefiller.go:$webhookcachefiller.(*webhookCacheFillerController).updateStatus","message":"webhookauthenticator status successfully updated","webhookAuthenticator":"test-name","endpoint":"%s","phase":"Ready"}`, goodWebhookAuthenticatorSpecWithCA.Endpoint), }, wantActions: func() []coretesting.Action { diff --git a/internal/controller/utils.go b/internal/controller/utils.go index a1600e705..e41b34dab 100644 --- a/internal/controller/utils.go +++ b/internal/controller/utils.go @@ -46,18 +46,7 @@ func SimpleFilter(match func(metav1.Object) bool, parentFunc controllerlib.Paren } func MatchAnySecretOfTypeFilter(secretType corev1.SecretType, parentFunc controllerlib.ParentFunc, namespaces ...string) controllerlib.Filter { - isSecretOfType := func(obj metav1.Object) bool { - secret, ok := obj.(*corev1.Secret) - if !ok { - return false - } - // Only match on namespace if namespaces are provided - if len(namespaces) > 0 && !slices.Contains(namespaces, secret.Namespace) { - return false - } - return secret.Type == secretType - } - return SimpleFilter(isSecretOfType, parentFunc) + return MatchAnySecretOfTypesFilter([]corev1.SecretType{secretType}, parentFunc, namespaces...) } func MatchAnySecretOfTypesFilter(secretTypes []corev1.SecretType, parentFunc controllerlib.ParentFunc, namespaces ...string) controllerlib.Filter { diff --git a/test/integration/supervisor_login_test.go b/test/integration/supervisor_login_test.go index 4c6c662e2..8ac9086e8 100644 --- a/test/integration/supervisor_login_test.go +++ b/test/integration/supervisor_login_test.go @@ -239,12 +239,13 @@ func TestSupervisorLogin_Browser(t *testing.T) { adIDP := testlib.CreateTestActiveDirectoryIdentityProvider(t, spec, idpv1alpha1.ActiveDirectoryPhaseReady) - expectedMsg := fmt.Sprintf( + expectedActiveDirectoryConnectionValidMessage := fmt.Sprintf( `successfully able to connect to "%s" and bind as user "%s" [validated with Secret "%s" at version "%s"]`, spec.Host, env.SupervisorUpstreamActiveDirectory.BindUsername, secret.Name, secret.ResourceVersion, ) - requireSuccessfulActiveDirectoryIdentityProviderConditions(t, adIDP, expectedMsg, env.SupervisorUpstreamActiveDirectory.CABundle != "") + requireSuccessfulActiveDirectoryIdentityProviderConditions(t, + adIDP, expectedActiveDirectoryConnectionValidMessage, env.SupervisorUpstreamActiveDirectory.CABundle != "") return adIDP, secret } @@ -292,12 +293,13 @@ func TestSupervisorLogin_Browser(t *testing.T) { ldapIDP := testlib.CreateTestLDAPIdentityProvider(t, spec, idpv1alpha1.LDAPPhaseReady) - expectedMsg := fmt.Sprintf( + expectedLDAPConnectionValidMessage := fmt.Sprintf( `successfully able to connect to "%s" and bind as user "%s" [validated with Secret "%s" at version "%s"]`, spec.Host, env.SupervisorUpstreamLDAP.BindUsername, secret.Name, secret.ResourceVersion, ) - requireSuccessfulLDAPIdentityProviderConditions(t, ldapIDP, expectedMsg, len(env.SupervisorUpstreamLDAP.CABundle) != 0) + requireSuccessfulLDAPIdentityProviderConditions(t, + ldapIDP, expectedLDAPConnectionValidMessage, len(env.SupervisorUpstreamLDAP.CABundle) != 0) return ldapIDP, secret } @@ -1124,7 +1126,7 @@ func TestSupervisorLogin_Browser(t *testing.T) { updatedSecret, err := client.CoreV1().Secrets(env.SupervisorNamespace).Update(ctx, secret, metav1.UpdateOptions{}) require.NoError(t, err) - expectedMsg := fmt.Sprintf( + expectedLDAPConnectionValidMessage := fmt.Sprintf( `successfully able to connect to "%s" and bind as user "%s" [validated with Secret "%s" at version "%s"]`, env.SupervisorUpstreamLDAP.Host, env.SupervisorUpstreamLDAP.BindUsername, updatedSecret.Name, updatedSecret.ResourceVersion, @@ -1135,7 +1137,8 @@ func TestSupervisorLogin_Browser(t *testing.T) { defer cancel() idp, err = supervisorClient.IDPV1alpha1().LDAPIdentityProviders(env.SupervisorNamespace).Get(ctx, idp.Name, metav1.GetOptions{}) requireEventually.NoError(err) - requireEventuallySuccessfulLDAPIdentityProviderConditions(t, requireEventually, idp, expectedMsg, len(env.SupervisorUpstreamLDAP.CABundle) != 0) + requireEventuallySuccessfulLDAPIdentityProviderConditions(t, + requireEventually, idp, expectedLDAPConnectionValidMessage, len(env.SupervisorUpstreamLDAP.CABundle) != 0) }, time.Minute, 500*time.Millisecond) return idp.Name }, @@ -1190,7 +1193,7 @@ func TestSupervisorLogin_Browser(t *testing.T) { }, }, metav1.CreateOptions{}) require.NoError(t, err) - expectedMsg := fmt.Sprintf( + expectedLDAPConnectionValidMessage := fmt.Sprintf( `successfully able to connect to "%s" and bind as user "%s" [validated with Secret "%s" at version "%s"]`, env.SupervisorUpstreamLDAP.Host, env.SupervisorUpstreamLDAP.BindUsername, recreatedSecret.Name, recreatedSecret.ResourceVersion, @@ -1201,7 +1204,8 @@ func TestSupervisorLogin_Browser(t *testing.T) { defer cancel() idp, err = supervisorClient.IDPV1alpha1().LDAPIdentityProviders(env.SupervisorNamespace).Get(ctx, idp.Name, metav1.GetOptions{}) requireEventually.NoError(err) - requireEventuallySuccessfulLDAPIdentityProviderConditions(t, requireEventually, idp, expectedMsg, len(env.SupervisorUpstreamLDAP.CABundle) != 0) + requireEventuallySuccessfulLDAPIdentityProviderConditions(t, + requireEventually, idp, expectedLDAPConnectionValidMessage, len(env.SupervisorUpstreamLDAP.CABundle) != 0) }, time.Minute, 500*time.Millisecond) return idp.Name }, @@ -1481,7 +1485,7 @@ func TestSupervisorLogin_Browser(t *testing.T) { updatedSecret, err := client.CoreV1().Secrets(env.SupervisorNamespace).Update(ctx, secret, metav1.UpdateOptions{}) require.NoError(t, err) - expectedMsg := fmt.Sprintf( + expectedActiveDirectoryConnectionValidMessage := fmt.Sprintf( `successfully able to connect to "%s" and bind as user "%s" [validated with Secret "%s" at version "%s"]`, env.SupervisorUpstreamActiveDirectory.Host, env.SupervisorUpstreamActiveDirectory.BindUsername, updatedSecret.Name, updatedSecret.ResourceVersion, @@ -1492,7 +1496,8 @@ func TestSupervisorLogin_Browser(t *testing.T) { defer cancel() idp, err = supervisorClient.IDPV1alpha1().ActiveDirectoryIdentityProviders(env.SupervisorNamespace).Get(ctx, idp.Name, metav1.GetOptions{}) requireEventually.NoError(err) - requireEventuallySuccessfulActiveDirectoryIdentityProviderConditions(t, requireEventually, idp, expectedMsg, len(env.SupervisorUpstreamActiveDirectory.CABundle) != 0) + requireEventuallySuccessfulActiveDirectoryIdentityProviderConditions(t, + requireEventually, idp, expectedActiveDirectoryConnectionValidMessage, len(env.SupervisorUpstreamActiveDirectory.CABundle) != 0) }, time.Minute, 500*time.Millisecond) return idp.Name }, @@ -1548,7 +1553,7 @@ func TestSupervisorLogin_Browser(t *testing.T) { }, metav1.CreateOptions{}) require.NoError(t, err) - expectedMsg := fmt.Sprintf( + expectedActiveDirectoryConnectionValidMessage := fmt.Sprintf( `successfully able to connect to "%s" and bind as user "%s" [validated with Secret "%s" at version "%s"]`, env.SupervisorUpstreamActiveDirectory.Host, env.SupervisorUpstreamActiveDirectory.BindUsername, recreatedSecret.Name, recreatedSecret.ResourceVersion, @@ -1559,7 +1564,8 @@ func TestSupervisorLogin_Browser(t *testing.T) { defer cancel() idp, err = supervisorClient.IDPV1alpha1().ActiveDirectoryIdentityProviders(env.SupervisorNamespace).Get(ctx, idp.Name, metav1.GetOptions{}) requireEventually.NoError(err) - requireEventuallySuccessfulActiveDirectoryIdentityProviderConditions(t, requireEventually, idp, expectedMsg, len(env.SupervisorUpstreamActiveDirectory.CABundle) != 0) + requireEventuallySuccessfulActiveDirectoryIdentityProviderConditions(t, + requireEventually, idp, expectedActiveDirectoryConnectionValidMessage, len(env.SupervisorUpstreamActiveDirectory.CABundle) != 0) }, time.Minute, 500*time.Millisecond) return idp.Name }, diff --git a/test/integration/supervisor_upstream_test.go b/test/integration/supervisor_upstream_test.go index 876839f07..c20da7301 100644 --- a/test/integration/supervisor_upstream_test.go +++ b/test/integration/supervisor_upstream_test.go @@ -140,19 +140,16 @@ func expectUpstreamConditions(t *testing.T, upstream *idpv1alpha1.OIDCIdentityPr } func expectedTLSConfigValidCondition(caBundleConfigured bool) metav1.Condition { - if caBundleConfigured { - return metav1.Condition{ - Type: "TLSConfigurationValid", - Status: "True", - Reason: "Success", - Message: `spec.tls is valid: using configured CA bundle`, - } - } - - return metav1.Condition{ + c := metav1.Condition{ Type: "TLSConfigurationValid", Status: "True", Reason: "Success", - Message: `spec.tls is valid: no TLS configuration provided: using default root CA bundle from container image`, + Message: "spec.tls is valid: no TLS configuration provided: using default root CA bundle from container image", } + + if caBundleConfigured { + c.Message = "spec.tls is valid: using configured CA bundle" + } + + return c } From e0235ed190f62938e90738f6a22edd8d03730577 Mon Sep 17 00:00:00 2001 From: Ryan Richard Date: Thu, 1 Aug 2024 16:21:31 -0700 Subject: [PATCH 83/99] update docs and change struct name in types_tls.go.tmpl files Co-authored-by: Ashish Amarnath --- .../authentication/v1alpha1/types_tls.go.tmpl | 18 +++++---- .../supervisor/idp/v1alpha1/types_tls.go.tmpl | 17 +++++---- ...cierge.pinniped.dev_jwtauthenticators.yaml | 15 +++++--- ...ge.pinniped.dev_webhookauthenticators.yaml | 15 +++++--- ....dev_activedirectoryidentityproviders.yaml | 15 +++++--- ....pinniped.dev_githubidentityproviders.yaml | 15 +++++--- ...or.pinniped.dev_ldapidentityproviders.yaml | 15 +++++--- ...or.pinniped.dev_oidcidentityproviders.yaml | 15 +++++--- generated/1.24/README.adoc | 38 ++++++++++--------- .../authentication/v1alpha1/types_tls.go | 18 +++++---- .../v1alpha1/zz_generated.deepcopy.go | 10 ++--- .../apis/supervisor/idp/v1alpha1/types_tls.go | 17 +++++---- .../idp/v1alpha1/zz_generated.deepcopy.go | 10 ++--- ...cierge.pinniped.dev_jwtauthenticators.yaml | 15 +++++--- ...ge.pinniped.dev_webhookauthenticators.yaml | 15 +++++--- ....dev_activedirectoryidentityproviders.yaml | 15 +++++--- ....pinniped.dev_githubidentityproviders.yaml | 15 +++++--- ...or.pinniped.dev_ldapidentityproviders.yaml | 15 +++++--- ...or.pinniped.dev_oidcidentityproviders.yaml | 15 +++++--- generated/1.25/README.adoc | 38 ++++++++++--------- .../authentication/v1alpha1/types_tls.go | 18 +++++---- .../v1alpha1/zz_generated.deepcopy.go | 10 ++--- .../apis/supervisor/idp/v1alpha1/types_tls.go | 17 +++++---- .../idp/v1alpha1/zz_generated.deepcopy.go | 10 ++--- ...cierge.pinniped.dev_jwtauthenticators.yaml | 15 +++++--- ...ge.pinniped.dev_webhookauthenticators.yaml | 15 +++++--- ....dev_activedirectoryidentityproviders.yaml | 15 +++++--- ....pinniped.dev_githubidentityproviders.yaml | 15 +++++--- ...or.pinniped.dev_ldapidentityproviders.yaml | 15 +++++--- ...or.pinniped.dev_oidcidentityproviders.yaml | 15 +++++--- generated/1.26/README.adoc | 38 ++++++++++--------- .../authentication/v1alpha1/types_tls.go | 18 +++++---- .../v1alpha1/zz_generated.deepcopy.go | 10 ++--- .../apis/supervisor/idp/v1alpha1/types_tls.go | 17 +++++---- .../idp/v1alpha1/zz_generated.deepcopy.go | 10 ++--- ...cierge.pinniped.dev_jwtauthenticators.yaml | 15 +++++--- ...ge.pinniped.dev_webhookauthenticators.yaml | 15 +++++--- ....dev_activedirectoryidentityproviders.yaml | 15 +++++--- ....pinniped.dev_githubidentityproviders.yaml | 15 +++++--- ...or.pinniped.dev_ldapidentityproviders.yaml | 15 +++++--- ...or.pinniped.dev_oidcidentityproviders.yaml | 15 +++++--- generated/1.27/README.adoc | 38 ++++++++++--------- .../authentication/v1alpha1/types_tls.go | 18 +++++---- .../v1alpha1/zz_generated.deepcopy.go | 10 ++--- .../apis/supervisor/idp/v1alpha1/types_tls.go | 17 +++++---- .../idp/v1alpha1/zz_generated.deepcopy.go | 10 ++--- ...cierge.pinniped.dev_jwtauthenticators.yaml | 15 +++++--- ...ge.pinniped.dev_webhookauthenticators.yaml | 15 +++++--- ....dev_activedirectoryidentityproviders.yaml | 15 +++++--- ....pinniped.dev_githubidentityproviders.yaml | 15 +++++--- ...or.pinniped.dev_ldapidentityproviders.yaml | 15 +++++--- ...or.pinniped.dev_oidcidentityproviders.yaml | 15 +++++--- generated/1.28/README.adoc | 38 ++++++++++--------- .../authentication/v1alpha1/types_tls.go | 18 +++++---- .../v1alpha1/zz_generated.deepcopy.go | 10 ++--- .../apis/supervisor/idp/v1alpha1/types_tls.go | 17 +++++---- .../idp/v1alpha1/zz_generated.deepcopy.go | 10 ++--- ...cierge.pinniped.dev_jwtauthenticators.yaml | 15 +++++--- ...ge.pinniped.dev_webhookauthenticators.yaml | 15 +++++--- ....dev_activedirectoryidentityproviders.yaml | 15 +++++--- ....pinniped.dev_githubidentityproviders.yaml | 15 +++++--- ...or.pinniped.dev_ldapidentityproviders.yaml | 15 +++++--- ...or.pinniped.dev_oidcidentityproviders.yaml | 15 +++++--- generated/1.29/README.adoc | 38 ++++++++++--------- .../authentication/v1alpha1/types_tls.go | 18 +++++---- .../v1alpha1/zz_generated.deepcopy.go | 10 ++--- .../apis/supervisor/idp/v1alpha1/types_tls.go | 17 +++++---- .../idp/v1alpha1/zz_generated.deepcopy.go | 10 ++--- ...cierge.pinniped.dev_jwtauthenticators.yaml | 15 +++++--- ...ge.pinniped.dev_webhookauthenticators.yaml | 15 +++++--- ....dev_activedirectoryidentityproviders.yaml | 15 +++++--- ....pinniped.dev_githubidentityproviders.yaml | 15 +++++--- ...or.pinniped.dev_ldapidentityproviders.yaml | 15 +++++--- ...or.pinniped.dev_oidcidentityproviders.yaml | 15 +++++--- generated/1.30/README.adoc | 38 ++++++++++--------- .../authentication/v1alpha1/types_tls.go | 18 +++++---- .../v1alpha1/zz_generated.deepcopy.go | 10 ++--- .../apis/supervisor/idp/v1alpha1/types_tls.go | 17 +++++---- .../idp/v1alpha1/zz_generated.deepcopy.go | 10 ++--- ...cierge.pinniped.dev_jwtauthenticators.yaml | 15 +++++--- ...ge.pinniped.dev_webhookauthenticators.yaml | 15 +++++--- ....dev_activedirectoryidentityproviders.yaml | 15 +++++--- ....pinniped.dev_githubidentityproviders.yaml | 15 +++++--- ...or.pinniped.dev_ldapidentityproviders.yaml | 15 +++++--- ...or.pinniped.dev_oidcidentityproviders.yaml | 15 +++++--- generated/latest/README.adoc | 38 ++++++++++--------- .../authentication/v1alpha1/types_tls.go | 18 +++++---- .../v1alpha1/zz_generated.deepcopy.go | 10 ++--- .../apis/supervisor/idp/v1alpha1/types_tls.go | 17 +++++---- .../idp/v1alpha1/zz_generated.deepcopy.go | 10 ++--- .../jwtcachefiller/jwtcachefiller_test.go | 4 +- .../webhookcachefiller_test.go | 4 +- .../active_directory_upstream_watcher_test.go | 6 +-- .../github_upstream_watcher_test.go | 8 ++-- .../ldap_upstream_watcher_test.go | 6 +-- .../oidc_upstream_watcher_test.go | 4 +- .../tlsconfigutil/tls_config_util_test.go | 8 ++-- test/integration/concierge_client_test.go | 6 +-- .../concierge_jwtauthenticator_status_test.go | 2 +- ...cierge_webhookauthenticator_status_test.go | 2 +- test/integration/e2e_test.go | 6 +-- test/integration/supervisor_login_test.go | 20 +++++----- 102 files changed, 889 insertions(+), 686 deletions(-) diff --git a/apis/concierge/authentication/v1alpha1/types_tls.go.tmpl b/apis/concierge/authentication/v1alpha1/types_tls.go.tmpl index cc823a05e..883dc7fc7 100644 --- a/apis/concierge/authentication/v1alpha1/types_tls.go.tmpl +++ b/apis/concierge/authentication/v1alpha1/types_tls.go.tmpl @@ -3,28 +3,30 @@ package v1alpha1 -// CABundleSource provides a source for CA bundle used for client-side TLS verification. -type CABundleSource struct { - // Whether the CA bundle is being sourced from a kubernetes secret or a configmap. +// CertificateAuthorityDataSourceSpec provides a source for CA bundle used for client-side TLS verification. +type CertificateAuthorityDataSourceSpec struct { + // Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. // Secrets must be of type kubernetes.io/tls or Opaque. - // For configmaps, the value associated with the key is not expected to be base64 encoded. // +kubebuilder:validation:Enum=Secret;ConfigMap Kind string `json:"kind"` - // Name of the secret or configmap from which to read the CA bundle. + // Name is the resource name of the secret or configmap from which to read the CA bundle. // The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. // +kubebuilder:validation:MinLength=1 Name string `json:"name"` - // Key within the secret or configmap from which to read the CA bundle. + // Key is the key name within the secret or configmap from which to read the CA bundle. + // The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + // certificate bundle. // +kubebuilder:validation:MinLength=1 Key string `json:"key"` } -// Configuration for configuring TLS on various authenticators. +// TLSSpec provides TLS configuration on various authenticators. type TLSSpec struct { // X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. // +optional CertificateAuthorityData string `json:"certificateAuthorityData,omitempty"` // Reference to a CA bundle in a secret or a configmap. + // Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. // +optional - CertificateAuthorityDataSource *CABundleSource `json:"certificateAuthorityDataSource,omitempty"` + CertificateAuthorityDataSource *CertificateAuthorityDataSourceSpec `json:"certificateAuthorityDataSource,omitempty"` } diff --git a/apis/supervisor/idp/v1alpha1/types_tls.go.tmpl b/apis/supervisor/idp/v1alpha1/types_tls.go.tmpl index 19d6d863a..407a5cde5 100644 --- a/apis/supervisor/idp/v1alpha1/types_tls.go.tmpl +++ b/apis/supervisor/idp/v1alpha1/types_tls.go.tmpl @@ -3,19 +3,19 @@ package v1alpha1 - -// CABundleSource provides a source for CA bundle used for client-side TLS verification. -type CABundleSource struct { - // Whether the CA bundle is being sourced from a kubernetes secret or a configmap. +// CertificateAuthorityDataSourceSpec provides a source for CA bundle used for client-side TLS verification. +type CertificateAuthorityDataSourceSpec struct { + // Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. // Secrets must be of type kubernetes.io/tls or Opaque. - // For configmaps, the value associated with the key is not expected to be base64 encoded. // +kubebuilder:validation:Enum=Secret;ConfigMap Kind string `json:"kind"` - // Name of the secret or configmap from which to read the CA bundle. + // Name is the resource name of the secret or configmap from which to read the CA bundle. // The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. // +kubebuilder:validation:MinLength=1 Name string `json:"name"` - // Key within the secret or configmap from which to read the CA bundle. + // Key is the key name within the secret or configmap from which to read the CA bundle. + // The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + // certificate bundle. // +kubebuilder:validation:MinLength=1 Key string `json:"key"` } @@ -26,6 +26,7 @@ type TLSSpec struct { // +optional CertificateAuthorityData string `json:"certificateAuthorityData,omitempty"` // Reference to a CA bundle in a secret or a configmap. + // Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. // +optional - CertificateAuthorityDataSource *CABundleSource `json:"certificateAuthorityDataSource,omitempty"` + CertificateAuthorityDataSource *CertificateAuthorityDataSourceSpec `json:"certificateAuthorityDataSource,omitempty"` } diff --git a/deploy/concierge/authentication.concierge.pinniped.dev_jwtauthenticators.yaml b/deploy/concierge/authentication.concierge.pinniped.dev_jwtauthenticators.yaml index 88a7b3d8b..838f11edf 100644 --- a/deploy/concierge/authentication.concierge.pinniped.dev_jwtauthenticators.yaml +++ b/deploy/concierge/authentication.concierge.pinniped.dev_jwtauthenticators.yaml @@ -96,25 +96,28 @@ spec: If omitted, a default set of system roots will be trusted. type: string certificateAuthorityDataSource: - description: Reference to a CA bundle in a secret or a configmap. + description: |- + Reference to a CA bundle in a secret or a configmap. + Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. properties: key: - description: Key within the secret or configmap from which - to read the CA bundle. + description: |- + Key is the key name within the secret or configmap from which to read the CA bundle. + The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + certificate bundle. minLength: 1 type: string kind: description: |- - Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. - For configmaps, the value associated with the key is not expected to be base64 encoded. enum: - Secret - ConfigMap type: string name: description: |- - Name of the secret or configmap from which to read the CA bundle. + Name is the resource name of the secret or configmap from which to read the CA bundle. The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. minLength: 1 type: string diff --git a/deploy/concierge/authentication.concierge.pinniped.dev_webhookauthenticators.yaml b/deploy/concierge/authentication.concierge.pinniped.dev_webhookauthenticators.yaml index 8576dabbf..0133d62fc 100644 --- a/deploy/concierge/authentication.concierge.pinniped.dev_webhookauthenticators.yaml +++ b/deploy/concierge/authentication.concierge.pinniped.dev_webhookauthenticators.yaml @@ -67,25 +67,28 @@ spec: If omitted, a default set of system roots will be trusted. type: string certificateAuthorityDataSource: - description: Reference to a CA bundle in a secret or a configmap. + description: |- + Reference to a CA bundle in a secret or a configmap. + Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. properties: key: - description: Key within the secret or configmap from which - to read the CA bundle. + description: |- + Key is the key name within the secret or configmap from which to read the CA bundle. + The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + certificate bundle. minLength: 1 type: string kind: description: |- - Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. - For configmaps, the value associated with the key is not expected to be base64 encoded. enum: - Secret - ConfigMap type: string name: description: |- - Name of the secret or configmap from which to read the CA bundle. + Name is the resource name of the secret or configmap from which to read the CA bundle. The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. minLength: 1 type: string diff --git a/deploy/supervisor/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml b/deploy/supervisor/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml index 36d7edcd4..dcc1836b6 100644 --- a/deploy/supervisor/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml +++ b/deploy/supervisor/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml @@ -171,25 +171,28 @@ spec: If omitted, a default set of system roots will be trusted. type: string certificateAuthorityDataSource: - description: Reference to a CA bundle in a secret or a configmap. + description: |- + Reference to a CA bundle in a secret or a configmap. + Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. properties: key: - description: Key within the secret or configmap from which - to read the CA bundle. + description: |- + Key is the key name within the secret or configmap from which to read the CA bundle. + The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + certificate bundle. minLength: 1 type: string kind: description: |- - Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. - For configmaps, the value associated with the key is not expected to be base64 encoded. enum: - Secret - ConfigMap type: string name: description: |- - Name of the secret or configmap from which to read the CA bundle. + Name is the resource name of the secret or configmap from which to read the CA bundle. The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string diff --git a/deploy/supervisor/idp.supervisor.pinniped.dev_githubidentityproviders.yaml b/deploy/supervisor/idp.supervisor.pinniped.dev_githubidentityproviders.yaml index 1a5dea759..669377e7c 100644 --- a/deploy/supervisor/idp.supervisor.pinniped.dev_githubidentityproviders.yaml +++ b/deploy/supervisor/idp.supervisor.pinniped.dev_githubidentityproviders.yaml @@ -226,25 +226,28 @@ spec: be trusted. type: string certificateAuthorityDataSource: - description: Reference to a CA bundle in a secret or a configmap. + description: |- + Reference to a CA bundle in a secret or a configmap. + Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. properties: key: - description: Key within the secret or configmap from which - to read the CA bundle. + description: |- + Key is the key name within the secret or configmap from which to read the CA bundle. + The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + certificate bundle. minLength: 1 type: string kind: description: |- - Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. - For configmaps, the value associated with the key is not expected to be base64 encoded. enum: - Secret - ConfigMap type: string name: description: |- - Name of the secret or configmap from which to read the CA bundle. + Name is the resource name of the secret or configmap from which to read the CA bundle. The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string diff --git a/deploy/supervisor/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml b/deploy/supervisor/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml index bcd798dba..e1fb91934 100644 --- a/deploy/supervisor/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml +++ b/deploy/supervisor/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml @@ -162,25 +162,28 @@ spec: If omitted, a default set of system roots will be trusted. type: string certificateAuthorityDataSource: - description: Reference to a CA bundle in a secret or a configmap. + description: |- + Reference to a CA bundle in a secret or a configmap. + Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. properties: key: - description: Key within the secret or configmap from which - to read the CA bundle. + description: |- + Key is the key name within the secret or configmap from which to read the CA bundle. + The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + certificate bundle. minLength: 1 type: string kind: description: |- - Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. - For configmaps, the value associated with the key is not expected to be base64 encoded. enum: - Secret - ConfigMap type: string name: description: |- - Name of the secret or configmap from which to read the CA bundle. + Name is the resource name of the secret or configmap from which to read the CA bundle. The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string diff --git a/deploy/supervisor/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml b/deploy/supervisor/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml index 330984a89..83ae89781 100644 --- a/deploy/supervisor/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml +++ b/deploy/supervisor/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml @@ -212,25 +212,28 @@ spec: If omitted, a default set of system roots will be trusted. type: string certificateAuthorityDataSource: - description: Reference to a CA bundle in a secret or a configmap. + description: |- + Reference to a CA bundle in a secret or a configmap. + Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. properties: key: - description: Key within the secret or configmap from which - to read the CA bundle. + description: |- + Key is the key name within the secret or configmap from which to read the CA bundle. + The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + certificate bundle. minLength: 1 type: string kind: description: |- - Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. - For configmaps, the value associated with the key is not expected to be base64 encoded. enum: - Secret - ConfigMap type: string name: description: |- - Name of the secret or configmap from which to read the CA bundle. + Name is the resource name of the secret or configmap from which to read the CA bundle. The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string diff --git a/generated/1.24/README.adoc b/generated/1.24/README.adoc index c61e4de01..ef1d03c8b 100644 --- a/generated/1.24/README.adoc +++ b/generated/1.24/README.adoc @@ -23,10 +23,10 @@ Package v1alpha1 is the v1alpha1 version of the Pinniped concierge authenticatio -[id="{anchor_prefix}-go-pinniped-dev-generated-1-24-apis-concierge-authentication-v1alpha1-cabundlesource"] -==== CABundleSource +[id="{anchor_prefix}-go-pinniped-dev-generated-1-24-apis-concierge-authentication-v1alpha1-certificateauthoritydatasourcespec"] +==== CertificateAuthorityDataSourceSpec -CABundleSource provides a source for CA bundle used for client-side TLS verification. +CertificateAuthorityDataSourceSpec provides a source for CA bundle used for client-side TLS verification. .Appears In: **** @@ -36,12 +36,13 @@ CABundleSource provides a source for CA bundle used for client-side TLS verifica [cols="25a,75a", options="header"] |=== | Field | Description -| *`kind`* __string__ | Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + +| *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + -For configmaps, the value associated with the key is not expected to be base64 encoded. + -| *`name`* __string__ | Name of the secret or configmap from which to read the CA bundle. + +| *`name`* __string__ | Name is the resource name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. + -| *`key`* __string__ | Key within the secret or configmap from which to read the CA bundle. + +| *`key`* __string__ | Key is the key name within the secret or configmap from which to read the CA bundle. + +The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + +certificate bundle. + |=== @@ -147,7 +148,7 @@ username from the JWT token. When not specified, it will default to "username". [id="{anchor_prefix}-go-pinniped-dev-generated-1-24-apis-concierge-authentication-v1alpha1-tlsspec"] ==== TLSSpec -Configuration for configuring TLS on various authenticators. +TLSSpec provides TLS configuration on various authenticators. .Appears In: **** @@ -159,7 +160,8 @@ Configuration for configuring TLS on various authenticators. |=== | Field | Description | *`certificateAuthorityData`* __string__ | X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. + -| *`certificateAuthorityDataSource`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-24-apis-concierge-authentication-v1alpha1-cabundlesource[$$CABundleSource$$]__ | Reference to a CA bundle in a secret or a configmap. + +| *`certificateAuthorityDataSource`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-24-apis-concierge-authentication-v1alpha1-certificateauthoritydatasourcespec[$$CertificateAuthorityDataSourceSpec$$]__ | Reference to a CA bundle in a secret or a configmap. + +Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. + |=== @@ -1668,10 +1670,10 @@ Optional, when empty this defaults to "objectGUID". + |=== -[id="{anchor_prefix}-go-pinniped-dev-generated-1-24-apis-supervisor-idp-v1alpha1-cabundlesource"] -==== CABundleSource +[id="{anchor_prefix}-go-pinniped-dev-generated-1-24-apis-supervisor-idp-v1alpha1-certificateauthoritydatasourcespec"] +==== CertificateAuthorityDataSourceSpec -CABundleSource provides a source for CA bundle used for client-side TLS verification. +CertificateAuthorityDataSourceSpec provides a source for CA bundle used for client-side TLS verification. .Appears In: **** @@ -1681,12 +1683,13 @@ CABundleSource provides a source for CA bundle used for client-side TLS verifica [cols="25a,75a", options="header"] |=== | Field | Description -| *`kind`* __string__ | Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + +| *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + -For configmaps, the value associated with the key is not expected to be base64 encoded. + -| *`name`* __string__ | Name of the secret or configmap from which to read the CA bundle. + +| *`name`* __string__ | Name is the resource name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. + -| *`key`* __string__ | Key within the secret or configmap from which to read the CA bundle. + +| *`key`* __string__ | Key is the key name within the secret or configmap from which to read the CA bundle. + +The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + +certificate bundle. + |=== @@ -2446,7 +2449,8 @@ TLSSpec provides TLS configuration for identity provider integration. |=== | Field | Description | *`certificateAuthorityData`* __string__ | X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. + -| *`certificateAuthorityDataSource`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-24-apis-supervisor-idp-v1alpha1-cabundlesource[$$CABundleSource$$]__ | Reference to a CA bundle in a secret or a configmap. + +| *`certificateAuthorityDataSource`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-24-apis-supervisor-idp-v1alpha1-certificateauthoritydatasourcespec[$$CertificateAuthorityDataSourceSpec$$]__ | Reference to a CA bundle in a secret or a configmap. + +Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. + |=== diff --git a/generated/1.24/apis/concierge/authentication/v1alpha1/types_tls.go b/generated/1.24/apis/concierge/authentication/v1alpha1/types_tls.go index cc823a05e..883dc7fc7 100644 --- a/generated/1.24/apis/concierge/authentication/v1alpha1/types_tls.go +++ b/generated/1.24/apis/concierge/authentication/v1alpha1/types_tls.go @@ -3,28 +3,30 @@ package v1alpha1 -// CABundleSource provides a source for CA bundle used for client-side TLS verification. -type CABundleSource struct { - // Whether the CA bundle is being sourced from a kubernetes secret or a configmap. +// CertificateAuthorityDataSourceSpec provides a source for CA bundle used for client-side TLS verification. +type CertificateAuthorityDataSourceSpec struct { + // Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. // Secrets must be of type kubernetes.io/tls or Opaque. - // For configmaps, the value associated with the key is not expected to be base64 encoded. // +kubebuilder:validation:Enum=Secret;ConfigMap Kind string `json:"kind"` - // Name of the secret or configmap from which to read the CA bundle. + // Name is the resource name of the secret or configmap from which to read the CA bundle. // The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. // +kubebuilder:validation:MinLength=1 Name string `json:"name"` - // Key within the secret or configmap from which to read the CA bundle. + // Key is the key name within the secret or configmap from which to read the CA bundle. + // The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + // certificate bundle. // +kubebuilder:validation:MinLength=1 Key string `json:"key"` } -// Configuration for configuring TLS on various authenticators. +// TLSSpec provides TLS configuration on various authenticators. type TLSSpec struct { // X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. // +optional CertificateAuthorityData string `json:"certificateAuthorityData,omitempty"` // Reference to a CA bundle in a secret or a configmap. + // Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. // +optional - CertificateAuthorityDataSource *CABundleSource `json:"certificateAuthorityDataSource,omitempty"` + CertificateAuthorityDataSource *CertificateAuthorityDataSourceSpec `json:"certificateAuthorityDataSource,omitempty"` } diff --git a/generated/1.24/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go b/generated/1.24/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go index 27cbcc844..1e64ee699 100644 --- a/generated/1.24/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go +++ b/generated/1.24/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go @@ -14,17 +14,17 @@ import ( ) // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CABundleSource) DeepCopyInto(out *CABundleSource) { +func (in *CertificateAuthorityDataSourceSpec) DeepCopyInto(out *CertificateAuthorityDataSourceSpec) { *out = *in return } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CABundleSource. -func (in *CABundleSource) DeepCopy() *CABundleSource { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CertificateAuthorityDataSourceSpec. +func (in *CertificateAuthorityDataSourceSpec) DeepCopy() *CertificateAuthorityDataSourceSpec { if in == nil { return nil } - out := new(CABundleSource) + out := new(CertificateAuthorityDataSourceSpec) in.DeepCopyInto(out) return out } @@ -156,7 +156,7 @@ func (in *TLSSpec) DeepCopyInto(out *TLSSpec) { *out = *in if in.CertificateAuthorityDataSource != nil { in, out := &in.CertificateAuthorityDataSource, &out.CertificateAuthorityDataSource - *out = new(CABundleSource) + *out = new(CertificateAuthorityDataSourceSpec) **out = **in } return diff --git a/generated/1.24/apis/supervisor/idp/v1alpha1/types_tls.go b/generated/1.24/apis/supervisor/idp/v1alpha1/types_tls.go index 19d6d863a..407a5cde5 100644 --- a/generated/1.24/apis/supervisor/idp/v1alpha1/types_tls.go +++ b/generated/1.24/apis/supervisor/idp/v1alpha1/types_tls.go @@ -3,19 +3,19 @@ package v1alpha1 - -// CABundleSource provides a source for CA bundle used for client-side TLS verification. -type CABundleSource struct { - // Whether the CA bundle is being sourced from a kubernetes secret or a configmap. +// CertificateAuthorityDataSourceSpec provides a source for CA bundle used for client-side TLS verification. +type CertificateAuthorityDataSourceSpec struct { + // Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. // Secrets must be of type kubernetes.io/tls or Opaque. - // For configmaps, the value associated with the key is not expected to be base64 encoded. // +kubebuilder:validation:Enum=Secret;ConfigMap Kind string `json:"kind"` - // Name of the secret or configmap from which to read the CA bundle. + // Name is the resource name of the secret or configmap from which to read the CA bundle. // The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. // +kubebuilder:validation:MinLength=1 Name string `json:"name"` - // Key within the secret or configmap from which to read the CA bundle. + // Key is the key name within the secret or configmap from which to read the CA bundle. + // The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + // certificate bundle. // +kubebuilder:validation:MinLength=1 Key string `json:"key"` } @@ -26,6 +26,7 @@ type TLSSpec struct { // +optional CertificateAuthorityData string `json:"certificateAuthorityData,omitempty"` // Reference to a CA bundle in a secret or a configmap. + // Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. // +optional - CertificateAuthorityDataSource *CABundleSource `json:"certificateAuthorityDataSource,omitempty"` + CertificateAuthorityDataSource *CertificateAuthorityDataSourceSpec `json:"certificateAuthorityDataSource,omitempty"` } diff --git a/generated/1.24/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go b/generated/1.24/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go index 41d44d226..395c8f0fb 100644 --- a/generated/1.24/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go +++ b/generated/1.24/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go @@ -204,17 +204,17 @@ func (in *ActiveDirectoryIdentityProviderUserSearchAttributes) DeepCopy() *Activ } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CABundleSource) DeepCopyInto(out *CABundleSource) { +func (in *CertificateAuthorityDataSourceSpec) DeepCopyInto(out *CertificateAuthorityDataSourceSpec) { *out = *in return } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CABundleSource. -func (in *CABundleSource) DeepCopy() *CABundleSource { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CertificateAuthorityDataSourceSpec. +func (in *CertificateAuthorityDataSourceSpec) DeepCopy() *CertificateAuthorityDataSourceSpec { if in == nil { return nil } - out := new(CABundleSource) + out := new(CertificateAuthorityDataSourceSpec) in.DeepCopyInto(out) return out } @@ -818,7 +818,7 @@ func (in *TLSSpec) DeepCopyInto(out *TLSSpec) { *out = *in if in.CertificateAuthorityDataSource != nil { in, out := &in.CertificateAuthorityDataSource, &out.CertificateAuthorityDataSource - *out = new(CABundleSource) + *out = new(CertificateAuthorityDataSourceSpec) **out = **in } return diff --git a/generated/1.24/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml b/generated/1.24/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml index 88a7b3d8b..838f11edf 100644 --- a/generated/1.24/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml +++ b/generated/1.24/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml @@ -96,25 +96,28 @@ spec: If omitted, a default set of system roots will be trusted. type: string certificateAuthorityDataSource: - description: Reference to a CA bundle in a secret or a configmap. + description: |- + Reference to a CA bundle in a secret or a configmap. + Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. properties: key: - description: Key within the secret or configmap from which - to read the CA bundle. + description: |- + Key is the key name within the secret or configmap from which to read the CA bundle. + The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + certificate bundle. minLength: 1 type: string kind: description: |- - Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. - For configmaps, the value associated with the key is not expected to be base64 encoded. enum: - Secret - ConfigMap type: string name: description: |- - Name of the secret or configmap from which to read the CA bundle. + Name is the resource name of the secret or configmap from which to read the CA bundle. The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. minLength: 1 type: string diff --git a/generated/1.24/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml b/generated/1.24/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml index 8576dabbf..0133d62fc 100644 --- a/generated/1.24/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml +++ b/generated/1.24/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml @@ -67,25 +67,28 @@ spec: If omitted, a default set of system roots will be trusted. type: string certificateAuthorityDataSource: - description: Reference to a CA bundle in a secret or a configmap. + description: |- + Reference to a CA bundle in a secret or a configmap. + Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. properties: key: - description: Key within the secret or configmap from which - to read the CA bundle. + description: |- + Key is the key name within the secret or configmap from which to read the CA bundle. + The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + certificate bundle. minLength: 1 type: string kind: description: |- - Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. - For configmaps, the value associated with the key is not expected to be base64 encoded. enum: - Secret - ConfigMap type: string name: description: |- - Name of the secret or configmap from which to read the CA bundle. + Name is the resource name of the secret or configmap from which to read the CA bundle. The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. minLength: 1 type: string diff --git a/generated/1.24/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml b/generated/1.24/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml index 36d7edcd4..dcc1836b6 100644 --- a/generated/1.24/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml +++ b/generated/1.24/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml @@ -171,25 +171,28 @@ spec: If omitted, a default set of system roots will be trusted. type: string certificateAuthorityDataSource: - description: Reference to a CA bundle in a secret or a configmap. + description: |- + Reference to a CA bundle in a secret or a configmap. + Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. properties: key: - description: Key within the secret or configmap from which - to read the CA bundle. + description: |- + Key is the key name within the secret or configmap from which to read the CA bundle. + The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + certificate bundle. minLength: 1 type: string kind: description: |- - Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. - For configmaps, the value associated with the key is not expected to be base64 encoded. enum: - Secret - ConfigMap type: string name: description: |- - Name of the secret or configmap from which to read the CA bundle. + Name is the resource name of the secret or configmap from which to read the CA bundle. The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string diff --git a/generated/1.24/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml b/generated/1.24/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml index 1a5dea759..669377e7c 100644 --- a/generated/1.24/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml +++ b/generated/1.24/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml @@ -226,25 +226,28 @@ spec: be trusted. type: string certificateAuthorityDataSource: - description: Reference to a CA bundle in a secret or a configmap. + description: |- + Reference to a CA bundle in a secret or a configmap. + Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. properties: key: - description: Key within the secret or configmap from which - to read the CA bundle. + description: |- + Key is the key name within the secret or configmap from which to read the CA bundle. + The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + certificate bundle. minLength: 1 type: string kind: description: |- - Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. - For configmaps, the value associated with the key is not expected to be base64 encoded. enum: - Secret - ConfigMap type: string name: description: |- - Name of the secret or configmap from which to read the CA bundle. + Name is the resource name of the secret or configmap from which to read the CA bundle. The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string diff --git a/generated/1.24/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml b/generated/1.24/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml index bcd798dba..e1fb91934 100644 --- a/generated/1.24/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml +++ b/generated/1.24/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml @@ -162,25 +162,28 @@ spec: If omitted, a default set of system roots will be trusted. type: string certificateAuthorityDataSource: - description: Reference to a CA bundle in a secret or a configmap. + description: |- + Reference to a CA bundle in a secret or a configmap. + Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. properties: key: - description: Key within the secret or configmap from which - to read the CA bundle. + description: |- + Key is the key name within the secret or configmap from which to read the CA bundle. + The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + certificate bundle. minLength: 1 type: string kind: description: |- - Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. - For configmaps, the value associated with the key is not expected to be base64 encoded. enum: - Secret - ConfigMap type: string name: description: |- - Name of the secret or configmap from which to read the CA bundle. + Name is the resource name of the secret or configmap from which to read the CA bundle. The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string diff --git a/generated/1.24/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml b/generated/1.24/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml index 330984a89..83ae89781 100644 --- a/generated/1.24/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml +++ b/generated/1.24/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml @@ -212,25 +212,28 @@ spec: If omitted, a default set of system roots will be trusted. type: string certificateAuthorityDataSource: - description: Reference to a CA bundle in a secret or a configmap. + description: |- + Reference to a CA bundle in a secret or a configmap. + Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. properties: key: - description: Key within the secret or configmap from which - to read the CA bundle. + description: |- + Key is the key name within the secret or configmap from which to read the CA bundle. + The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + certificate bundle. minLength: 1 type: string kind: description: |- - Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. - For configmaps, the value associated with the key is not expected to be base64 encoded. enum: - Secret - ConfigMap type: string name: description: |- - Name of the secret or configmap from which to read the CA bundle. + Name is the resource name of the secret or configmap from which to read the CA bundle. The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string diff --git a/generated/1.25/README.adoc b/generated/1.25/README.adoc index e876a4f6c..ac3c245ca 100644 --- a/generated/1.25/README.adoc +++ b/generated/1.25/README.adoc @@ -23,10 +23,10 @@ Package v1alpha1 is the v1alpha1 version of the Pinniped concierge authenticatio -[id="{anchor_prefix}-go-pinniped-dev-generated-1-25-apis-concierge-authentication-v1alpha1-cabundlesource"] -==== CABundleSource +[id="{anchor_prefix}-go-pinniped-dev-generated-1-25-apis-concierge-authentication-v1alpha1-certificateauthoritydatasourcespec"] +==== CertificateAuthorityDataSourceSpec -CABundleSource provides a source for CA bundle used for client-side TLS verification. +CertificateAuthorityDataSourceSpec provides a source for CA bundle used for client-side TLS verification. .Appears In: **** @@ -36,12 +36,13 @@ CABundleSource provides a source for CA bundle used for client-side TLS verifica [cols="25a,75a", options="header"] |=== | Field | Description -| *`kind`* __string__ | Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + +| *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + -For configmaps, the value associated with the key is not expected to be base64 encoded. + -| *`name`* __string__ | Name of the secret or configmap from which to read the CA bundle. + +| *`name`* __string__ | Name is the resource name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. + -| *`key`* __string__ | Key within the secret or configmap from which to read the CA bundle. + +| *`key`* __string__ | Key is the key name within the secret or configmap from which to read the CA bundle. + +The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + +certificate bundle. + |=== @@ -147,7 +148,7 @@ username from the JWT token. When not specified, it will default to "username". [id="{anchor_prefix}-go-pinniped-dev-generated-1-25-apis-concierge-authentication-v1alpha1-tlsspec"] ==== TLSSpec -Configuration for configuring TLS on various authenticators. +TLSSpec provides TLS configuration on various authenticators. .Appears In: **** @@ -159,7 +160,8 @@ Configuration for configuring TLS on various authenticators. |=== | Field | Description | *`certificateAuthorityData`* __string__ | X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. + -| *`certificateAuthorityDataSource`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-25-apis-concierge-authentication-v1alpha1-cabundlesource[$$CABundleSource$$]__ | Reference to a CA bundle in a secret or a configmap. + +| *`certificateAuthorityDataSource`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-25-apis-concierge-authentication-v1alpha1-certificateauthoritydatasourcespec[$$CertificateAuthorityDataSourceSpec$$]__ | Reference to a CA bundle in a secret or a configmap. + +Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. + |=== @@ -1668,10 +1670,10 @@ Optional, when empty this defaults to "objectGUID". + |=== -[id="{anchor_prefix}-go-pinniped-dev-generated-1-25-apis-supervisor-idp-v1alpha1-cabundlesource"] -==== CABundleSource +[id="{anchor_prefix}-go-pinniped-dev-generated-1-25-apis-supervisor-idp-v1alpha1-certificateauthoritydatasourcespec"] +==== CertificateAuthorityDataSourceSpec -CABundleSource provides a source for CA bundle used for client-side TLS verification. +CertificateAuthorityDataSourceSpec provides a source for CA bundle used for client-side TLS verification. .Appears In: **** @@ -1681,12 +1683,13 @@ CABundleSource provides a source for CA bundle used for client-side TLS verifica [cols="25a,75a", options="header"] |=== | Field | Description -| *`kind`* __string__ | Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + +| *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + -For configmaps, the value associated with the key is not expected to be base64 encoded. + -| *`name`* __string__ | Name of the secret or configmap from which to read the CA bundle. + +| *`name`* __string__ | Name is the resource name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. + -| *`key`* __string__ | Key within the secret or configmap from which to read the CA bundle. + +| *`key`* __string__ | Key is the key name within the secret or configmap from which to read the CA bundle. + +The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + +certificate bundle. + |=== @@ -2446,7 +2449,8 @@ TLSSpec provides TLS configuration for identity provider integration. |=== | Field | Description | *`certificateAuthorityData`* __string__ | X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. + -| *`certificateAuthorityDataSource`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-25-apis-supervisor-idp-v1alpha1-cabundlesource[$$CABundleSource$$]__ | Reference to a CA bundle in a secret or a configmap. + +| *`certificateAuthorityDataSource`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-25-apis-supervisor-idp-v1alpha1-certificateauthoritydatasourcespec[$$CertificateAuthorityDataSourceSpec$$]__ | Reference to a CA bundle in a secret or a configmap. + +Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. + |=== diff --git a/generated/1.25/apis/concierge/authentication/v1alpha1/types_tls.go b/generated/1.25/apis/concierge/authentication/v1alpha1/types_tls.go index cc823a05e..883dc7fc7 100644 --- a/generated/1.25/apis/concierge/authentication/v1alpha1/types_tls.go +++ b/generated/1.25/apis/concierge/authentication/v1alpha1/types_tls.go @@ -3,28 +3,30 @@ package v1alpha1 -// CABundleSource provides a source for CA bundle used for client-side TLS verification. -type CABundleSource struct { - // Whether the CA bundle is being sourced from a kubernetes secret or a configmap. +// CertificateAuthorityDataSourceSpec provides a source for CA bundle used for client-side TLS verification. +type CertificateAuthorityDataSourceSpec struct { + // Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. // Secrets must be of type kubernetes.io/tls or Opaque. - // For configmaps, the value associated with the key is not expected to be base64 encoded. // +kubebuilder:validation:Enum=Secret;ConfigMap Kind string `json:"kind"` - // Name of the secret or configmap from which to read the CA bundle. + // Name is the resource name of the secret or configmap from which to read the CA bundle. // The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. // +kubebuilder:validation:MinLength=1 Name string `json:"name"` - // Key within the secret or configmap from which to read the CA bundle. + // Key is the key name within the secret or configmap from which to read the CA bundle. + // The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + // certificate bundle. // +kubebuilder:validation:MinLength=1 Key string `json:"key"` } -// Configuration for configuring TLS on various authenticators. +// TLSSpec provides TLS configuration on various authenticators. type TLSSpec struct { // X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. // +optional CertificateAuthorityData string `json:"certificateAuthorityData,omitempty"` // Reference to a CA bundle in a secret or a configmap. + // Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. // +optional - CertificateAuthorityDataSource *CABundleSource `json:"certificateAuthorityDataSource,omitempty"` + CertificateAuthorityDataSource *CertificateAuthorityDataSourceSpec `json:"certificateAuthorityDataSource,omitempty"` } diff --git a/generated/1.25/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go b/generated/1.25/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go index 27cbcc844..1e64ee699 100644 --- a/generated/1.25/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go +++ b/generated/1.25/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go @@ -14,17 +14,17 @@ import ( ) // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CABundleSource) DeepCopyInto(out *CABundleSource) { +func (in *CertificateAuthorityDataSourceSpec) DeepCopyInto(out *CertificateAuthorityDataSourceSpec) { *out = *in return } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CABundleSource. -func (in *CABundleSource) DeepCopy() *CABundleSource { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CertificateAuthorityDataSourceSpec. +func (in *CertificateAuthorityDataSourceSpec) DeepCopy() *CertificateAuthorityDataSourceSpec { if in == nil { return nil } - out := new(CABundleSource) + out := new(CertificateAuthorityDataSourceSpec) in.DeepCopyInto(out) return out } @@ -156,7 +156,7 @@ func (in *TLSSpec) DeepCopyInto(out *TLSSpec) { *out = *in if in.CertificateAuthorityDataSource != nil { in, out := &in.CertificateAuthorityDataSource, &out.CertificateAuthorityDataSource - *out = new(CABundleSource) + *out = new(CertificateAuthorityDataSourceSpec) **out = **in } return diff --git a/generated/1.25/apis/supervisor/idp/v1alpha1/types_tls.go b/generated/1.25/apis/supervisor/idp/v1alpha1/types_tls.go index 19d6d863a..407a5cde5 100644 --- a/generated/1.25/apis/supervisor/idp/v1alpha1/types_tls.go +++ b/generated/1.25/apis/supervisor/idp/v1alpha1/types_tls.go @@ -3,19 +3,19 @@ package v1alpha1 - -// CABundleSource provides a source for CA bundle used for client-side TLS verification. -type CABundleSource struct { - // Whether the CA bundle is being sourced from a kubernetes secret or a configmap. +// CertificateAuthorityDataSourceSpec provides a source for CA bundle used for client-side TLS verification. +type CertificateAuthorityDataSourceSpec struct { + // Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. // Secrets must be of type kubernetes.io/tls or Opaque. - // For configmaps, the value associated with the key is not expected to be base64 encoded. // +kubebuilder:validation:Enum=Secret;ConfigMap Kind string `json:"kind"` - // Name of the secret or configmap from which to read the CA bundle. + // Name is the resource name of the secret or configmap from which to read the CA bundle. // The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. // +kubebuilder:validation:MinLength=1 Name string `json:"name"` - // Key within the secret or configmap from which to read the CA bundle. + // Key is the key name within the secret or configmap from which to read the CA bundle. + // The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + // certificate bundle. // +kubebuilder:validation:MinLength=1 Key string `json:"key"` } @@ -26,6 +26,7 @@ type TLSSpec struct { // +optional CertificateAuthorityData string `json:"certificateAuthorityData,omitempty"` // Reference to a CA bundle in a secret or a configmap. + // Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. // +optional - CertificateAuthorityDataSource *CABundleSource `json:"certificateAuthorityDataSource,omitempty"` + CertificateAuthorityDataSource *CertificateAuthorityDataSourceSpec `json:"certificateAuthorityDataSource,omitempty"` } diff --git a/generated/1.25/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go b/generated/1.25/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go index 41d44d226..395c8f0fb 100644 --- a/generated/1.25/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go +++ b/generated/1.25/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go @@ -204,17 +204,17 @@ func (in *ActiveDirectoryIdentityProviderUserSearchAttributes) DeepCopy() *Activ } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CABundleSource) DeepCopyInto(out *CABundleSource) { +func (in *CertificateAuthorityDataSourceSpec) DeepCopyInto(out *CertificateAuthorityDataSourceSpec) { *out = *in return } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CABundleSource. -func (in *CABundleSource) DeepCopy() *CABundleSource { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CertificateAuthorityDataSourceSpec. +func (in *CertificateAuthorityDataSourceSpec) DeepCopy() *CertificateAuthorityDataSourceSpec { if in == nil { return nil } - out := new(CABundleSource) + out := new(CertificateAuthorityDataSourceSpec) in.DeepCopyInto(out) return out } @@ -818,7 +818,7 @@ func (in *TLSSpec) DeepCopyInto(out *TLSSpec) { *out = *in if in.CertificateAuthorityDataSource != nil { in, out := &in.CertificateAuthorityDataSource, &out.CertificateAuthorityDataSource - *out = new(CABundleSource) + *out = new(CertificateAuthorityDataSourceSpec) **out = **in } return diff --git a/generated/1.25/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml b/generated/1.25/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml index 88a7b3d8b..838f11edf 100644 --- a/generated/1.25/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml +++ b/generated/1.25/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml @@ -96,25 +96,28 @@ spec: If omitted, a default set of system roots will be trusted. type: string certificateAuthorityDataSource: - description: Reference to a CA bundle in a secret or a configmap. + description: |- + Reference to a CA bundle in a secret or a configmap. + Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. properties: key: - description: Key within the secret or configmap from which - to read the CA bundle. + description: |- + Key is the key name within the secret or configmap from which to read the CA bundle. + The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + certificate bundle. minLength: 1 type: string kind: description: |- - Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. - For configmaps, the value associated with the key is not expected to be base64 encoded. enum: - Secret - ConfigMap type: string name: description: |- - Name of the secret or configmap from which to read the CA bundle. + Name is the resource name of the secret or configmap from which to read the CA bundle. The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. minLength: 1 type: string diff --git a/generated/1.25/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml b/generated/1.25/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml index 8576dabbf..0133d62fc 100644 --- a/generated/1.25/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml +++ b/generated/1.25/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml @@ -67,25 +67,28 @@ spec: If omitted, a default set of system roots will be trusted. type: string certificateAuthorityDataSource: - description: Reference to a CA bundle in a secret or a configmap. + description: |- + Reference to a CA bundle in a secret or a configmap. + Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. properties: key: - description: Key within the secret or configmap from which - to read the CA bundle. + description: |- + Key is the key name within the secret or configmap from which to read the CA bundle. + The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + certificate bundle. minLength: 1 type: string kind: description: |- - Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. - For configmaps, the value associated with the key is not expected to be base64 encoded. enum: - Secret - ConfigMap type: string name: description: |- - Name of the secret or configmap from which to read the CA bundle. + Name is the resource name of the secret or configmap from which to read the CA bundle. The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. minLength: 1 type: string diff --git a/generated/1.25/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml b/generated/1.25/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml index 36d7edcd4..dcc1836b6 100644 --- a/generated/1.25/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml +++ b/generated/1.25/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml @@ -171,25 +171,28 @@ spec: If omitted, a default set of system roots will be trusted. type: string certificateAuthorityDataSource: - description: Reference to a CA bundle in a secret or a configmap. + description: |- + Reference to a CA bundle in a secret or a configmap. + Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. properties: key: - description: Key within the secret or configmap from which - to read the CA bundle. + description: |- + Key is the key name within the secret or configmap from which to read the CA bundle. + The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + certificate bundle. minLength: 1 type: string kind: description: |- - Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. - For configmaps, the value associated with the key is not expected to be base64 encoded. enum: - Secret - ConfigMap type: string name: description: |- - Name of the secret or configmap from which to read the CA bundle. + Name is the resource name of the secret or configmap from which to read the CA bundle. The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string diff --git a/generated/1.25/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml b/generated/1.25/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml index 1a5dea759..669377e7c 100644 --- a/generated/1.25/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml +++ b/generated/1.25/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml @@ -226,25 +226,28 @@ spec: be trusted. type: string certificateAuthorityDataSource: - description: Reference to a CA bundle in a secret or a configmap. + description: |- + Reference to a CA bundle in a secret or a configmap. + Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. properties: key: - description: Key within the secret or configmap from which - to read the CA bundle. + description: |- + Key is the key name within the secret or configmap from which to read the CA bundle. + The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + certificate bundle. minLength: 1 type: string kind: description: |- - Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. - For configmaps, the value associated with the key is not expected to be base64 encoded. enum: - Secret - ConfigMap type: string name: description: |- - Name of the secret or configmap from which to read the CA bundle. + Name is the resource name of the secret or configmap from which to read the CA bundle. The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string diff --git a/generated/1.25/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml b/generated/1.25/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml index bcd798dba..e1fb91934 100644 --- a/generated/1.25/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml +++ b/generated/1.25/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml @@ -162,25 +162,28 @@ spec: If omitted, a default set of system roots will be trusted. type: string certificateAuthorityDataSource: - description: Reference to a CA bundle in a secret or a configmap. + description: |- + Reference to a CA bundle in a secret or a configmap. + Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. properties: key: - description: Key within the secret or configmap from which - to read the CA bundle. + description: |- + Key is the key name within the secret or configmap from which to read the CA bundle. + The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + certificate bundle. minLength: 1 type: string kind: description: |- - Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. - For configmaps, the value associated with the key is not expected to be base64 encoded. enum: - Secret - ConfigMap type: string name: description: |- - Name of the secret or configmap from which to read the CA bundle. + Name is the resource name of the secret or configmap from which to read the CA bundle. The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string diff --git a/generated/1.25/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml b/generated/1.25/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml index 330984a89..83ae89781 100644 --- a/generated/1.25/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml +++ b/generated/1.25/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml @@ -212,25 +212,28 @@ spec: If omitted, a default set of system roots will be trusted. type: string certificateAuthorityDataSource: - description: Reference to a CA bundle in a secret or a configmap. + description: |- + Reference to a CA bundle in a secret or a configmap. + Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. properties: key: - description: Key within the secret or configmap from which - to read the CA bundle. + description: |- + Key is the key name within the secret or configmap from which to read the CA bundle. + The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + certificate bundle. minLength: 1 type: string kind: description: |- - Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. - For configmaps, the value associated with the key is not expected to be base64 encoded. enum: - Secret - ConfigMap type: string name: description: |- - Name of the secret or configmap from which to read the CA bundle. + Name is the resource name of the secret or configmap from which to read the CA bundle. The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string diff --git a/generated/1.26/README.adoc b/generated/1.26/README.adoc index 0a22ec4c3..22635e322 100644 --- a/generated/1.26/README.adoc +++ b/generated/1.26/README.adoc @@ -23,10 +23,10 @@ Package v1alpha1 is the v1alpha1 version of the Pinniped concierge authenticatio -[id="{anchor_prefix}-go-pinniped-dev-generated-1-26-apis-concierge-authentication-v1alpha1-cabundlesource"] -==== CABundleSource +[id="{anchor_prefix}-go-pinniped-dev-generated-1-26-apis-concierge-authentication-v1alpha1-certificateauthoritydatasourcespec"] +==== CertificateAuthorityDataSourceSpec -CABundleSource provides a source for CA bundle used for client-side TLS verification. +CertificateAuthorityDataSourceSpec provides a source for CA bundle used for client-side TLS verification. .Appears In: **** @@ -36,12 +36,13 @@ CABundleSource provides a source for CA bundle used for client-side TLS verifica [cols="25a,75a", options="header"] |=== | Field | Description -| *`kind`* __string__ | Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + +| *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + -For configmaps, the value associated with the key is not expected to be base64 encoded. + -| *`name`* __string__ | Name of the secret or configmap from which to read the CA bundle. + +| *`name`* __string__ | Name is the resource name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. + -| *`key`* __string__ | Key within the secret or configmap from which to read the CA bundle. + +| *`key`* __string__ | Key is the key name within the secret or configmap from which to read the CA bundle. + +The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + +certificate bundle. + |=== @@ -147,7 +148,7 @@ username from the JWT token. When not specified, it will default to "username". [id="{anchor_prefix}-go-pinniped-dev-generated-1-26-apis-concierge-authentication-v1alpha1-tlsspec"] ==== TLSSpec -Configuration for configuring TLS on various authenticators. +TLSSpec provides TLS configuration on various authenticators. .Appears In: **** @@ -159,7 +160,8 @@ Configuration for configuring TLS on various authenticators. |=== | Field | Description | *`certificateAuthorityData`* __string__ | X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. + -| *`certificateAuthorityDataSource`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-26-apis-concierge-authentication-v1alpha1-cabundlesource[$$CABundleSource$$]__ | Reference to a CA bundle in a secret or a configmap. + +| *`certificateAuthorityDataSource`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-26-apis-concierge-authentication-v1alpha1-certificateauthoritydatasourcespec[$$CertificateAuthorityDataSourceSpec$$]__ | Reference to a CA bundle in a secret or a configmap. + +Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. + |=== @@ -1668,10 +1670,10 @@ Optional, when empty this defaults to "objectGUID". + |=== -[id="{anchor_prefix}-go-pinniped-dev-generated-1-26-apis-supervisor-idp-v1alpha1-cabundlesource"] -==== CABundleSource +[id="{anchor_prefix}-go-pinniped-dev-generated-1-26-apis-supervisor-idp-v1alpha1-certificateauthoritydatasourcespec"] +==== CertificateAuthorityDataSourceSpec -CABundleSource provides a source for CA bundle used for client-side TLS verification. +CertificateAuthorityDataSourceSpec provides a source for CA bundle used for client-side TLS verification. .Appears In: **** @@ -1681,12 +1683,13 @@ CABundleSource provides a source for CA bundle used for client-side TLS verifica [cols="25a,75a", options="header"] |=== | Field | Description -| *`kind`* __string__ | Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + +| *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + -For configmaps, the value associated with the key is not expected to be base64 encoded. + -| *`name`* __string__ | Name of the secret or configmap from which to read the CA bundle. + +| *`name`* __string__ | Name is the resource name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. + -| *`key`* __string__ | Key within the secret or configmap from which to read the CA bundle. + +| *`key`* __string__ | Key is the key name within the secret or configmap from which to read the CA bundle. + +The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + +certificate bundle. + |=== @@ -2446,7 +2449,8 @@ TLSSpec provides TLS configuration for identity provider integration. |=== | Field | Description | *`certificateAuthorityData`* __string__ | X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. + -| *`certificateAuthorityDataSource`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-26-apis-supervisor-idp-v1alpha1-cabundlesource[$$CABundleSource$$]__ | Reference to a CA bundle in a secret or a configmap. + +| *`certificateAuthorityDataSource`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-26-apis-supervisor-idp-v1alpha1-certificateauthoritydatasourcespec[$$CertificateAuthorityDataSourceSpec$$]__ | Reference to a CA bundle in a secret or a configmap. + +Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. + |=== diff --git a/generated/1.26/apis/concierge/authentication/v1alpha1/types_tls.go b/generated/1.26/apis/concierge/authentication/v1alpha1/types_tls.go index cc823a05e..883dc7fc7 100644 --- a/generated/1.26/apis/concierge/authentication/v1alpha1/types_tls.go +++ b/generated/1.26/apis/concierge/authentication/v1alpha1/types_tls.go @@ -3,28 +3,30 @@ package v1alpha1 -// CABundleSource provides a source for CA bundle used for client-side TLS verification. -type CABundleSource struct { - // Whether the CA bundle is being sourced from a kubernetes secret or a configmap. +// CertificateAuthorityDataSourceSpec provides a source for CA bundle used for client-side TLS verification. +type CertificateAuthorityDataSourceSpec struct { + // Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. // Secrets must be of type kubernetes.io/tls or Opaque. - // For configmaps, the value associated with the key is not expected to be base64 encoded. // +kubebuilder:validation:Enum=Secret;ConfigMap Kind string `json:"kind"` - // Name of the secret or configmap from which to read the CA bundle. + // Name is the resource name of the secret or configmap from which to read the CA bundle. // The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. // +kubebuilder:validation:MinLength=1 Name string `json:"name"` - // Key within the secret or configmap from which to read the CA bundle. + // Key is the key name within the secret or configmap from which to read the CA bundle. + // The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + // certificate bundle. // +kubebuilder:validation:MinLength=1 Key string `json:"key"` } -// Configuration for configuring TLS on various authenticators. +// TLSSpec provides TLS configuration on various authenticators. type TLSSpec struct { // X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. // +optional CertificateAuthorityData string `json:"certificateAuthorityData,omitempty"` // Reference to a CA bundle in a secret or a configmap. + // Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. // +optional - CertificateAuthorityDataSource *CABundleSource `json:"certificateAuthorityDataSource,omitempty"` + CertificateAuthorityDataSource *CertificateAuthorityDataSourceSpec `json:"certificateAuthorityDataSource,omitempty"` } diff --git a/generated/1.26/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go b/generated/1.26/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go index 27cbcc844..1e64ee699 100644 --- a/generated/1.26/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go +++ b/generated/1.26/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go @@ -14,17 +14,17 @@ import ( ) // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CABundleSource) DeepCopyInto(out *CABundleSource) { +func (in *CertificateAuthorityDataSourceSpec) DeepCopyInto(out *CertificateAuthorityDataSourceSpec) { *out = *in return } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CABundleSource. -func (in *CABundleSource) DeepCopy() *CABundleSource { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CertificateAuthorityDataSourceSpec. +func (in *CertificateAuthorityDataSourceSpec) DeepCopy() *CertificateAuthorityDataSourceSpec { if in == nil { return nil } - out := new(CABundleSource) + out := new(CertificateAuthorityDataSourceSpec) in.DeepCopyInto(out) return out } @@ -156,7 +156,7 @@ func (in *TLSSpec) DeepCopyInto(out *TLSSpec) { *out = *in if in.CertificateAuthorityDataSource != nil { in, out := &in.CertificateAuthorityDataSource, &out.CertificateAuthorityDataSource - *out = new(CABundleSource) + *out = new(CertificateAuthorityDataSourceSpec) **out = **in } return diff --git a/generated/1.26/apis/supervisor/idp/v1alpha1/types_tls.go b/generated/1.26/apis/supervisor/idp/v1alpha1/types_tls.go index 19d6d863a..407a5cde5 100644 --- a/generated/1.26/apis/supervisor/idp/v1alpha1/types_tls.go +++ b/generated/1.26/apis/supervisor/idp/v1alpha1/types_tls.go @@ -3,19 +3,19 @@ package v1alpha1 - -// CABundleSource provides a source for CA bundle used for client-side TLS verification. -type CABundleSource struct { - // Whether the CA bundle is being sourced from a kubernetes secret or a configmap. +// CertificateAuthorityDataSourceSpec provides a source for CA bundle used for client-side TLS verification. +type CertificateAuthorityDataSourceSpec struct { + // Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. // Secrets must be of type kubernetes.io/tls or Opaque. - // For configmaps, the value associated with the key is not expected to be base64 encoded. // +kubebuilder:validation:Enum=Secret;ConfigMap Kind string `json:"kind"` - // Name of the secret or configmap from which to read the CA bundle. + // Name is the resource name of the secret or configmap from which to read the CA bundle. // The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. // +kubebuilder:validation:MinLength=1 Name string `json:"name"` - // Key within the secret or configmap from which to read the CA bundle. + // Key is the key name within the secret or configmap from which to read the CA bundle. + // The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + // certificate bundle. // +kubebuilder:validation:MinLength=1 Key string `json:"key"` } @@ -26,6 +26,7 @@ type TLSSpec struct { // +optional CertificateAuthorityData string `json:"certificateAuthorityData,omitempty"` // Reference to a CA bundle in a secret or a configmap. + // Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. // +optional - CertificateAuthorityDataSource *CABundleSource `json:"certificateAuthorityDataSource,omitempty"` + CertificateAuthorityDataSource *CertificateAuthorityDataSourceSpec `json:"certificateAuthorityDataSource,omitempty"` } diff --git a/generated/1.26/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go b/generated/1.26/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go index 41d44d226..395c8f0fb 100644 --- a/generated/1.26/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go +++ b/generated/1.26/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go @@ -204,17 +204,17 @@ func (in *ActiveDirectoryIdentityProviderUserSearchAttributes) DeepCopy() *Activ } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CABundleSource) DeepCopyInto(out *CABundleSource) { +func (in *CertificateAuthorityDataSourceSpec) DeepCopyInto(out *CertificateAuthorityDataSourceSpec) { *out = *in return } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CABundleSource. -func (in *CABundleSource) DeepCopy() *CABundleSource { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CertificateAuthorityDataSourceSpec. +func (in *CertificateAuthorityDataSourceSpec) DeepCopy() *CertificateAuthorityDataSourceSpec { if in == nil { return nil } - out := new(CABundleSource) + out := new(CertificateAuthorityDataSourceSpec) in.DeepCopyInto(out) return out } @@ -818,7 +818,7 @@ func (in *TLSSpec) DeepCopyInto(out *TLSSpec) { *out = *in if in.CertificateAuthorityDataSource != nil { in, out := &in.CertificateAuthorityDataSource, &out.CertificateAuthorityDataSource - *out = new(CABundleSource) + *out = new(CertificateAuthorityDataSourceSpec) **out = **in } return diff --git a/generated/1.26/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml b/generated/1.26/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml index 88a7b3d8b..838f11edf 100644 --- a/generated/1.26/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml +++ b/generated/1.26/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml @@ -96,25 +96,28 @@ spec: If omitted, a default set of system roots will be trusted. type: string certificateAuthorityDataSource: - description: Reference to a CA bundle in a secret or a configmap. + description: |- + Reference to a CA bundle in a secret or a configmap. + Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. properties: key: - description: Key within the secret or configmap from which - to read the CA bundle. + description: |- + Key is the key name within the secret or configmap from which to read the CA bundle. + The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + certificate bundle. minLength: 1 type: string kind: description: |- - Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. - For configmaps, the value associated with the key is not expected to be base64 encoded. enum: - Secret - ConfigMap type: string name: description: |- - Name of the secret or configmap from which to read the CA bundle. + Name is the resource name of the secret or configmap from which to read the CA bundle. The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. minLength: 1 type: string diff --git a/generated/1.26/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml b/generated/1.26/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml index 8576dabbf..0133d62fc 100644 --- a/generated/1.26/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml +++ b/generated/1.26/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml @@ -67,25 +67,28 @@ spec: If omitted, a default set of system roots will be trusted. type: string certificateAuthorityDataSource: - description: Reference to a CA bundle in a secret or a configmap. + description: |- + Reference to a CA bundle in a secret or a configmap. + Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. properties: key: - description: Key within the secret or configmap from which - to read the CA bundle. + description: |- + Key is the key name within the secret or configmap from which to read the CA bundle. + The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + certificate bundle. minLength: 1 type: string kind: description: |- - Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. - For configmaps, the value associated with the key is not expected to be base64 encoded. enum: - Secret - ConfigMap type: string name: description: |- - Name of the secret or configmap from which to read the CA bundle. + Name is the resource name of the secret or configmap from which to read the CA bundle. The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. minLength: 1 type: string diff --git a/generated/1.26/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml b/generated/1.26/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml index 36d7edcd4..dcc1836b6 100644 --- a/generated/1.26/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml +++ b/generated/1.26/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml @@ -171,25 +171,28 @@ spec: If omitted, a default set of system roots will be trusted. type: string certificateAuthorityDataSource: - description: Reference to a CA bundle in a secret or a configmap. + description: |- + Reference to a CA bundle in a secret or a configmap. + Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. properties: key: - description: Key within the secret or configmap from which - to read the CA bundle. + description: |- + Key is the key name within the secret or configmap from which to read the CA bundle. + The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + certificate bundle. minLength: 1 type: string kind: description: |- - Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. - For configmaps, the value associated with the key is not expected to be base64 encoded. enum: - Secret - ConfigMap type: string name: description: |- - Name of the secret or configmap from which to read the CA bundle. + Name is the resource name of the secret or configmap from which to read the CA bundle. The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string diff --git a/generated/1.26/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml b/generated/1.26/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml index 1a5dea759..669377e7c 100644 --- a/generated/1.26/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml +++ b/generated/1.26/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml @@ -226,25 +226,28 @@ spec: be trusted. type: string certificateAuthorityDataSource: - description: Reference to a CA bundle in a secret or a configmap. + description: |- + Reference to a CA bundle in a secret or a configmap. + Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. properties: key: - description: Key within the secret or configmap from which - to read the CA bundle. + description: |- + Key is the key name within the secret or configmap from which to read the CA bundle. + The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + certificate bundle. minLength: 1 type: string kind: description: |- - Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. - For configmaps, the value associated with the key is not expected to be base64 encoded. enum: - Secret - ConfigMap type: string name: description: |- - Name of the secret or configmap from which to read the CA bundle. + Name is the resource name of the secret or configmap from which to read the CA bundle. The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string diff --git a/generated/1.26/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml b/generated/1.26/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml index bcd798dba..e1fb91934 100644 --- a/generated/1.26/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml +++ b/generated/1.26/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml @@ -162,25 +162,28 @@ spec: If omitted, a default set of system roots will be trusted. type: string certificateAuthorityDataSource: - description: Reference to a CA bundle in a secret or a configmap. + description: |- + Reference to a CA bundle in a secret or a configmap. + Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. properties: key: - description: Key within the secret or configmap from which - to read the CA bundle. + description: |- + Key is the key name within the secret or configmap from which to read the CA bundle. + The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + certificate bundle. minLength: 1 type: string kind: description: |- - Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. - For configmaps, the value associated with the key is not expected to be base64 encoded. enum: - Secret - ConfigMap type: string name: description: |- - Name of the secret or configmap from which to read the CA bundle. + Name is the resource name of the secret or configmap from which to read the CA bundle. The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string diff --git a/generated/1.26/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml b/generated/1.26/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml index 330984a89..83ae89781 100644 --- a/generated/1.26/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml +++ b/generated/1.26/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml @@ -212,25 +212,28 @@ spec: If omitted, a default set of system roots will be trusted. type: string certificateAuthorityDataSource: - description: Reference to a CA bundle in a secret or a configmap. + description: |- + Reference to a CA bundle in a secret or a configmap. + Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. properties: key: - description: Key within the secret or configmap from which - to read the CA bundle. + description: |- + Key is the key name within the secret or configmap from which to read the CA bundle. + The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + certificate bundle. minLength: 1 type: string kind: description: |- - Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. - For configmaps, the value associated with the key is not expected to be base64 encoded. enum: - Secret - ConfigMap type: string name: description: |- - Name of the secret or configmap from which to read the CA bundle. + Name is the resource name of the secret or configmap from which to read the CA bundle. The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string diff --git a/generated/1.27/README.adoc b/generated/1.27/README.adoc index 2a1868652..4408cc279 100644 --- a/generated/1.27/README.adoc +++ b/generated/1.27/README.adoc @@ -23,10 +23,10 @@ Package v1alpha1 is the v1alpha1 version of the Pinniped concierge authenticatio -[id="{anchor_prefix}-go-pinniped-dev-generated-1-27-apis-concierge-authentication-v1alpha1-cabundlesource"] -==== CABundleSource +[id="{anchor_prefix}-go-pinniped-dev-generated-1-27-apis-concierge-authentication-v1alpha1-certificateauthoritydatasourcespec"] +==== CertificateAuthorityDataSourceSpec -CABundleSource provides a source for CA bundle used for client-side TLS verification. +CertificateAuthorityDataSourceSpec provides a source for CA bundle used for client-side TLS verification. .Appears In: **** @@ -36,12 +36,13 @@ CABundleSource provides a source for CA bundle used for client-side TLS verifica [cols="25a,75a", options="header"] |=== | Field | Description -| *`kind`* __string__ | Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + +| *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + -For configmaps, the value associated with the key is not expected to be base64 encoded. + -| *`name`* __string__ | Name of the secret or configmap from which to read the CA bundle. + +| *`name`* __string__ | Name is the resource name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. + -| *`key`* __string__ | Key within the secret or configmap from which to read the CA bundle. + +| *`key`* __string__ | Key is the key name within the secret or configmap from which to read the CA bundle. + +The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + +certificate bundle. + |=== @@ -147,7 +148,7 @@ username from the JWT token. When not specified, it will default to "username". [id="{anchor_prefix}-go-pinniped-dev-generated-1-27-apis-concierge-authentication-v1alpha1-tlsspec"] ==== TLSSpec -Configuration for configuring TLS on various authenticators. +TLSSpec provides TLS configuration on various authenticators. .Appears In: **** @@ -159,7 +160,8 @@ Configuration for configuring TLS on various authenticators. |=== | Field | Description | *`certificateAuthorityData`* __string__ | X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. + -| *`certificateAuthorityDataSource`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-27-apis-concierge-authentication-v1alpha1-cabundlesource[$$CABundleSource$$]__ | Reference to a CA bundle in a secret or a configmap. + +| *`certificateAuthorityDataSource`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-27-apis-concierge-authentication-v1alpha1-certificateauthoritydatasourcespec[$$CertificateAuthorityDataSourceSpec$$]__ | Reference to a CA bundle in a secret or a configmap. + +Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. + |=== @@ -1668,10 +1670,10 @@ Optional, when empty this defaults to "objectGUID". + |=== -[id="{anchor_prefix}-go-pinniped-dev-generated-1-27-apis-supervisor-idp-v1alpha1-cabundlesource"] -==== CABundleSource +[id="{anchor_prefix}-go-pinniped-dev-generated-1-27-apis-supervisor-idp-v1alpha1-certificateauthoritydatasourcespec"] +==== CertificateAuthorityDataSourceSpec -CABundleSource provides a source for CA bundle used for client-side TLS verification. +CertificateAuthorityDataSourceSpec provides a source for CA bundle used for client-side TLS verification. .Appears In: **** @@ -1681,12 +1683,13 @@ CABundleSource provides a source for CA bundle used for client-side TLS verifica [cols="25a,75a", options="header"] |=== | Field | Description -| *`kind`* __string__ | Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + +| *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + -For configmaps, the value associated with the key is not expected to be base64 encoded. + -| *`name`* __string__ | Name of the secret or configmap from which to read the CA bundle. + +| *`name`* __string__ | Name is the resource name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. + -| *`key`* __string__ | Key within the secret or configmap from which to read the CA bundle. + +| *`key`* __string__ | Key is the key name within the secret or configmap from which to read the CA bundle. + +The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + +certificate bundle. + |=== @@ -2446,7 +2449,8 @@ TLSSpec provides TLS configuration for identity provider integration. |=== | Field | Description | *`certificateAuthorityData`* __string__ | X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. + -| *`certificateAuthorityDataSource`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-27-apis-supervisor-idp-v1alpha1-cabundlesource[$$CABundleSource$$]__ | Reference to a CA bundle in a secret or a configmap. + +| *`certificateAuthorityDataSource`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-27-apis-supervisor-idp-v1alpha1-certificateauthoritydatasourcespec[$$CertificateAuthorityDataSourceSpec$$]__ | Reference to a CA bundle in a secret or a configmap. + +Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. + |=== diff --git a/generated/1.27/apis/concierge/authentication/v1alpha1/types_tls.go b/generated/1.27/apis/concierge/authentication/v1alpha1/types_tls.go index cc823a05e..883dc7fc7 100644 --- a/generated/1.27/apis/concierge/authentication/v1alpha1/types_tls.go +++ b/generated/1.27/apis/concierge/authentication/v1alpha1/types_tls.go @@ -3,28 +3,30 @@ package v1alpha1 -// CABundleSource provides a source for CA bundle used for client-side TLS verification. -type CABundleSource struct { - // Whether the CA bundle is being sourced from a kubernetes secret or a configmap. +// CertificateAuthorityDataSourceSpec provides a source for CA bundle used for client-side TLS verification. +type CertificateAuthorityDataSourceSpec struct { + // Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. // Secrets must be of type kubernetes.io/tls or Opaque. - // For configmaps, the value associated with the key is not expected to be base64 encoded. // +kubebuilder:validation:Enum=Secret;ConfigMap Kind string `json:"kind"` - // Name of the secret or configmap from which to read the CA bundle. + // Name is the resource name of the secret or configmap from which to read the CA bundle. // The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. // +kubebuilder:validation:MinLength=1 Name string `json:"name"` - // Key within the secret or configmap from which to read the CA bundle. + // Key is the key name within the secret or configmap from which to read the CA bundle. + // The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + // certificate bundle. // +kubebuilder:validation:MinLength=1 Key string `json:"key"` } -// Configuration for configuring TLS on various authenticators. +// TLSSpec provides TLS configuration on various authenticators. type TLSSpec struct { // X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. // +optional CertificateAuthorityData string `json:"certificateAuthorityData,omitempty"` // Reference to a CA bundle in a secret or a configmap. + // Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. // +optional - CertificateAuthorityDataSource *CABundleSource `json:"certificateAuthorityDataSource,omitempty"` + CertificateAuthorityDataSource *CertificateAuthorityDataSourceSpec `json:"certificateAuthorityDataSource,omitempty"` } diff --git a/generated/1.27/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go b/generated/1.27/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go index 27cbcc844..1e64ee699 100644 --- a/generated/1.27/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go +++ b/generated/1.27/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go @@ -14,17 +14,17 @@ import ( ) // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CABundleSource) DeepCopyInto(out *CABundleSource) { +func (in *CertificateAuthorityDataSourceSpec) DeepCopyInto(out *CertificateAuthorityDataSourceSpec) { *out = *in return } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CABundleSource. -func (in *CABundleSource) DeepCopy() *CABundleSource { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CertificateAuthorityDataSourceSpec. +func (in *CertificateAuthorityDataSourceSpec) DeepCopy() *CertificateAuthorityDataSourceSpec { if in == nil { return nil } - out := new(CABundleSource) + out := new(CertificateAuthorityDataSourceSpec) in.DeepCopyInto(out) return out } @@ -156,7 +156,7 @@ func (in *TLSSpec) DeepCopyInto(out *TLSSpec) { *out = *in if in.CertificateAuthorityDataSource != nil { in, out := &in.CertificateAuthorityDataSource, &out.CertificateAuthorityDataSource - *out = new(CABundleSource) + *out = new(CertificateAuthorityDataSourceSpec) **out = **in } return diff --git a/generated/1.27/apis/supervisor/idp/v1alpha1/types_tls.go b/generated/1.27/apis/supervisor/idp/v1alpha1/types_tls.go index 19d6d863a..407a5cde5 100644 --- a/generated/1.27/apis/supervisor/idp/v1alpha1/types_tls.go +++ b/generated/1.27/apis/supervisor/idp/v1alpha1/types_tls.go @@ -3,19 +3,19 @@ package v1alpha1 - -// CABundleSource provides a source for CA bundle used for client-side TLS verification. -type CABundleSource struct { - // Whether the CA bundle is being sourced from a kubernetes secret or a configmap. +// CertificateAuthorityDataSourceSpec provides a source for CA bundle used for client-side TLS verification. +type CertificateAuthorityDataSourceSpec struct { + // Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. // Secrets must be of type kubernetes.io/tls or Opaque. - // For configmaps, the value associated with the key is not expected to be base64 encoded. // +kubebuilder:validation:Enum=Secret;ConfigMap Kind string `json:"kind"` - // Name of the secret or configmap from which to read the CA bundle. + // Name is the resource name of the secret or configmap from which to read the CA bundle. // The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. // +kubebuilder:validation:MinLength=1 Name string `json:"name"` - // Key within the secret or configmap from which to read the CA bundle. + // Key is the key name within the secret or configmap from which to read the CA bundle. + // The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + // certificate bundle. // +kubebuilder:validation:MinLength=1 Key string `json:"key"` } @@ -26,6 +26,7 @@ type TLSSpec struct { // +optional CertificateAuthorityData string `json:"certificateAuthorityData,omitempty"` // Reference to a CA bundle in a secret or a configmap. + // Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. // +optional - CertificateAuthorityDataSource *CABundleSource `json:"certificateAuthorityDataSource,omitempty"` + CertificateAuthorityDataSource *CertificateAuthorityDataSourceSpec `json:"certificateAuthorityDataSource,omitempty"` } diff --git a/generated/1.27/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go b/generated/1.27/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go index 41d44d226..395c8f0fb 100644 --- a/generated/1.27/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go +++ b/generated/1.27/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go @@ -204,17 +204,17 @@ func (in *ActiveDirectoryIdentityProviderUserSearchAttributes) DeepCopy() *Activ } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CABundleSource) DeepCopyInto(out *CABundleSource) { +func (in *CertificateAuthorityDataSourceSpec) DeepCopyInto(out *CertificateAuthorityDataSourceSpec) { *out = *in return } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CABundleSource. -func (in *CABundleSource) DeepCopy() *CABundleSource { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CertificateAuthorityDataSourceSpec. +func (in *CertificateAuthorityDataSourceSpec) DeepCopy() *CertificateAuthorityDataSourceSpec { if in == nil { return nil } - out := new(CABundleSource) + out := new(CertificateAuthorityDataSourceSpec) in.DeepCopyInto(out) return out } @@ -818,7 +818,7 @@ func (in *TLSSpec) DeepCopyInto(out *TLSSpec) { *out = *in if in.CertificateAuthorityDataSource != nil { in, out := &in.CertificateAuthorityDataSource, &out.CertificateAuthorityDataSource - *out = new(CABundleSource) + *out = new(CertificateAuthorityDataSourceSpec) **out = **in } return diff --git a/generated/1.27/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml b/generated/1.27/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml index 88a7b3d8b..838f11edf 100644 --- a/generated/1.27/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml +++ b/generated/1.27/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml @@ -96,25 +96,28 @@ spec: If omitted, a default set of system roots will be trusted. type: string certificateAuthorityDataSource: - description: Reference to a CA bundle in a secret or a configmap. + description: |- + Reference to a CA bundle in a secret or a configmap. + Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. properties: key: - description: Key within the secret or configmap from which - to read the CA bundle. + description: |- + Key is the key name within the secret or configmap from which to read the CA bundle. + The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + certificate bundle. minLength: 1 type: string kind: description: |- - Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. - For configmaps, the value associated with the key is not expected to be base64 encoded. enum: - Secret - ConfigMap type: string name: description: |- - Name of the secret or configmap from which to read the CA bundle. + Name is the resource name of the secret or configmap from which to read the CA bundle. The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. minLength: 1 type: string diff --git a/generated/1.27/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml b/generated/1.27/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml index 8576dabbf..0133d62fc 100644 --- a/generated/1.27/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml +++ b/generated/1.27/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml @@ -67,25 +67,28 @@ spec: If omitted, a default set of system roots will be trusted. type: string certificateAuthorityDataSource: - description: Reference to a CA bundle in a secret or a configmap. + description: |- + Reference to a CA bundle in a secret or a configmap. + Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. properties: key: - description: Key within the secret or configmap from which - to read the CA bundle. + description: |- + Key is the key name within the secret or configmap from which to read the CA bundle. + The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + certificate bundle. minLength: 1 type: string kind: description: |- - Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. - For configmaps, the value associated with the key is not expected to be base64 encoded. enum: - Secret - ConfigMap type: string name: description: |- - Name of the secret or configmap from which to read the CA bundle. + Name is the resource name of the secret or configmap from which to read the CA bundle. The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. minLength: 1 type: string diff --git a/generated/1.27/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml b/generated/1.27/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml index 36d7edcd4..dcc1836b6 100644 --- a/generated/1.27/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml +++ b/generated/1.27/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml @@ -171,25 +171,28 @@ spec: If omitted, a default set of system roots will be trusted. type: string certificateAuthorityDataSource: - description: Reference to a CA bundle in a secret or a configmap. + description: |- + Reference to a CA bundle in a secret or a configmap. + Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. properties: key: - description: Key within the secret or configmap from which - to read the CA bundle. + description: |- + Key is the key name within the secret or configmap from which to read the CA bundle. + The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + certificate bundle. minLength: 1 type: string kind: description: |- - Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. - For configmaps, the value associated with the key is not expected to be base64 encoded. enum: - Secret - ConfigMap type: string name: description: |- - Name of the secret or configmap from which to read the CA bundle. + Name is the resource name of the secret or configmap from which to read the CA bundle. The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string diff --git a/generated/1.27/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml b/generated/1.27/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml index 1a5dea759..669377e7c 100644 --- a/generated/1.27/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml +++ b/generated/1.27/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml @@ -226,25 +226,28 @@ spec: be trusted. type: string certificateAuthorityDataSource: - description: Reference to a CA bundle in a secret or a configmap. + description: |- + Reference to a CA bundle in a secret or a configmap. + Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. properties: key: - description: Key within the secret or configmap from which - to read the CA bundle. + description: |- + Key is the key name within the secret or configmap from which to read the CA bundle. + The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + certificate bundle. minLength: 1 type: string kind: description: |- - Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. - For configmaps, the value associated with the key is not expected to be base64 encoded. enum: - Secret - ConfigMap type: string name: description: |- - Name of the secret or configmap from which to read the CA bundle. + Name is the resource name of the secret or configmap from which to read the CA bundle. The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string diff --git a/generated/1.27/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml b/generated/1.27/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml index bcd798dba..e1fb91934 100644 --- a/generated/1.27/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml +++ b/generated/1.27/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml @@ -162,25 +162,28 @@ spec: If omitted, a default set of system roots will be trusted. type: string certificateAuthorityDataSource: - description: Reference to a CA bundle in a secret or a configmap. + description: |- + Reference to a CA bundle in a secret or a configmap. + Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. properties: key: - description: Key within the secret or configmap from which - to read the CA bundle. + description: |- + Key is the key name within the secret or configmap from which to read the CA bundle. + The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + certificate bundle. minLength: 1 type: string kind: description: |- - Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. - For configmaps, the value associated with the key is not expected to be base64 encoded. enum: - Secret - ConfigMap type: string name: description: |- - Name of the secret or configmap from which to read the CA bundle. + Name is the resource name of the secret or configmap from which to read the CA bundle. The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string diff --git a/generated/1.27/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml b/generated/1.27/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml index 330984a89..83ae89781 100644 --- a/generated/1.27/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml +++ b/generated/1.27/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml @@ -212,25 +212,28 @@ spec: If omitted, a default set of system roots will be trusted. type: string certificateAuthorityDataSource: - description: Reference to a CA bundle in a secret or a configmap. + description: |- + Reference to a CA bundle in a secret or a configmap. + Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. properties: key: - description: Key within the secret or configmap from which - to read the CA bundle. + description: |- + Key is the key name within the secret or configmap from which to read the CA bundle. + The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + certificate bundle. minLength: 1 type: string kind: description: |- - Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. - For configmaps, the value associated with the key is not expected to be base64 encoded. enum: - Secret - ConfigMap type: string name: description: |- - Name of the secret or configmap from which to read the CA bundle. + Name is the resource name of the secret or configmap from which to read the CA bundle. The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string diff --git a/generated/1.28/README.adoc b/generated/1.28/README.adoc index 64f76af36..65ef835b8 100644 --- a/generated/1.28/README.adoc +++ b/generated/1.28/README.adoc @@ -23,10 +23,10 @@ Package v1alpha1 is the v1alpha1 version of the Pinniped concierge authenticatio -[id="{anchor_prefix}-go-pinniped-dev-generated-1-28-apis-concierge-authentication-v1alpha1-cabundlesource"] -==== CABundleSource +[id="{anchor_prefix}-go-pinniped-dev-generated-1-28-apis-concierge-authentication-v1alpha1-certificateauthoritydatasourcespec"] +==== CertificateAuthorityDataSourceSpec -CABundleSource provides a source for CA bundle used for client-side TLS verification. +CertificateAuthorityDataSourceSpec provides a source for CA bundle used for client-side TLS verification. .Appears In: **** @@ -36,12 +36,13 @@ CABundleSource provides a source for CA bundle used for client-side TLS verifica [cols="25a,75a", options="header"] |=== | Field | Description -| *`kind`* __string__ | Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + +| *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + -For configmaps, the value associated with the key is not expected to be base64 encoded. + -| *`name`* __string__ | Name of the secret or configmap from which to read the CA bundle. + +| *`name`* __string__ | Name is the resource name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. + -| *`key`* __string__ | Key within the secret or configmap from which to read the CA bundle. + +| *`key`* __string__ | Key is the key name within the secret or configmap from which to read the CA bundle. + +The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + +certificate bundle. + |=== @@ -147,7 +148,7 @@ username from the JWT token. When not specified, it will default to "username". [id="{anchor_prefix}-go-pinniped-dev-generated-1-28-apis-concierge-authentication-v1alpha1-tlsspec"] ==== TLSSpec -Configuration for configuring TLS on various authenticators. +TLSSpec provides TLS configuration on various authenticators. .Appears In: **** @@ -159,7 +160,8 @@ Configuration for configuring TLS on various authenticators. |=== | Field | Description | *`certificateAuthorityData`* __string__ | X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. + -| *`certificateAuthorityDataSource`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-28-apis-concierge-authentication-v1alpha1-cabundlesource[$$CABundleSource$$]__ | Reference to a CA bundle in a secret or a configmap. + +| *`certificateAuthorityDataSource`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-28-apis-concierge-authentication-v1alpha1-certificateauthoritydatasourcespec[$$CertificateAuthorityDataSourceSpec$$]__ | Reference to a CA bundle in a secret or a configmap. + +Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. + |=== @@ -1668,10 +1670,10 @@ Optional, when empty this defaults to "objectGUID". + |=== -[id="{anchor_prefix}-go-pinniped-dev-generated-1-28-apis-supervisor-idp-v1alpha1-cabundlesource"] -==== CABundleSource +[id="{anchor_prefix}-go-pinniped-dev-generated-1-28-apis-supervisor-idp-v1alpha1-certificateauthoritydatasourcespec"] +==== CertificateAuthorityDataSourceSpec -CABundleSource provides a source for CA bundle used for client-side TLS verification. +CertificateAuthorityDataSourceSpec provides a source for CA bundle used for client-side TLS verification. .Appears In: **** @@ -1681,12 +1683,13 @@ CABundleSource provides a source for CA bundle used for client-side TLS verifica [cols="25a,75a", options="header"] |=== | Field | Description -| *`kind`* __string__ | Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + +| *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + -For configmaps, the value associated with the key is not expected to be base64 encoded. + -| *`name`* __string__ | Name of the secret or configmap from which to read the CA bundle. + +| *`name`* __string__ | Name is the resource name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. + -| *`key`* __string__ | Key within the secret or configmap from which to read the CA bundle. + +| *`key`* __string__ | Key is the key name within the secret or configmap from which to read the CA bundle. + +The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + +certificate bundle. + |=== @@ -2446,7 +2449,8 @@ TLSSpec provides TLS configuration for identity provider integration. |=== | Field | Description | *`certificateAuthorityData`* __string__ | X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. + -| *`certificateAuthorityDataSource`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-28-apis-supervisor-idp-v1alpha1-cabundlesource[$$CABundleSource$$]__ | Reference to a CA bundle in a secret or a configmap. + +| *`certificateAuthorityDataSource`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-28-apis-supervisor-idp-v1alpha1-certificateauthoritydatasourcespec[$$CertificateAuthorityDataSourceSpec$$]__ | Reference to a CA bundle in a secret or a configmap. + +Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. + |=== diff --git a/generated/1.28/apis/concierge/authentication/v1alpha1/types_tls.go b/generated/1.28/apis/concierge/authentication/v1alpha1/types_tls.go index cc823a05e..883dc7fc7 100644 --- a/generated/1.28/apis/concierge/authentication/v1alpha1/types_tls.go +++ b/generated/1.28/apis/concierge/authentication/v1alpha1/types_tls.go @@ -3,28 +3,30 @@ package v1alpha1 -// CABundleSource provides a source for CA bundle used for client-side TLS verification. -type CABundleSource struct { - // Whether the CA bundle is being sourced from a kubernetes secret or a configmap. +// CertificateAuthorityDataSourceSpec provides a source for CA bundle used for client-side TLS verification. +type CertificateAuthorityDataSourceSpec struct { + // Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. // Secrets must be of type kubernetes.io/tls or Opaque. - // For configmaps, the value associated with the key is not expected to be base64 encoded. // +kubebuilder:validation:Enum=Secret;ConfigMap Kind string `json:"kind"` - // Name of the secret or configmap from which to read the CA bundle. + // Name is the resource name of the secret or configmap from which to read the CA bundle. // The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. // +kubebuilder:validation:MinLength=1 Name string `json:"name"` - // Key within the secret or configmap from which to read the CA bundle. + // Key is the key name within the secret or configmap from which to read the CA bundle. + // The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + // certificate bundle. // +kubebuilder:validation:MinLength=1 Key string `json:"key"` } -// Configuration for configuring TLS on various authenticators. +// TLSSpec provides TLS configuration on various authenticators. type TLSSpec struct { // X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. // +optional CertificateAuthorityData string `json:"certificateAuthorityData,omitempty"` // Reference to a CA bundle in a secret or a configmap. + // Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. // +optional - CertificateAuthorityDataSource *CABundleSource `json:"certificateAuthorityDataSource,omitempty"` + CertificateAuthorityDataSource *CertificateAuthorityDataSourceSpec `json:"certificateAuthorityDataSource,omitempty"` } diff --git a/generated/1.28/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go b/generated/1.28/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go index 27cbcc844..1e64ee699 100644 --- a/generated/1.28/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go +++ b/generated/1.28/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go @@ -14,17 +14,17 @@ import ( ) // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CABundleSource) DeepCopyInto(out *CABundleSource) { +func (in *CertificateAuthorityDataSourceSpec) DeepCopyInto(out *CertificateAuthorityDataSourceSpec) { *out = *in return } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CABundleSource. -func (in *CABundleSource) DeepCopy() *CABundleSource { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CertificateAuthorityDataSourceSpec. +func (in *CertificateAuthorityDataSourceSpec) DeepCopy() *CertificateAuthorityDataSourceSpec { if in == nil { return nil } - out := new(CABundleSource) + out := new(CertificateAuthorityDataSourceSpec) in.DeepCopyInto(out) return out } @@ -156,7 +156,7 @@ func (in *TLSSpec) DeepCopyInto(out *TLSSpec) { *out = *in if in.CertificateAuthorityDataSource != nil { in, out := &in.CertificateAuthorityDataSource, &out.CertificateAuthorityDataSource - *out = new(CABundleSource) + *out = new(CertificateAuthorityDataSourceSpec) **out = **in } return diff --git a/generated/1.28/apis/supervisor/idp/v1alpha1/types_tls.go b/generated/1.28/apis/supervisor/idp/v1alpha1/types_tls.go index 19d6d863a..407a5cde5 100644 --- a/generated/1.28/apis/supervisor/idp/v1alpha1/types_tls.go +++ b/generated/1.28/apis/supervisor/idp/v1alpha1/types_tls.go @@ -3,19 +3,19 @@ package v1alpha1 - -// CABundleSource provides a source for CA bundle used for client-side TLS verification. -type CABundleSource struct { - // Whether the CA bundle is being sourced from a kubernetes secret or a configmap. +// CertificateAuthorityDataSourceSpec provides a source for CA bundle used for client-side TLS verification. +type CertificateAuthorityDataSourceSpec struct { + // Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. // Secrets must be of type kubernetes.io/tls or Opaque. - // For configmaps, the value associated with the key is not expected to be base64 encoded. // +kubebuilder:validation:Enum=Secret;ConfigMap Kind string `json:"kind"` - // Name of the secret or configmap from which to read the CA bundle. + // Name is the resource name of the secret or configmap from which to read the CA bundle. // The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. // +kubebuilder:validation:MinLength=1 Name string `json:"name"` - // Key within the secret or configmap from which to read the CA bundle. + // Key is the key name within the secret or configmap from which to read the CA bundle. + // The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + // certificate bundle. // +kubebuilder:validation:MinLength=1 Key string `json:"key"` } @@ -26,6 +26,7 @@ type TLSSpec struct { // +optional CertificateAuthorityData string `json:"certificateAuthorityData,omitempty"` // Reference to a CA bundle in a secret or a configmap. + // Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. // +optional - CertificateAuthorityDataSource *CABundleSource `json:"certificateAuthorityDataSource,omitempty"` + CertificateAuthorityDataSource *CertificateAuthorityDataSourceSpec `json:"certificateAuthorityDataSource,omitempty"` } diff --git a/generated/1.28/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go b/generated/1.28/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go index 41d44d226..395c8f0fb 100644 --- a/generated/1.28/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go +++ b/generated/1.28/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go @@ -204,17 +204,17 @@ func (in *ActiveDirectoryIdentityProviderUserSearchAttributes) DeepCopy() *Activ } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CABundleSource) DeepCopyInto(out *CABundleSource) { +func (in *CertificateAuthorityDataSourceSpec) DeepCopyInto(out *CertificateAuthorityDataSourceSpec) { *out = *in return } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CABundleSource. -func (in *CABundleSource) DeepCopy() *CABundleSource { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CertificateAuthorityDataSourceSpec. +func (in *CertificateAuthorityDataSourceSpec) DeepCopy() *CertificateAuthorityDataSourceSpec { if in == nil { return nil } - out := new(CABundleSource) + out := new(CertificateAuthorityDataSourceSpec) in.DeepCopyInto(out) return out } @@ -818,7 +818,7 @@ func (in *TLSSpec) DeepCopyInto(out *TLSSpec) { *out = *in if in.CertificateAuthorityDataSource != nil { in, out := &in.CertificateAuthorityDataSource, &out.CertificateAuthorityDataSource - *out = new(CABundleSource) + *out = new(CertificateAuthorityDataSourceSpec) **out = **in } return diff --git a/generated/1.28/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml b/generated/1.28/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml index 88a7b3d8b..838f11edf 100644 --- a/generated/1.28/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml +++ b/generated/1.28/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml @@ -96,25 +96,28 @@ spec: If omitted, a default set of system roots will be trusted. type: string certificateAuthorityDataSource: - description: Reference to a CA bundle in a secret or a configmap. + description: |- + Reference to a CA bundle in a secret or a configmap. + Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. properties: key: - description: Key within the secret or configmap from which - to read the CA bundle. + description: |- + Key is the key name within the secret or configmap from which to read the CA bundle. + The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + certificate bundle. minLength: 1 type: string kind: description: |- - Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. - For configmaps, the value associated with the key is not expected to be base64 encoded. enum: - Secret - ConfigMap type: string name: description: |- - Name of the secret or configmap from which to read the CA bundle. + Name is the resource name of the secret or configmap from which to read the CA bundle. The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. minLength: 1 type: string diff --git a/generated/1.28/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml b/generated/1.28/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml index 8576dabbf..0133d62fc 100644 --- a/generated/1.28/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml +++ b/generated/1.28/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml @@ -67,25 +67,28 @@ spec: If omitted, a default set of system roots will be trusted. type: string certificateAuthorityDataSource: - description: Reference to a CA bundle in a secret or a configmap. + description: |- + Reference to a CA bundle in a secret or a configmap. + Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. properties: key: - description: Key within the secret or configmap from which - to read the CA bundle. + description: |- + Key is the key name within the secret or configmap from which to read the CA bundle. + The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + certificate bundle. minLength: 1 type: string kind: description: |- - Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. - For configmaps, the value associated with the key is not expected to be base64 encoded. enum: - Secret - ConfigMap type: string name: description: |- - Name of the secret or configmap from which to read the CA bundle. + Name is the resource name of the secret or configmap from which to read the CA bundle. The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. minLength: 1 type: string diff --git a/generated/1.28/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml b/generated/1.28/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml index 36d7edcd4..dcc1836b6 100644 --- a/generated/1.28/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml +++ b/generated/1.28/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml @@ -171,25 +171,28 @@ spec: If omitted, a default set of system roots will be trusted. type: string certificateAuthorityDataSource: - description: Reference to a CA bundle in a secret or a configmap. + description: |- + Reference to a CA bundle in a secret or a configmap. + Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. properties: key: - description: Key within the secret or configmap from which - to read the CA bundle. + description: |- + Key is the key name within the secret or configmap from which to read the CA bundle. + The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + certificate bundle. minLength: 1 type: string kind: description: |- - Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. - For configmaps, the value associated with the key is not expected to be base64 encoded. enum: - Secret - ConfigMap type: string name: description: |- - Name of the secret or configmap from which to read the CA bundle. + Name is the resource name of the secret or configmap from which to read the CA bundle. The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string diff --git a/generated/1.28/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml b/generated/1.28/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml index 1a5dea759..669377e7c 100644 --- a/generated/1.28/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml +++ b/generated/1.28/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml @@ -226,25 +226,28 @@ spec: be trusted. type: string certificateAuthorityDataSource: - description: Reference to a CA bundle in a secret or a configmap. + description: |- + Reference to a CA bundle in a secret or a configmap. + Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. properties: key: - description: Key within the secret or configmap from which - to read the CA bundle. + description: |- + Key is the key name within the secret or configmap from which to read the CA bundle. + The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + certificate bundle. minLength: 1 type: string kind: description: |- - Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. - For configmaps, the value associated with the key is not expected to be base64 encoded. enum: - Secret - ConfigMap type: string name: description: |- - Name of the secret or configmap from which to read the CA bundle. + Name is the resource name of the secret or configmap from which to read the CA bundle. The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string diff --git a/generated/1.28/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml b/generated/1.28/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml index bcd798dba..e1fb91934 100644 --- a/generated/1.28/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml +++ b/generated/1.28/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml @@ -162,25 +162,28 @@ spec: If omitted, a default set of system roots will be trusted. type: string certificateAuthorityDataSource: - description: Reference to a CA bundle in a secret or a configmap. + description: |- + Reference to a CA bundle in a secret or a configmap. + Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. properties: key: - description: Key within the secret or configmap from which - to read the CA bundle. + description: |- + Key is the key name within the secret or configmap from which to read the CA bundle. + The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + certificate bundle. minLength: 1 type: string kind: description: |- - Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. - For configmaps, the value associated with the key is not expected to be base64 encoded. enum: - Secret - ConfigMap type: string name: description: |- - Name of the secret or configmap from which to read the CA bundle. + Name is the resource name of the secret or configmap from which to read the CA bundle. The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string diff --git a/generated/1.28/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml b/generated/1.28/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml index 330984a89..83ae89781 100644 --- a/generated/1.28/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml +++ b/generated/1.28/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml @@ -212,25 +212,28 @@ spec: If omitted, a default set of system roots will be trusted. type: string certificateAuthorityDataSource: - description: Reference to a CA bundle in a secret or a configmap. + description: |- + Reference to a CA bundle in a secret or a configmap. + Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. properties: key: - description: Key within the secret or configmap from which - to read the CA bundle. + description: |- + Key is the key name within the secret or configmap from which to read the CA bundle. + The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + certificate bundle. minLength: 1 type: string kind: description: |- - Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. - For configmaps, the value associated with the key is not expected to be base64 encoded. enum: - Secret - ConfigMap type: string name: description: |- - Name of the secret or configmap from which to read the CA bundle. + Name is the resource name of the secret or configmap from which to read the CA bundle. The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string diff --git a/generated/1.29/README.adoc b/generated/1.29/README.adoc index a6e8fdf5b..b5ae5e4a4 100644 --- a/generated/1.29/README.adoc +++ b/generated/1.29/README.adoc @@ -23,10 +23,10 @@ Package v1alpha1 is the v1alpha1 version of the Pinniped concierge authenticatio -[id="{anchor_prefix}-go-pinniped-dev-generated-1-29-apis-concierge-authentication-v1alpha1-cabundlesource"] -==== CABundleSource +[id="{anchor_prefix}-go-pinniped-dev-generated-1-29-apis-concierge-authentication-v1alpha1-certificateauthoritydatasourcespec"] +==== CertificateAuthorityDataSourceSpec -CABundleSource provides a source for CA bundle used for client-side TLS verification. +CertificateAuthorityDataSourceSpec provides a source for CA bundle used for client-side TLS verification. .Appears In: **** @@ -36,12 +36,13 @@ CABundleSource provides a source for CA bundle used for client-side TLS verifica [cols="25a,75a", options="header"] |=== | Field | Description -| *`kind`* __string__ | Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + +| *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + -For configmaps, the value associated with the key is not expected to be base64 encoded. + -| *`name`* __string__ | Name of the secret or configmap from which to read the CA bundle. + +| *`name`* __string__ | Name is the resource name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. + -| *`key`* __string__ | Key within the secret or configmap from which to read the CA bundle. + +| *`key`* __string__ | Key is the key name within the secret or configmap from which to read the CA bundle. + +The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + +certificate bundle. + |=== @@ -147,7 +148,7 @@ username from the JWT token. When not specified, it will default to "username". [id="{anchor_prefix}-go-pinniped-dev-generated-1-29-apis-concierge-authentication-v1alpha1-tlsspec"] ==== TLSSpec -Configuration for configuring TLS on various authenticators. +TLSSpec provides TLS configuration on various authenticators. .Appears In: **** @@ -159,7 +160,8 @@ Configuration for configuring TLS on various authenticators. |=== | Field | Description | *`certificateAuthorityData`* __string__ | X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. + -| *`certificateAuthorityDataSource`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-29-apis-concierge-authentication-v1alpha1-cabundlesource[$$CABundleSource$$]__ | Reference to a CA bundle in a secret or a configmap. + +| *`certificateAuthorityDataSource`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-29-apis-concierge-authentication-v1alpha1-certificateauthoritydatasourcespec[$$CertificateAuthorityDataSourceSpec$$]__ | Reference to a CA bundle in a secret or a configmap. + +Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. + |=== @@ -1668,10 +1670,10 @@ Optional, when empty this defaults to "objectGUID". + |=== -[id="{anchor_prefix}-go-pinniped-dev-generated-1-29-apis-supervisor-idp-v1alpha1-cabundlesource"] -==== CABundleSource +[id="{anchor_prefix}-go-pinniped-dev-generated-1-29-apis-supervisor-idp-v1alpha1-certificateauthoritydatasourcespec"] +==== CertificateAuthorityDataSourceSpec -CABundleSource provides a source for CA bundle used for client-side TLS verification. +CertificateAuthorityDataSourceSpec provides a source for CA bundle used for client-side TLS verification. .Appears In: **** @@ -1681,12 +1683,13 @@ CABundleSource provides a source for CA bundle used for client-side TLS verifica [cols="25a,75a", options="header"] |=== | Field | Description -| *`kind`* __string__ | Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + +| *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + -For configmaps, the value associated with the key is not expected to be base64 encoded. + -| *`name`* __string__ | Name of the secret or configmap from which to read the CA bundle. + +| *`name`* __string__ | Name is the resource name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. + -| *`key`* __string__ | Key within the secret or configmap from which to read the CA bundle. + +| *`key`* __string__ | Key is the key name within the secret or configmap from which to read the CA bundle. + +The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + +certificate bundle. + |=== @@ -2446,7 +2449,8 @@ TLSSpec provides TLS configuration for identity provider integration. |=== | Field | Description | *`certificateAuthorityData`* __string__ | X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. + -| *`certificateAuthorityDataSource`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-29-apis-supervisor-idp-v1alpha1-cabundlesource[$$CABundleSource$$]__ | Reference to a CA bundle in a secret or a configmap. + +| *`certificateAuthorityDataSource`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-29-apis-supervisor-idp-v1alpha1-certificateauthoritydatasourcespec[$$CertificateAuthorityDataSourceSpec$$]__ | Reference to a CA bundle in a secret or a configmap. + +Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. + |=== diff --git a/generated/1.29/apis/concierge/authentication/v1alpha1/types_tls.go b/generated/1.29/apis/concierge/authentication/v1alpha1/types_tls.go index cc823a05e..883dc7fc7 100644 --- a/generated/1.29/apis/concierge/authentication/v1alpha1/types_tls.go +++ b/generated/1.29/apis/concierge/authentication/v1alpha1/types_tls.go @@ -3,28 +3,30 @@ package v1alpha1 -// CABundleSource provides a source for CA bundle used for client-side TLS verification. -type CABundleSource struct { - // Whether the CA bundle is being sourced from a kubernetes secret or a configmap. +// CertificateAuthorityDataSourceSpec provides a source for CA bundle used for client-side TLS verification. +type CertificateAuthorityDataSourceSpec struct { + // Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. // Secrets must be of type kubernetes.io/tls or Opaque. - // For configmaps, the value associated with the key is not expected to be base64 encoded. // +kubebuilder:validation:Enum=Secret;ConfigMap Kind string `json:"kind"` - // Name of the secret or configmap from which to read the CA bundle. + // Name is the resource name of the secret or configmap from which to read the CA bundle. // The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. // +kubebuilder:validation:MinLength=1 Name string `json:"name"` - // Key within the secret or configmap from which to read the CA bundle. + // Key is the key name within the secret or configmap from which to read the CA bundle. + // The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + // certificate bundle. // +kubebuilder:validation:MinLength=1 Key string `json:"key"` } -// Configuration for configuring TLS on various authenticators. +// TLSSpec provides TLS configuration on various authenticators. type TLSSpec struct { // X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. // +optional CertificateAuthorityData string `json:"certificateAuthorityData,omitempty"` // Reference to a CA bundle in a secret or a configmap. + // Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. // +optional - CertificateAuthorityDataSource *CABundleSource `json:"certificateAuthorityDataSource,omitempty"` + CertificateAuthorityDataSource *CertificateAuthorityDataSourceSpec `json:"certificateAuthorityDataSource,omitempty"` } diff --git a/generated/1.29/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go b/generated/1.29/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go index 27cbcc844..1e64ee699 100644 --- a/generated/1.29/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go +++ b/generated/1.29/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go @@ -14,17 +14,17 @@ import ( ) // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CABundleSource) DeepCopyInto(out *CABundleSource) { +func (in *CertificateAuthorityDataSourceSpec) DeepCopyInto(out *CertificateAuthorityDataSourceSpec) { *out = *in return } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CABundleSource. -func (in *CABundleSource) DeepCopy() *CABundleSource { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CertificateAuthorityDataSourceSpec. +func (in *CertificateAuthorityDataSourceSpec) DeepCopy() *CertificateAuthorityDataSourceSpec { if in == nil { return nil } - out := new(CABundleSource) + out := new(CertificateAuthorityDataSourceSpec) in.DeepCopyInto(out) return out } @@ -156,7 +156,7 @@ func (in *TLSSpec) DeepCopyInto(out *TLSSpec) { *out = *in if in.CertificateAuthorityDataSource != nil { in, out := &in.CertificateAuthorityDataSource, &out.CertificateAuthorityDataSource - *out = new(CABundleSource) + *out = new(CertificateAuthorityDataSourceSpec) **out = **in } return diff --git a/generated/1.29/apis/supervisor/idp/v1alpha1/types_tls.go b/generated/1.29/apis/supervisor/idp/v1alpha1/types_tls.go index 19d6d863a..407a5cde5 100644 --- a/generated/1.29/apis/supervisor/idp/v1alpha1/types_tls.go +++ b/generated/1.29/apis/supervisor/idp/v1alpha1/types_tls.go @@ -3,19 +3,19 @@ package v1alpha1 - -// CABundleSource provides a source for CA bundle used for client-side TLS verification. -type CABundleSource struct { - // Whether the CA bundle is being sourced from a kubernetes secret or a configmap. +// CertificateAuthorityDataSourceSpec provides a source for CA bundle used for client-side TLS verification. +type CertificateAuthorityDataSourceSpec struct { + // Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. // Secrets must be of type kubernetes.io/tls or Opaque. - // For configmaps, the value associated with the key is not expected to be base64 encoded. // +kubebuilder:validation:Enum=Secret;ConfigMap Kind string `json:"kind"` - // Name of the secret or configmap from which to read the CA bundle. + // Name is the resource name of the secret or configmap from which to read the CA bundle. // The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. // +kubebuilder:validation:MinLength=1 Name string `json:"name"` - // Key within the secret or configmap from which to read the CA bundle. + // Key is the key name within the secret or configmap from which to read the CA bundle. + // The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + // certificate bundle. // +kubebuilder:validation:MinLength=1 Key string `json:"key"` } @@ -26,6 +26,7 @@ type TLSSpec struct { // +optional CertificateAuthorityData string `json:"certificateAuthorityData,omitempty"` // Reference to a CA bundle in a secret or a configmap. + // Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. // +optional - CertificateAuthorityDataSource *CABundleSource `json:"certificateAuthorityDataSource,omitempty"` + CertificateAuthorityDataSource *CertificateAuthorityDataSourceSpec `json:"certificateAuthorityDataSource,omitempty"` } diff --git a/generated/1.29/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go b/generated/1.29/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go index 41d44d226..395c8f0fb 100644 --- a/generated/1.29/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go +++ b/generated/1.29/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go @@ -204,17 +204,17 @@ func (in *ActiveDirectoryIdentityProviderUserSearchAttributes) DeepCopy() *Activ } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CABundleSource) DeepCopyInto(out *CABundleSource) { +func (in *CertificateAuthorityDataSourceSpec) DeepCopyInto(out *CertificateAuthorityDataSourceSpec) { *out = *in return } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CABundleSource. -func (in *CABundleSource) DeepCopy() *CABundleSource { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CertificateAuthorityDataSourceSpec. +func (in *CertificateAuthorityDataSourceSpec) DeepCopy() *CertificateAuthorityDataSourceSpec { if in == nil { return nil } - out := new(CABundleSource) + out := new(CertificateAuthorityDataSourceSpec) in.DeepCopyInto(out) return out } @@ -818,7 +818,7 @@ func (in *TLSSpec) DeepCopyInto(out *TLSSpec) { *out = *in if in.CertificateAuthorityDataSource != nil { in, out := &in.CertificateAuthorityDataSource, &out.CertificateAuthorityDataSource - *out = new(CABundleSource) + *out = new(CertificateAuthorityDataSourceSpec) **out = **in } return diff --git a/generated/1.29/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml b/generated/1.29/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml index 88a7b3d8b..838f11edf 100644 --- a/generated/1.29/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml +++ b/generated/1.29/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml @@ -96,25 +96,28 @@ spec: If omitted, a default set of system roots will be trusted. type: string certificateAuthorityDataSource: - description: Reference to a CA bundle in a secret or a configmap. + description: |- + Reference to a CA bundle in a secret or a configmap. + Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. properties: key: - description: Key within the secret or configmap from which - to read the CA bundle. + description: |- + Key is the key name within the secret or configmap from which to read the CA bundle. + The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + certificate bundle. minLength: 1 type: string kind: description: |- - Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. - For configmaps, the value associated with the key is not expected to be base64 encoded. enum: - Secret - ConfigMap type: string name: description: |- - Name of the secret or configmap from which to read the CA bundle. + Name is the resource name of the secret or configmap from which to read the CA bundle. The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. minLength: 1 type: string diff --git a/generated/1.29/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml b/generated/1.29/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml index 8576dabbf..0133d62fc 100644 --- a/generated/1.29/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml +++ b/generated/1.29/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml @@ -67,25 +67,28 @@ spec: If omitted, a default set of system roots will be trusted. type: string certificateAuthorityDataSource: - description: Reference to a CA bundle in a secret or a configmap. + description: |- + Reference to a CA bundle in a secret or a configmap. + Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. properties: key: - description: Key within the secret or configmap from which - to read the CA bundle. + description: |- + Key is the key name within the secret or configmap from which to read the CA bundle. + The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + certificate bundle. minLength: 1 type: string kind: description: |- - Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. - For configmaps, the value associated with the key is not expected to be base64 encoded. enum: - Secret - ConfigMap type: string name: description: |- - Name of the secret or configmap from which to read the CA bundle. + Name is the resource name of the secret or configmap from which to read the CA bundle. The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. minLength: 1 type: string diff --git a/generated/1.29/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml b/generated/1.29/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml index 36d7edcd4..dcc1836b6 100644 --- a/generated/1.29/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml +++ b/generated/1.29/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml @@ -171,25 +171,28 @@ spec: If omitted, a default set of system roots will be trusted. type: string certificateAuthorityDataSource: - description: Reference to a CA bundle in a secret or a configmap. + description: |- + Reference to a CA bundle in a secret or a configmap. + Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. properties: key: - description: Key within the secret or configmap from which - to read the CA bundle. + description: |- + Key is the key name within the secret or configmap from which to read the CA bundle. + The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + certificate bundle. minLength: 1 type: string kind: description: |- - Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. - For configmaps, the value associated with the key is not expected to be base64 encoded. enum: - Secret - ConfigMap type: string name: description: |- - Name of the secret or configmap from which to read the CA bundle. + Name is the resource name of the secret or configmap from which to read the CA bundle. The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string diff --git a/generated/1.29/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml b/generated/1.29/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml index 1a5dea759..669377e7c 100644 --- a/generated/1.29/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml +++ b/generated/1.29/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml @@ -226,25 +226,28 @@ spec: be trusted. type: string certificateAuthorityDataSource: - description: Reference to a CA bundle in a secret or a configmap. + description: |- + Reference to a CA bundle in a secret or a configmap. + Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. properties: key: - description: Key within the secret or configmap from which - to read the CA bundle. + description: |- + Key is the key name within the secret or configmap from which to read the CA bundle. + The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + certificate bundle. minLength: 1 type: string kind: description: |- - Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. - For configmaps, the value associated with the key is not expected to be base64 encoded. enum: - Secret - ConfigMap type: string name: description: |- - Name of the secret or configmap from which to read the CA bundle. + Name is the resource name of the secret or configmap from which to read the CA bundle. The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string diff --git a/generated/1.29/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml b/generated/1.29/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml index bcd798dba..e1fb91934 100644 --- a/generated/1.29/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml +++ b/generated/1.29/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml @@ -162,25 +162,28 @@ spec: If omitted, a default set of system roots will be trusted. type: string certificateAuthorityDataSource: - description: Reference to a CA bundle in a secret or a configmap. + description: |- + Reference to a CA bundle in a secret or a configmap. + Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. properties: key: - description: Key within the secret or configmap from which - to read the CA bundle. + description: |- + Key is the key name within the secret or configmap from which to read the CA bundle. + The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + certificate bundle. minLength: 1 type: string kind: description: |- - Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. - For configmaps, the value associated with the key is not expected to be base64 encoded. enum: - Secret - ConfigMap type: string name: description: |- - Name of the secret or configmap from which to read the CA bundle. + Name is the resource name of the secret or configmap from which to read the CA bundle. The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string diff --git a/generated/1.29/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml b/generated/1.29/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml index 330984a89..83ae89781 100644 --- a/generated/1.29/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml +++ b/generated/1.29/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml @@ -212,25 +212,28 @@ spec: If omitted, a default set of system roots will be trusted. type: string certificateAuthorityDataSource: - description: Reference to a CA bundle in a secret or a configmap. + description: |- + Reference to a CA bundle in a secret or a configmap. + Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. properties: key: - description: Key within the secret or configmap from which - to read the CA bundle. + description: |- + Key is the key name within the secret or configmap from which to read the CA bundle. + The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + certificate bundle. minLength: 1 type: string kind: description: |- - Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. - For configmaps, the value associated with the key is not expected to be base64 encoded. enum: - Secret - ConfigMap type: string name: description: |- - Name of the secret or configmap from which to read the CA bundle. + Name is the resource name of the secret or configmap from which to read the CA bundle. The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string diff --git a/generated/1.30/README.adoc b/generated/1.30/README.adoc index f91e3138c..0b9021c99 100644 --- a/generated/1.30/README.adoc +++ b/generated/1.30/README.adoc @@ -23,10 +23,10 @@ Package v1alpha1 is the v1alpha1 version of the Pinniped concierge authenticatio -[id="{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-concierge-authentication-v1alpha1-cabundlesource"] -==== CABundleSource +[id="{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-concierge-authentication-v1alpha1-certificateauthoritydatasourcespec"] +==== CertificateAuthorityDataSourceSpec -CABundleSource provides a source for CA bundle used for client-side TLS verification. +CertificateAuthorityDataSourceSpec provides a source for CA bundle used for client-side TLS verification. .Appears In: **** @@ -36,12 +36,13 @@ CABundleSource provides a source for CA bundle used for client-side TLS verifica [cols="25a,75a", options="header"] |=== | Field | Description -| *`kind`* __string__ | Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + +| *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + -For configmaps, the value associated with the key is not expected to be base64 encoded. + -| *`name`* __string__ | Name of the secret or configmap from which to read the CA bundle. + +| *`name`* __string__ | Name is the resource name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. + -| *`key`* __string__ | Key within the secret or configmap from which to read the CA bundle. + +| *`key`* __string__ | Key is the key name within the secret or configmap from which to read the CA bundle. + +The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + +certificate bundle. + |=== @@ -147,7 +148,7 @@ username from the JWT token. When not specified, it will default to "username". [id="{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-concierge-authentication-v1alpha1-tlsspec"] ==== TLSSpec -Configuration for configuring TLS on various authenticators. +TLSSpec provides TLS configuration on various authenticators. .Appears In: **** @@ -159,7 +160,8 @@ Configuration for configuring TLS on various authenticators. |=== | Field | Description | *`certificateAuthorityData`* __string__ | X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. + -| *`certificateAuthorityDataSource`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-concierge-authentication-v1alpha1-cabundlesource[$$CABundleSource$$]__ | Reference to a CA bundle in a secret or a configmap. + +| *`certificateAuthorityDataSource`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-concierge-authentication-v1alpha1-certificateauthoritydatasourcespec[$$CertificateAuthorityDataSourceSpec$$]__ | Reference to a CA bundle in a secret or a configmap. + +Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. + |=== @@ -1668,10 +1670,10 @@ Optional, when empty this defaults to "objectGUID". + |=== -[id="{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-supervisor-idp-v1alpha1-cabundlesource"] -==== CABundleSource +[id="{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-supervisor-idp-v1alpha1-certificateauthoritydatasourcespec"] +==== CertificateAuthorityDataSourceSpec -CABundleSource provides a source for CA bundle used for client-side TLS verification. +CertificateAuthorityDataSourceSpec provides a source for CA bundle used for client-side TLS verification. .Appears In: **** @@ -1681,12 +1683,13 @@ CABundleSource provides a source for CA bundle used for client-side TLS verifica [cols="25a,75a", options="header"] |=== | Field | Description -| *`kind`* __string__ | Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + +| *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + -For configmaps, the value associated with the key is not expected to be base64 encoded. + -| *`name`* __string__ | Name of the secret or configmap from which to read the CA bundle. + +| *`name`* __string__ | Name is the resource name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. + -| *`key`* __string__ | Key within the secret or configmap from which to read the CA bundle. + +| *`key`* __string__ | Key is the key name within the secret or configmap from which to read the CA bundle. + +The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + +certificate bundle. + |=== @@ -2446,7 +2449,8 @@ TLSSpec provides TLS configuration for identity provider integration. |=== | Field | Description | *`certificateAuthorityData`* __string__ | X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. + -| *`certificateAuthorityDataSource`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-supervisor-idp-v1alpha1-cabundlesource[$$CABundleSource$$]__ | Reference to a CA bundle in a secret or a configmap. + +| *`certificateAuthorityDataSource`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-supervisor-idp-v1alpha1-certificateauthoritydatasourcespec[$$CertificateAuthorityDataSourceSpec$$]__ | Reference to a CA bundle in a secret or a configmap. + +Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. + |=== diff --git a/generated/1.30/apis/concierge/authentication/v1alpha1/types_tls.go b/generated/1.30/apis/concierge/authentication/v1alpha1/types_tls.go index cc823a05e..883dc7fc7 100644 --- a/generated/1.30/apis/concierge/authentication/v1alpha1/types_tls.go +++ b/generated/1.30/apis/concierge/authentication/v1alpha1/types_tls.go @@ -3,28 +3,30 @@ package v1alpha1 -// CABundleSource provides a source for CA bundle used for client-side TLS verification. -type CABundleSource struct { - // Whether the CA bundle is being sourced from a kubernetes secret or a configmap. +// CertificateAuthorityDataSourceSpec provides a source for CA bundle used for client-side TLS verification. +type CertificateAuthorityDataSourceSpec struct { + // Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. // Secrets must be of type kubernetes.io/tls or Opaque. - // For configmaps, the value associated with the key is not expected to be base64 encoded. // +kubebuilder:validation:Enum=Secret;ConfigMap Kind string `json:"kind"` - // Name of the secret or configmap from which to read the CA bundle. + // Name is the resource name of the secret or configmap from which to read the CA bundle. // The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. // +kubebuilder:validation:MinLength=1 Name string `json:"name"` - // Key within the secret or configmap from which to read the CA bundle. + // Key is the key name within the secret or configmap from which to read the CA bundle. + // The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + // certificate bundle. // +kubebuilder:validation:MinLength=1 Key string `json:"key"` } -// Configuration for configuring TLS on various authenticators. +// TLSSpec provides TLS configuration on various authenticators. type TLSSpec struct { // X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. // +optional CertificateAuthorityData string `json:"certificateAuthorityData,omitempty"` // Reference to a CA bundle in a secret or a configmap. + // Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. // +optional - CertificateAuthorityDataSource *CABundleSource `json:"certificateAuthorityDataSource,omitempty"` + CertificateAuthorityDataSource *CertificateAuthorityDataSourceSpec `json:"certificateAuthorityDataSource,omitempty"` } diff --git a/generated/1.30/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go b/generated/1.30/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go index 27cbcc844..1e64ee699 100644 --- a/generated/1.30/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go +++ b/generated/1.30/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go @@ -14,17 +14,17 @@ import ( ) // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CABundleSource) DeepCopyInto(out *CABundleSource) { +func (in *CertificateAuthorityDataSourceSpec) DeepCopyInto(out *CertificateAuthorityDataSourceSpec) { *out = *in return } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CABundleSource. -func (in *CABundleSource) DeepCopy() *CABundleSource { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CertificateAuthorityDataSourceSpec. +func (in *CertificateAuthorityDataSourceSpec) DeepCopy() *CertificateAuthorityDataSourceSpec { if in == nil { return nil } - out := new(CABundleSource) + out := new(CertificateAuthorityDataSourceSpec) in.DeepCopyInto(out) return out } @@ -156,7 +156,7 @@ func (in *TLSSpec) DeepCopyInto(out *TLSSpec) { *out = *in if in.CertificateAuthorityDataSource != nil { in, out := &in.CertificateAuthorityDataSource, &out.CertificateAuthorityDataSource - *out = new(CABundleSource) + *out = new(CertificateAuthorityDataSourceSpec) **out = **in } return diff --git a/generated/1.30/apis/supervisor/idp/v1alpha1/types_tls.go b/generated/1.30/apis/supervisor/idp/v1alpha1/types_tls.go index 19d6d863a..407a5cde5 100644 --- a/generated/1.30/apis/supervisor/idp/v1alpha1/types_tls.go +++ b/generated/1.30/apis/supervisor/idp/v1alpha1/types_tls.go @@ -3,19 +3,19 @@ package v1alpha1 - -// CABundleSource provides a source for CA bundle used for client-side TLS verification. -type CABundleSource struct { - // Whether the CA bundle is being sourced from a kubernetes secret or a configmap. +// CertificateAuthorityDataSourceSpec provides a source for CA bundle used for client-side TLS verification. +type CertificateAuthorityDataSourceSpec struct { + // Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. // Secrets must be of type kubernetes.io/tls or Opaque. - // For configmaps, the value associated with the key is not expected to be base64 encoded. // +kubebuilder:validation:Enum=Secret;ConfigMap Kind string `json:"kind"` - // Name of the secret or configmap from which to read the CA bundle. + // Name is the resource name of the secret or configmap from which to read the CA bundle. // The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. // +kubebuilder:validation:MinLength=1 Name string `json:"name"` - // Key within the secret or configmap from which to read the CA bundle. + // Key is the key name within the secret or configmap from which to read the CA bundle. + // The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + // certificate bundle. // +kubebuilder:validation:MinLength=1 Key string `json:"key"` } @@ -26,6 +26,7 @@ type TLSSpec struct { // +optional CertificateAuthorityData string `json:"certificateAuthorityData,omitempty"` // Reference to a CA bundle in a secret or a configmap. + // Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. // +optional - CertificateAuthorityDataSource *CABundleSource `json:"certificateAuthorityDataSource,omitempty"` + CertificateAuthorityDataSource *CertificateAuthorityDataSourceSpec `json:"certificateAuthorityDataSource,omitempty"` } diff --git a/generated/1.30/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go b/generated/1.30/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go index 41d44d226..395c8f0fb 100644 --- a/generated/1.30/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go +++ b/generated/1.30/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go @@ -204,17 +204,17 @@ func (in *ActiveDirectoryIdentityProviderUserSearchAttributes) DeepCopy() *Activ } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CABundleSource) DeepCopyInto(out *CABundleSource) { +func (in *CertificateAuthorityDataSourceSpec) DeepCopyInto(out *CertificateAuthorityDataSourceSpec) { *out = *in return } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CABundleSource. -func (in *CABundleSource) DeepCopy() *CABundleSource { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CertificateAuthorityDataSourceSpec. +func (in *CertificateAuthorityDataSourceSpec) DeepCopy() *CertificateAuthorityDataSourceSpec { if in == nil { return nil } - out := new(CABundleSource) + out := new(CertificateAuthorityDataSourceSpec) in.DeepCopyInto(out) return out } @@ -818,7 +818,7 @@ func (in *TLSSpec) DeepCopyInto(out *TLSSpec) { *out = *in if in.CertificateAuthorityDataSource != nil { in, out := &in.CertificateAuthorityDataSource, &out.CertificateAuthorityDataSource - *out = new(CABundleSource) + *out = new(CertificateAuthorityDataSourceSpec) **out = **in } return diff --git a/generated/1.30/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml b/generated/1.30/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml index 88a7b3d8b..838f11edf 100644 --- a/generated/1.30/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml +++ b/generated/1.30/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml @@ -96,25 +96,28 @@ spec: If omitted, a default set of system roots will be trusted. type: string certificateAuthorityDataSource: - description: Reference to a CA bundle in a secret or a configmap. + description: |- + Reference to a CA bundle in a secret or a configmap. + Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. properties: key: - description: Key within the secret or configmap from which - to read the CA bundle. + description: |- + Key is the key name within the secret or configmap from which to read the CA bundle. + The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + certificate bundle. minLength: 1 type: string kind: description: |- - Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. - For configmaps, the value associated with the key is not expected to be base64 encoded. enum: - Secret - ConfigMap type: string name: description: |- - Name of the secret or configmap from which to read the CA bundle. + Name is the resource name of the secret or configmap from which to read the CA bundle. The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. minLength: 1 type: string diff --git a/generated/1.30/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml b/generated/1.30/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml index 8576dabbf..0133d62fc 100644 --- a/generated/1.30/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml +++ b/generated/1.30/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml @@ -67,25 +67,28 @@ spec: If omitted, a default set of system roots will be trusted. type: string certificateAuthorityDataSource: - description: Reference to a CA bundle in a secret or a configmap. + description: |- + Reference to a CA bundle in a secret or a configmap. + Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. properties: key: - description: Key within the secret or configmap from which - to read the CA bundle. + description: |- + Key is the key name within the secret or configmap from which to read the CA bundle. + The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + certificate bundle. minLength: 1 type: string kind: description: |- - Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. - For configmaps, the value associated with the key is not expected to be base64 encoded. enum: - Secret - ConfigMap type: string name: description: |- - Name of the secret or configmap from which to read the CA bundle. + Name is the resource name of the secret or configmap from which to read the CA bundle. The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. minLength: 1 type: string diff --git a/generated/1.30/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml b/generated/1.30/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml index 36d7edcd4..dcc1836b6 100644 --- a/generated/1.30/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml +++ b/generated/1.30/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml @@ -171,25 +171,28 @@ spec: If omitted, a default set of system roots will be trusted. type: string certificateAuthorityDataSource: - description: Reference to a CA bundle in a secret or a configmap. + description: |- + Reference to a CA bundle in a secret or a configmap. + Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. properties: key: - description: Key within the secret or configmap from which - to read the CA bundle. + description: |- + Key is the key name within the secret or configmap from which to read the CA bundle. + The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + certificate bundle. minLength: 1 type: string kind: description: |- - Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. - For configmaps, the value associated with the key is not expected to be base64 encoded. enum: - Secret - ConfigMap type: string name: description: |- - Name of the secret or configmap from which to read the CA bundle. + Name is the resource name of the secret or configmap from which to read the CA bundle. The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string diff --git a/generated/1.30/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml b/generated/1.30/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml index 1a5dea759..669377e7c 100644 --- a/generated/1.30/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml +++ b/generated/1.30/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml @@ -226,25 +226,28 @@ spec: be trusted. type: string certificateAuthorityDataSource: - description: Reference to a CA bundle in a secret or a configmap. + description: |- + Reference to a CA bundle in a secret or a configmap. + Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. properties: key: - description: Key within the secret or configmap from which - to read the CA bundle. + description: |- + Key is the key name within the secret or configmap from which to read the CA bundle. + The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + certificate bundle. minLength: 1 type: string kind: description: |- - Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. - For configmaps, the value associated with the key is not expected to be base64 encoded. enum: - Secret - ConfigMap type: string name: description: |- - Name of the secret or configmap from which to read the CA bundle. + Name is the resource name of the secret or configmap from which to read the CA bundle. The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string diff --git a/generated/1.30/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml b/generated/1.30/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml index bcd798dba..e1fb91934 100644 --- a/generated/1.30/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml +++ b/generated/1.30/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml @@ -162,25 +162,28 @@ spec: If omitted, a default set of system roots will be trusted. type: string certificateAuthorityDataSource: - description: Reference to a CA bundle in a secret or a configmap. + description: |- + Reference to a CA bundle in a secret or a configmap. + Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. properties: key: - description: Key within the secret or configmap from which - to read the CA bundle. + description: |- + Key is the key name within the secret or configmap from which to read the CA bundle. + The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + certificate bundle. minLength: 1 type: string kind: description: |- - Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. - For configmaps, the value associated with the key is not expected to be base64 encoded. enum: - Secret - ConfigMap type: string name: description: |- - Name of the secret or configmap from which to read the CA bundle. + Name is the resource name of the secret or configmap from which to read the CA bundle. The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string diff --git a/generated/1.30/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml b/generated/1.30/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml index 330984a89..83ae89781 100644 --- a/generated/1.30/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml +++ b/generated/1.30/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml @@ -212,25 +212,28 @@ spec: If omitted, a default set of system roots will be trusted. type: string certificateAuthorityDataSource: - description: Reference to a CA bundle in a secret or a configmap. + description: |- + Reference to a CA bundle in a secret or a configmap. + Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. properties: key: - description: Key within the secret or configmap from which - to read the CA bundle. + description: |- + Key is the key name within the secret or configmap from which to read the CA bundle. + The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + certificate bundle. minLength: 1 type: string kind: description: |- - Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. - For configmaps, the value associated with the key is not expected to be base64 encoded. enum: - Secret - ConfigMap type: string name: description: |- - Name of the secret or configmap from which to read the CA bundle. + Name is the resource name of the secret or configmap from which to read the CA bundle. The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. minLength: 1 type: string diff --git a/generated/latest/README.adoc b/generated/latest/README.adoc index f91e3138c..0b9021c99 100644 --- a/generated/latest/README.adoc +++ b/generated/latest/README.adoc @@ -23,10 +23,10 @@ Package v1alpha1 is the v1alpha1 version of the Pinniped concierge authenticatio -[id="{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-concierge-authentication-v1alpha1-cabundlesource"] -==== CABundleSource +[id="{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-concierge-authentication-v1alpha1-certificateauthoritydatasourcespec"] +==== CertificateAuthorityDataSourceSpec -CABundleSource provides a source for CA bundle used for client-side TLS verification. +CertificateAuthorityDataSourceSpec provides a source for CA bundle used for client-side TLS verification. .Appears In: **** @@ -36,12 +36,13 @@ CABundleSource provides a source for CA bundle used for client-side TLS verifica [cols="25a,75a", options="header"] |=== | Field | Description -| *`kind`* __string__ | Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + +| *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + -For configmaps, the value associated with the key is not expected to be base64 encoded. + -| *`name`* __string__ | Name of the secret or configmap from which to read the CA bundle. + +| *`name`* __string__ | Name is the resource name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. + -| *`key`* __string__ | Key within the secret or configmap from which to read the CA bundle. + +| *`key`* __string__ | Key is the key name within the secret or configmap from which to read the CA bundle. + +The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + +certificate bundle. + |=== @@ -147,7 +148,7 @@ username from the JWT token. When not specified, it will default to "username". [id="{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-concierge-authentication-v1alpha1-tlsspec"] ==== TLSSpec -Configuration for configuring TLS on various authenticators. +TLSSpec provides TLS configuration on various authenticators. .Appears In: **** @@ -159,7 +160,8 @@ Configuration for configuring TLS on various authenticators. |=== | Field | Description | *`certificateAuthorityData`* __string__ | X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. + -| *`certificateAuthorityDataSource`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-concierge-authentication-v1alpha1-cabundlesource[$$CABundleSource$$]__ | Reference to a CA bundle in a secret or a configmap. + +| *`certificateAuthorityDataSource`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-concierge-authentication-v1alpha1-certificateauthoritydatasourcespec[$$CertificateAuthorityDataSourceSpec$$]__ | Reference to a CA bundle in a secret or a configmap. + +Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. + |=== @@ -1668,10 +1670,10 @@ Optional, when empty this defaults to "objectGUID". + |=== -[id="{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-supervisor-idp-v1alpha1-cabundlesource"] -==== CABundleSource +[id="{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-supervisor-idp-v1alpha1-certificateauthoritydatasourcespec"] +==== CertificateAuthorityDataSourceSpec -CABundleSource provides a source for CA bundle used for client-side TLS verification. +CertificateAuthorityDataSourceSpec provides a source for CA bundle used for client-side TLS verification. .Appears In: **** @@ -1681,12 +1683,13 @@ CABundleSource provides a source for CA bundle used for client-side TLS verifica [cols="25a,75a", options="header"] |=== | Field | Description -| *`kind`* __string__ | Whether the CA bundle is being sourced from a kubernetes secret or a configmap. + +| *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + -For configmaps, the value associated with the key is not expected to be base64 encoded. + -| *`name`* __string__ | Name of the secret or configmap from which to read the CA bundle. + +| *`name`* __string__ | Name is the resource name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. + -| *`key`* __string__ | Key within the secret or configmap from which to read the CA bundle. + +| *`key`* __string__ | Key is the key name within the secret or configmap from which to read the CA bundle. + +The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + +certificate bundle. + |=== @@ -2446,7 +2449,8 @@ TLSSpec provides TLS configuration for identity provider integration. |=== | Field | Description | *`certificateAuthorityData`* __string__ | X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. + -| *`certificateAuthorityDataSource`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-supervisor-idp-v1alpha1-cabundlesource[$$CABundleSource$$]__ | Reference to a CA bundle in a secret or a configmap. + +| *`certificateAuthorityDataSource`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-supervisor-idp-v1alpha1-certificateauthoritydatasourcespec[$$CertificateAuthorityDataSourceSpec$$]__ | Reference to a CA bundle in a secret or a configmap. + +Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. + |=== diff --git a/generated/latest/apis/concierge/authentication/v1alpha1/types_tls.go b/generated/latest/apis/concierge/authentication/v1alpha1/types_tls.go index cc823a05e..883dc7fc7 100644 --- a/generated/latest/apis/concierge/authentication/v1alpha1/types_tls.go +++ b/generated/latest/apis/concierge/authentication/v1alpha1/types_tls.go @@ -3,28 +3,30 @@ package v1alpha1 -// CABundleSource provides a source for CA bundle used for client-side TLS verification. -type CABundleSource struct { - // Whether the CA bundle is being sourced from a kubernetes secret or a configmap. +// CertificateAuthorityDataSourceSpec provides a source for CA bundle used for client-side TLS verification. +type CertificateAuthorityDataSourceSpec struct { + // Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. // Secrets must be of type kubernetes.io/tls or Opaque. - // For configmaps, the value associated with the key is not expected to be base64 encoded. // +kubebuilder:validation:Enum=Secret;ConfigMap Kind string `json:"kind"` - // Name of the secret or configmap from which to read the CA bundle. + // Name is the resource name of the secret or configmap from which to read the CA bundle. // The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. // +kubebuilder:validation:MinLength=1 Name string `json:"name"` - // Key within the secret or configmap from which to read the CA bundle. + // Key is the key name within the secret or configmap from which to read the CA bundle. + // The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + // certificate bundle. // +kubebuilder:validation:MinLength=1 Key string `json:"key"` } -// Configuration for configuring TLS on various authenticators. +// TLSSpec provides TLS configuration on various authenticators. type TLSSpec struct { // X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted. // +optional CertificateAuthorityData string `json:"certificateAuthorityData,omitempty"` // Reference to a CA bundle in a secret or a configmap. + // Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. // +optional - CertificateAuthorityDataSource *CABundleSource `json:"certificateAuthorityDataSource,omitempty"` + CertificateAuthorityDataSource *CertificateAuthorityDataSourceSpec `json:"certificateAuthorityDataSource,omitempty"` } diff --git a/generated/latest/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go b/generated/latest/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go index 27cbcc844..1e64ee699 100644 --- a/generated/latest/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go +++ b/generated/latest/apis/concierge/authentication/v1alpha1/zz_generated.deepcopy.go @@ -14,17 +14,17 @@ import ( ) // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CABundleSource) DeepCopyInto(out *CABundleSource) { +func (in *CertificateAuthorityDataSourceSpec) DeepCopyInto(out *CertificateAuthorityDataSourceSpec) { *out = *in return } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CABundleSource. -func (in *CABundleSource) DeepCopy() *CABundleSource { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CertificateAuthorityDataSourceSpec. +func (in *CertificateAuthorityDataSourceSpec) DeepCopy() *CertificateAuthorityDataSourceSpec { if in == nil { return nil } - out := new(CABundleSource) + out := new(CertificateAuthorityDataSourceSpec) in.DeepCopyInto(out) return out } @@ -156,7 +156,7 @@ func (in *TLSSpec) DeepCopyInto(out *TLSSpec) { *out = *in if in.CertificateAuthorityDataSource != nil { in, out := &in.CertificateAuthorityDataSource, &out.CertificateAuthorityDataSource - *out = new(CABundleSource) + *out = new(CertificateAuthorityDataSourceSpec) **out = **in } return diff --git a/generated/latest/apis/supervisor/idp/v1alpha1/types_tls.go b/generated/latest/apis/supervisor/idp/v1alpha1/types_tls.go index 19d6d863a..407a5cde5 100644 --- a/generated/latest/apis/supervisor/idp/v1alpha1/types_tls.go +++ b/generated/latest/apis/supervisor/idp/v1alpha1/types_tls.go @@ -3,19 +3,19 @@ package v1alpha1 - -// CABundleSource provides a source for CA bundle used for client-side TLS verification. -type CABundleSource struct { - // Whether the CA bundle is being sourced from a kubernetes secret or a configmap. +// CertificateAuthorityDataSourceSpec provides a source for CA bundle used for client-side TLS verification. +type CertificateAuthorityDataSourceSpec struct { + // Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. // Secrets must be of type kubernetes.io/tls or Opaque. - // For configmaps, the value associated with the key is not expected to be base64 encoded. // +kubebuilder:validation:Enum=Secret;ConfigMap Kind string `json:"kind"` - // Name of the secret or configmap from which to read the CA bundle. + // Name is the resource name of the secret or configmap from which to read the CA bundle. // The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. // +kubebuilder:validation:MinLength=1 Name string `json:"name"` - // Key within the secret or configmap from which to read the CA bundle. + // Key is the key name within the secret or configmap from which to read the CA bundle. + // The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded + // certificate bundle. // +kubebuilder:validation:MinLength=1 Key string `json:"key"` } @@ -26,6 +26,7 @@ type TLSSpec struct { // +optional CertificateAuthorityData string `json:"certificateAuthorityData,omitempty"` // Reference to a CA bundle in a secret or a configmap. + // Any changes to the CA bundle in the secret or configmap will be dynamically reloaded. // +optional - CertificateAuthorityDataSource *CABundleSource `json:"certificateAuthorityDataSource,omitempty"` + CertificateAuthorityDataSource *CertificateAuthorityDataSourceSpec `json:"certificateAuthorityDataSource,omitempty"` } diff --git a/generated/latest/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go b/generated/latest/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go index 41d44d226..395c8f0fb 100644 --- a/generated/latest/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go +++ b/generated/latest/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go @@ -204,17 +204,17 @@ func (in *ActiveDirectoryIdentityProviderUserSearchAttributes) DeepCopy() *Activ } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CABundleSource) DeepCopyInto(out *CABundleSource) { +func (in *CertificateAuthorityDataSourceSpec) DeepCopyInto(out *CertificateAuthorityDataSourceSpec) { *out = *in return } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CABundleSource. -func (in *CABundleSource) DeepCopy() *CABundleSource { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CertificateAuthorityDataSourceSpec. +func (in *CertificateAuthorityDataSourceSpec) DeepCopy() *CertificateAuthorityDataSourceSpec { if in == nil { return nil } - out := new(CABundleSource) + out := new(CertificateAuthorityDataSourceSpec) in.DeepCopyInto(out) return out } @@ -818,7 +818,7 @@ func (in *TLSSpec) DeepCopyInto(out *TLSSpec) { *out = *in if in.CertificateAuthorityDataSource != nil { in, out := &in.CertificateAuthorityDataSource, &out.CertificateAuthorityDataSource - *out = new(CABundleSource) + *out = new(CertificateAuthorityDataSourceSpec) **out = **in } return diff --git a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller_test.go b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller_test.go index bd5ea7c2b..1f9dbd693 100644 --- a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller_test.go +++ b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller_test.go @@ -309,7 +309,7 @@ func TestController(t *testing.T) { Issuer: goodIssuer, Audience: goodAudience, TLS: &authenticationv1alpha1.TLSSpec{ - CertificateAuthorityDataSource: &authenticationv1alpha1.CABundleSource{ + CertificateAuthorityDataSource: &authenticationv1alpha1.CertificateAuthorityDataSourceSpec{ Kind: "Secret", Name: "secret-with-ca", Key: "ca.crt", @@ -330,7 +330,7 @@ func TestController(t *testing.T) { Issuer: goodIssuer, Audience: goodAudience, TLS: &authenticationv1alpha1.TLSSpec{ - CertificateAuthorityDataSource: &authenticationv1alpha1.CABundleSource{ + CertificateAuthorityDataSource: &authenticationv1alpha1.CertificateAuthorityDataSourceSpec{ Kind: "ConfigMap", Name: "configmap-with-ca", Key: "ca.crt", diff --git a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go index 35c530240..b75548a3a 100644 --- a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go +++ b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go @@ -155,7 +155,7 @@ func TestController(t *testing.T) { goodWebhookAuthenticatorSpecWithCAFromSecret := authenticationv1alpha1.WebhookAuthenticatorSpec{ Endpoint: goodWebhookDefaultServingCertEndpoint, TLS: &authenticationv1alpha1.TLSSpec{ - CertificateAuthorityDataSource: &authenticationv1alpha1.CABundleSource{ + CertificateAuthorityDataSource: &authenticationv1alpha1.CertificateAuthorityDataSourceSpec{ Kind: "Secret", Name: "secret-with-ca", Key: "ca.crt", @@ -175,7 +175,7 @@ func TestController(t *testing.T) { goodWebhookAuthenticatorSpecWithCAFromConfigMap := authenticationv1alpha1.WebhookAuthenticatorSpec{ Endpoint: goodWebhookDefaultServingCertEndpoint, TLS: &authenticationv1alpha1.TLSSpec{ - CertificateAuthorityDataSource: &authenticationv1alpha1.CABundleSource{ + CertificateAuthorityDataSource: &authenticationv1alpha1.CertificateAuthorityDataSourceSpec{ Kind: "ConfigMap", Name: "configmap-with-ca", Key: "ca.crt", diff --git a/internal/controller/supervisorconfig/activedirectoryupstreamwatcher/active_directory_upstream_watcher_test.go b/internal/controller/supervisorconfig/activedirectoryupstreamwatcher/active_directory_upstream_watcher_test.go index 320ecbb06..42cee8d20 100644 --- a/internal/controller/supervisorconfig/activedirectoryupstreamwatcher/active_directory_upstream_watcher_test.go +++ b/internal/controller/supervisorconfig/activedirectoryupstreamwatcher/active_directory_upstream_watcher_test.go @@ -275,7 +275,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { validUpstreamWithConfigMapCABundleSource := validUpstream.DeepCopy() validUpstreamWithConfigMapCABundleSource.Spec.TLS.CertificateAuthorityData = "" - validUpstreamWithConfigMapCABundleSource.Spec.TLS.CertificateAuthorityDataSource = &idpv1alpha1.CABundleSource{ + validUpstreamWithConfigMapCABundleSource.Spec.TLS.CertificateAuthorityDataSource = &idpv1alpha1.CertificateAuthorityDataSourceSpec{ Kind: "ConfigMap", Name: caBundleConfigMapName, Key: "ca.crt", @@ -289,7 +289,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { validUpstreamWithOpaqueSecretCABundleSource := validUpstream.DeepCopy() validUpstreamWithOpaqueSecretCABundleSource.Spec.TLS.CertificateAuthorityData = "" - validUpstreamWithOpaqueSecretCABundleSource.Spec.TLS.CertificateAuthorityDataSource = &idpv1alpha1.CABundleSource{ + validUpstreamWithOpaqueSecretCABundleSource.Spec.TLS.CertificateAuthorityDataSource = &idpv1alpha1.CertificateAuthorityDataSourceSpec{ Kind: "Secret", Name: caBundleSecretName, Key: "ca.crt", @@ -304,7 +304,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { validUpstreamWithTLSSecretCABundleSource := validUpstream.DeepCopy() validUpstreamWithTLSSecretCABundleSource.Spec.TLS.CertificateAuthorityData = "" - validUpstreamWithTLSSecretCABundleSource.Spec.TLS.CertificateAuthorityDataSource = &idpv1alpha1.CABundleSource{ + validUpstreamWithTLSSecretCABundleSource.Spec.TLS.CertificateAuthorityDataSource = &idpv1alpha1.CertificateAuthorityDataSourceSpec{ Kind: "Secret", Name: caBundleSecretName, Key: "ca.crt", diff --git a/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher_test.go b/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher_test.go index 437c5620a..efaae07b8 100644 --- a/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher_test.go +++ b/internal/controller/supervisorconfig/githubupstreamwatcher/github_upstream_watcher_test.go @@ -894,7 +894,7 @@ func TestController(t *testing.T) { otherIDP := validFilledOutIDP.DeepCopy() otherIDP.Name = "idp-with-tls-in-secret" otherIDP.Spec.GitHubAPI.TLS = &idpv1alpha1.TLSSpec{ - CertificateAuthorityDataSource: &idpv1alpha1.CABundleSource{ + CertificateAuthorityDataSource: &idpv1alpha1.CertificateAuthorityDataSourceSpec{ Kind: "Secret", Name: goodCABundleSecret.Name, Key: "ca.crt", @@ -906,7 +906,7 @@ func TestController(t *testing.T) { otherIDP := validFilledOutIDP.DeepCopy() otherIDP.Name = "idp-with-tls-in-config-map" otherIDP.Spec.GitHubAPI.TLS = &idpv1alpha1.TLSSpec{ - CertificateAuthorityDataSource: &idpv1alpha1.CABundleSource{ + CertificateAuthorityDataSource: &idpv1alpha1.CertificateAuthorityDataSourceSpec{ Kind: "ConfigMap", Name: goodCABundleConfigMap.Name, Key: "ca.crt", @@ -975,7 +975,7 @@ func TestController(t *testing.T) { Spec: func() idpv1alpha1.GitHubIdentityProviderSpec { otherSpec := validFilledOutIDP.Spec.DeepCopy() otherSpec.GitHubAPI.TLS = &idpv1alpha1.TLSSpec{ - CertificateAuthorityDataSource: &idpv1alpha1.CABundleSource{ + CertificateAuthorityDataSource: &idpv1alpha1.CertificateAuthorityDataSourceSpec{ Kind: "ConfigMap", Name: goodCABundleSecret.Name, Key: "ca.crt", @@ -1004,7 +1004,7 @@ func TestController(t *testing.T) { Spec: func() idpv1alpha1.GitHubIdentityProviderSpec { otherSpec := validFilledOutIDP.Spec.DeepCopy() otherSpec.GitHubAPI.TLS = &idpv1alpha1.TLSSpec{ - CertificateAuthorityDataSource: &idpv1alpha1.CABundleSource{ + CertificateAuthorityDataSource: &idpv1alpha1.CertificateAuthorityDataSourceSpec{ Kind: "Secret", Name: goodCABundleSecret.Name, Key: "ca.crt", diff --git a/internal/controller/supervisorconfig/ldapupstreamwatcher/ldap_upstream_watcher_test.go b/internal/controller/supervisorconfig/ldapupstreamwatcher/ldap_upstream_watcher_test.go index 6fba51ab9..f529e9182 100644 --- a/internal/controller/supervisorconfig/ldapupstreamwatcher/ldap_upstream_watcher_test.go +++ b/internal/controller/supervisorconfig/ldapupstreamwatcher/ldap_upstream_watcher_test.go @@ -284,7 +284,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { validUpstreamWithConfigMapCABundleSource := validUpstream.DeepCopy() validUpstreamWithConfigMapCABundleSource.Spec.TLS.CertificateAuthorityData = "" - validUpstreamWithConfigMapCABundleSource.Spec.TLS.CertificateAuthorityDataSource = &idpv1alpha1.CABundleSource{ + validUpstreamWithConfigMapCABundleSource.Spec.TLS.CertificateAuthorityDataSource = &idpv1alpha1.CertificateAuthorityDataSourceSpec{ Kind: "ConfigMap", Name: caBundleConfigMapName, Key: "ca.crt", @@ -298,7 +298,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { validUpstreamWithOpaqueSecretCABundleSource := validUpstream.DeepCopy() validUpstreamWithOpaqueSecretCABundleSource.Spec.TLS.CertificateAuthorityData = "" - validUpstreamWithOpaqueSecretCABundleSource.Spec.TLS.CertificateAuthorityDataSource = &idpv1alpha1.CABundleSource{ + validUpstreamWithOpaqueSecretCABundleSource.Spec.TLS.CertificateAuthorityDataSource = &idpv1alpha1.CertificateAuthorityDataSourceSpec{ Kind: "Secret", Name: caBundleSecretName, Key: "ca.crt", @@ -313,7 +313,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { validUpstreamWithTLSSecretCABundleSource := validUpstream.DeepCopy() validUpstreamWithTLSSecretCABundleSource.Spec.TLS.CertificateAuthorityData = "" - validUpstreamWithTLSSecretCABundleSource.Spec.TLS.CertificateAuthorityDataSource = &idpv1alpha1.CABundleSource{ + validUpstreamWithTLSSecretCABundleSource.Spec.TLS.CertificateAuthorityDataSource = &idpv1alpha1.CertificateAuthorityDataSourceSpec{ Kind: "Secret", Name: caBundleSecretName, Key: "ca.crt", diff --git a/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher_test.go b/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher_test.go index 57f6481a2..659e25e8c 100644 --- a/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher_test.go +++ b/internal/controller/supervisorconfig/oidcupstreamwatcher/oidc_upstream_watcher_test.go @@ -1162,7 +1162,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { Spec: idpv1alpha1.OIDCIdentityProviderSpec{ Issuer: testIssuerURL, TLS: &idpv1alpha1.TLSSpec{ - CertificateAuthorityDataSource: &idpv1alpha1.CABundleSource{ + CertificateAuthorityDataSource: &idpv1alpha1.CertificateAuthorityDataSourceSpec{ Kind: "Secret", Name: "ca-bundle-secret", Key: "ca.crt", @@ -1229,7 +1229,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) { Spec: idpv1alpha1.OIDCIdentityProviderSpec{ Issuer: testIssuerURL, TLS: &idpv1alpha1.TLSSpec{ - CertificateAuthorityDataSource: &idpv1alpha1.CABundleSource{ + CertificateAuthorityDataSource: &idpv1alpha1.CertificateAuthorityDataSourceSpec{ Kind: "ConfigMap", Name: "ca-bundle-configmap", Key: "ca.crt", diff --git a/internal/controller/tlsconfigutil/tls_config_util_test.go b/internal/controller/tlsconfigutil/tls_config_util_test.go index 52fbdff87..230432e46 100644 --- a/internal/controller/tlsconfigutil/tls_config_util_test.go +++ b/internal/controller/tlsconfigutil/tls_config_util_test.go @@ -535,7 +535,7 @@ func TestTLSSpecForSupervisor(t *testing.T) { { name: "should return tls spec with certificateAuthorityDataSource", supervisorTLSSpec: &idpv1alpha1.TLSSpec{ - CertificateAuthorityDataSource: &idpv1alpha1.CABundleSource{ + CertificateAuthorityDataSource: &idpv1alpha1.CertificateAuthorityDataSourceSpec{ Kind: "Secret", Name: "awesome-secret", Key: "ca-bundle", @@ -553,7 +553,7 @@ func TestTLSSpecForSupervisor(t *testing.T) { name: "should return tls spec when source has all fields filled", supervisorTLSSpec: &idpv1alpha1.TLSSpec{ CertificateAuthorityData: base64EncodedBundle, - CertificateAuthorityDataSource: &idpv1alpha1.CABundleSource{ + CertificateAuthorityDataSource: &idpv1alpha1.CertificateAuthorityDataSourceSpec{ Kind: "Secret", Name: "awesome-secret", Key: "ca-bundle", @@ -609,7 +609,7 @@ func TestTLSSpecForConcierge(t *testing.T) { { name: "should return tls spec with certificateAuthorityDataSource", conciergeTLSSpec: &authenticationv1alpha1.TLSSpec{ - CertificateAuthorityDataSource: &authenticationv1alpha1.CABundleSource{ + CertificateAuthorityDataSource: &authenticationv1alpha1.CertificateAuthorityDataSourceSpec{ Kind: "Secret", Name: "awesome-secret", Key: "ca-bundle", @@ -627,7 +627,7 @@ func TestTLSSpecForConcierge(t *testing.T) { name: "should return tls spec when source has all fields filled", conciergeTLSSpec: &authenticationv1alpha1.TLSSpec{ CertificateAuthorityData: base64EncodedBundle, - CertificateAuthorityDataSource: &authenticationv1alpha1.CABundleSource{ + CertificateAuthorityDataSource: &authenticationv1alpha1.CertificateAuthorityDataSourceSpec{ Kind: "Secret", Name: "awesome-secret", Key: "ca-bundle", diff --git a/test/integration/concierge_client_test.go b/test/integration/concierge_client_test.go index 2d6eb428a..78b3c8ca1 100644 --- a/test/integration/concierge_client_test.go +++ b/test/integration/concierge_client_test.go @@ -83,7 +83,7 @@ func TestClient(t *testing.T) { "tls.key": "", }) spec.TLS.CertificateAuthorityData = "" - spec.TLS.CertificateAuthorityDataSource = &authenticationv1alpha1.CABundleSource{ + spec.TLS.CertificateAuthorityDataSource = &authenticationv1alpha1.CertificateAuthorityDataSourceSpec{ Kind: "Secret", Name: caSecret.Name, Key: "ca.crt", @@ -98,7 +98,7 @@ func TestClient(t *testing.T) { "ca.crt": string(TLSCABundle), }) spec.TLS.CertificateAuthorityData = "" - spec.TLS.CertificateAuthorityDataSource = &authenticationv1alpha1.CABundleSource{ + spec.TLS.CertificateAuthorityDataSource = &authenticationv1alpha1.CertificateAuthorityDataSourceSpec{ Kind: "Secret", Name: caSecret.Name, Key: "ca.crt", @@ -113,7 +113,7 @@ func TestClient(t *testing.T) { "ca.crt": string(TLSCABundle), }) spec.TLS.CertificateAuthorityData = "" - spec.TLS.CertificateAuthorityDataSource = &authenticationv1alpha1.CABundleSource{ + spec.TLS.CertificateAuthorityDataSource = &authenticationv1alpha1.CertificateAuthorityDataSourceSpec{ Kind: "ConfigMap", Name: caConfigmap.Name, Key: "ca.crt", diff --git a/test/integration/concierge_jwtauthenticator_status_test.go b/test/integration/concierge_jwtauthenticator_status_test.go index 09f0d56a2..32471f72e 100644 --- a/test/integration/concierge_jwtauthenticator_status_test.go +++ b/test/integration/concierge_jwtauthenticator_status_test.go @@ -93,7 +93,7 @@ func TestConciergeJWTAuthenticatorWithExternalCABundleStatusIsUpdatedWhenExterna Issuer: env.SupervisorUpstreamOIDC.Issuer, Audience: "does-not-matter", TLS: &authenticationv1alpha1.TLSSpec{ - CertificateAuthorityDataSource: &authenticationv1alpha1.CABundleSource{ + CertificateAuthorityDataSource: &authenticationv1alpha1.CertificateAuthorityDataSourceSpec{ Kind: test.caBundleSourceSpecKind, Name: caBundleResourceName, Key: "ca.crt", diff --git a/test/integration/concierge_webhookauthenticator_status_test.go b/test/integration/concierge_webhookauthenticator_status_test.go index 119909f6c..f74549a8d 100644 --- a/test/integration/concierge_webhookauthenticator_status_test.go +++ b/test/integration/concierge_webhookauthenticator_status_test.go @@ -90,7 +90,7 @@ func TestConciergeWebhookAuthenticatorWithExternalCABundleStatusIsUpdatedWhenExt authenticator := testlib.CreateTestWebhookAuthenticator(ctx, t, &authenticationv1alpha1.WebhookAuthenticatorSpec{ Endpoint: env.TestWebhook.Endpoint, TLS: &authenticationv1alpha1.TLSSpec{ - CertificateAuthorityDataSource: &authenticationv1alpha1.CABundleSource{ + CertificateAuthorityDataSource: &authenticationv1alpha1.CertificateAuthorityDataSourceSpec{ Kind: test.caBundleSourceSpecKind, Name: caBundleResourceName, Key: "ca.crt", diff --git a/test/integration/e2e_test.go b/test/integration/e2e_test.go index 89edb8d84..7732e4da0 100644 --- a/test/integration/e2e_test.go +++ b/test/integration/e2e_test.go @@ -237,7 +237,7 @@ func TestE2EFullIntegration_Browser(t *testing.T) { }) jwtAuthnSpec := defaultJWTAuthenticatorSpec.DeepCopy() jwtAuthnSpec.TLS.CertificateAuthorityData = "" - jwtAuthnSpec.TLS.CertificateAuthorityDataSource = &authenticationv1alpha1.CABundleSource{ + jwtAuthnSpec.TLS.CertificateAuthorityDataSource = &authenticationv1alpha1.CertificateAuthorityDataSourceSpec{ Kind: "Secret", Name: caSecret.Name, Key: "ca.crt", @@ -339,7 +339,7 @@ func TestE2EFullIntegration_Browser(t *testing.T) { }) jwtAuthnSpec := defaultJWTAuthenticatorSpec.DeepCopy() jwtAuthnSpec.TLS.CertificateAuthorityData = "" - jwtAuthnSpec.TLS.CertificateAuthorityDataSource = &authenticationv1alpha1.CABundleSource{ + jwtAuthnSpec.TLS.CertificateAuthorityDataSource = &authenticationv1alpha1.CertificateAuthorityDataSourceSpec{ Kind: "Secret", Name: caSecret.Name, Key: "ca.crt", @@ -477,7 +477,7 @@ func TestE2EFullIntegration_Browser(t *testing.T) { }) jwtAuthnSpec := defaultJWTAuthenticatorSpec.DeepCopy() jwtAuthnSpec.TLS.CertificateAuthorityData = "" - jwtAuthnSpec.TLS.CertificateAuthorityDataSource = &authenticationv1alpha1.CABundleSource{ + jwtAuthnSpec.TLS.CertificateAuthorityDataSource = &authenticationv1alpha1.CertificateAuthorityDataSourceSpec{ Kind: "ConfigMap", Name: caConfigMap.Name, Key: "ca.crt", diff --git a/test/integration/supervisor_login_test.go b/test/integration/supervisor_login_test.go index 8ac9086e8..c5dc0ad60 100644 --- a/test/integration/supervisor_login_test.go +++ b/test/integration/supervisor_login_test.go @@ -375,7 +375,7 @@ func TestSupervisorLogin_Browser(t *testing.T) { "ca.crt": string(caData), }) idpSpec.TLS.CertificateAuthorityData = "" - idpSpec.TLS.CertificateAuthorityDataSource = &idpv1alpha1.CABundleSource{ + idpSpec.TLS.CertificateAuthorityDataSource = &idpv1alpha1.CertificateAuthorityDataSourceSpec{ Kind: "Secret", Name: caSecret.Name, Key: "ca.crt", @@ -405,7 +405,7 @@ func TestSupervisorLogin_Browser(t *testing.T) { "tls.key": "", }) idpSpec.TLS.CertificateAuthorityData = "" - idpSpec.TLS.CertificateAuthorityDataSource = &idpv1alpha1.CABundleSource{ + idpSpec.TLS.CertificateAuthorityDataSource = &idpv1alpha1.CertificateAuthorityDataSourceSpec{ Kind: "Secret", Name: caSecret.Name, Key: "ca.crt", @@ -432,7 +432,7 @@ func TestSupervisorLogin_Browser(t *testing.T) { "ca.crt": string(caData), }) idpSpec.TLS.CertificateAuthorityData = "" - idpSpec.TLS.CertificateAuthorityDataSource = &idpv1alpha1.CABundleSource{ + idpSpec.TLS.CertificateAuthorityDataSource = &idpv1alpha1.CertificateAuthorityDataSourceSpec{ Kind: "ConfigMap", Name: caConfigMap.Name, Key: "ca.crt", @@ -461,7 +461,7 @@ func TestSupervisorLogin_Browser(t *testing.T) { "ca.crt": string(caData), }) idpSpec.TLS.CertificateAuthorityData = "" - idpSpec.TLS.CertificateAuthorityDataSource = &idpv1alpha1.CABundleSource{ + idpSpec.TLS.CertificateAuthorityDataSource = &idpv1alpha1.CertificateAuthorityDataSourceSpec{ Kind: "Secret", Name: caSecret.Name, Key: "ca.crt", @@ -684,7 +684,7 @@ func TestSupervisorLogin_Browser(t *testing.T) { "ca.crt": env.SupervisorUpstreamLDAP.CABundle, }) spec.TLS.CertificateAuthorityData = "" - spec.TLS.CertificateAuthorityDataSource = &idpv1alpha1.CABundleSource{ + spec.TLS.CertificateAuthorityDataSource = &idpv1alpha1.CertificateAuthorityDataSourceSpec{ Kind: "Secret", Name: caSecret.Name, Key: "ca.crt", @@ -735,7 +735,7 @@ func TestSupervisorLogin_Browser(t *testing.T) { "tls.key": "", }) spec.TLS.CertificateAuthorityData = "" - spec.TLS.CertificateAuthorityDataSource = &idpv1alpha1.CABundleSource{ + spec.TLS.CertificateAuthorityDataSource = &idpv1alpha1.CertificateAuthorityDataSourceSpec{ Kind: "Secret", Name: caSecret.Name, Key: "ca.crt", @@ -784,7 +784,7 @@ func TestSupervisorLogin_Browser(t *testing.T) { "ca.crt": env.SupervisorUpstreamLDAP.CABundle, }) spec.TLS.CertificateAuthorityData = "" - spec.TLS.CertificateAuthorityDataSource = &idpv1alpha1.CABundleSource{ + spec.TLS.CertificateAuthorityDataSource = &idpv1alpha1.CertificateAuthorityDataSourceSpec{ Kind: "ConfigMap", Name: caConfigMap.Name, Key: "ca.crt", @@ -1270,7 +1270,7 @@ func TestSupervisorLogin_Browser(t *testing.T) { "ca.crt": env.SupervisorUpstreamActiveDirectory.CABundle, }) spec.TLS.CertificateAuthorityData = "" - spec.TLS.CertificateAuthorityDataSource = &idpv1alpha1.CABundleSource{ + spec.TLS.CertificateAuthorityDataSource = &idpv1alpha1.CertificateAuthorityDataSourceSpec{ Kind: "Secret", Name: caSecret.Name, Key: "ca.crt", @@ -1312,7 +1312,7 @@ func TestSupervisorLogin_Browser(t *testing.T) { "tls.key": "", }) spec.TLS.CertificateAuthorityData = "" - spec.TLS.CertificateAuthorityDataSource = &idpv1alpha1.CABundleSource{ + spec.TLS.CertificateAuthorityDataSource = &idpv1alpha1.CertificateAuthorityDataSourceSpec{ Kind: "Secret", Name: caSecret.Name, Key: "ca.crt", @@ -1352,7 +1352,7 @@ func TestSupervisorLogin_Browser(t *testing.T) { "ca.crt": env.SupervisorUpstreamActiveDirectory.CABundle, }) spec.TLS.CertificateAuthorityData = "" - spec.TLS.CertificateAuthorityDataSource = &idpv1alpha1.CABundleSource{ + spec.TLS.CertificateAuthorityDataSource = &idpv1alpha1.CertificateAuthorityDataSourceSpec{ Kind: "ConfigMap", Name: caConfigMap.Name, Key: "ca.crt", From 2181418cc57dac7be485fa454e93f5e7e69c7d3e Mon Sep 17 00:00:00 2001 From: Ryan Richard Date: Thu, 1 Aug 2024 16:27:13 -0700 Subject: [PATCH 84/99] refactor test helpers in supervisor_login_test.go Co-authored-by: Ashish Amarnath --- test/integration/supervisor_login_test.go | 148 ++++++---------------- 1 file changed, 42 insertions(+), 106 deletions(-) diff --git a/test/integration/supervisor_login_test.go b/test/integration/supervisor_login_test.go index c5dc0ad60..c9b14fa4d 100644 --- a/test/integration/supervisor_login_test.go +++ b/test/integration/supervisor_login_test.go @@ -2824,32 +2824,8 @@ func requireSuccessfulLDAPIdentityProviderConditions( expectedLDAPConnectionValidMessage string, caBundleConfigured bool, ) { - require.Len(t, ldapIDP.Status.Conditions, 3) - - conditionsSummary := [][]string{} - for _, condition := range ldapIDP.Status.Conditions { - conditionsSummary = append(conditionsSummary, []string{condition.Type, string(condition.Status), condition.Reason}) - t.Logf("Saw LDAPIdentityProvider Status.Condition Type=%s Status=%s Reason=%s Message=%s", - condition.Type, string(condition.Status), condition.Reason, condition.Message) - switch condition.Type { - case "BindSecretValid": - require.Equal(t, "loaded bind secret", condition.Message) - case "TLSConfigurationValid": - if caBundleConfigured { - require.Equal(t, "spec.tls is valid: using configured CA bundle", condition.Message) - } else { - require.Equal(t, "spec.tls is valid: no TLS configuration provided: using default root CA bundle from container image", condition.Message) - } - case "LDAPConnectionValid": - require.Equal(t, expectedLDAPConnectionValidMessage, condition.Message) - } - } - - require.ElementsMatch(t, [][]string{ - {"BindSecretValid", "True", "Success"}, - {"TLSConfigurationValid", "True", "Success"}, - {"LDAPConnectionValid", "True", "Success"}, - }, conditionsSummary) + requireEventuallySuccessfulLDAPIdentityProviderConditions(t, + require.New(t), ldapIDP, expectedLDAPConnectionValidMessage, caBundleConfigured) } func requireSuccessfulActiveDirectoryIdentityProviderConditions( @@ -2858,106 +2834,37 @@ func requireSuccessfulActiveDirectoryIdentityProviderConditions( expectedActiveDirectoryConnectionValidMessage string, caBundleConfigured bool, ) { - require.Len(t, adIDP.Status.Conditions, 4) - - conditionsSummary := [][]string{} - for _, condition := range adIDP.Status.Conditions { - conditionsSummary = append(conditionsSummary, []string{condition.Type, string(condition.Status), condition.Reason}) - t.Logf("Saw ActiveDirectoryIdentityProvider Status.Condition Type=%s Status=%s Reason=%s Message=%s", - condition.Type, string(condition.Status), condition.Reason, condition.Message) - switch condition.Type { - case "BindSecretValid": - require.Equal(t, "loaded bind secret", condition.Message) - case "TLSConfigurationValid": - if caBundleConfigured { - require.Equal(t, "spec.tls is valid: using configured CA bundle", condition.Message) - } else { - require.Equal(t, "spec.tls is valid: no TLS configuration provided: using default root CA bundle from container image", condition.Message) - } - case "LDAPConnectionValid": - require.Equal(t, expectedActiveDirectoryConnectionValidMessage, condition.Message) - } - } - - expectedUserSearchReason := "" - if adIDP.Spec.UserSearch.Base == "" || adIDP.Spec.GroupSearch.Base == "" { - expectedUserSearchReason = "Success" - } else { - expectedUserSearchReason = "UsingConfigurationFromSpec" - } - - require.ElementsMatch(t, [][]string{ - {"BindSecretValid", "True", "Success"}, - {"TLSConfigurationValid", "True", "Success"}, - {"LDAPConnectionValid", "True", "Success"}, - {"SearchBaseFound", "True", expectedUserSearchReason}, - }, conditionsSummary) + requireEventuallySuccessfulActiveDirectoryIdentityProviderConditions(t, + require.New(t), adIDP, expectedActiveDirectoryConnectionValidMessage, caBundleConfigured) } func requireEventuallySuccessfulLDAPIdentityProviderConditions( t *testing.T, - requireEventually *require.Assertions, + assertions *require.Assertions, ldapIDP *idpv1alpha1.LDAPIdentityProvider, expectedLDAPConnectionValidMessage string, caBundleConfigured bool, ) { t.Helper() - requireEventually.Len(ldapIDP.Status.Conditions, 3) + assertions.Len(ldapIDP.Status.Conditions, 3) - conditionsSummary := [][]string{} - for _, condition := range ldapIDP.Status.Conditions { - conditionsSummary = append(conditionsSummary, []string{condition.Type, string(condition.Status), condition.Reason}) - t.Logf("Saw ActiveDirectoryIdentityProvider Status.Condition Type=%s Status=%s Reason=%s Message=%s", - condition.Type, string(condition.Status), condition.Reason, condition.Message) - switch condition.Type { - case "BindSecretValid": - requireEventually.Equal("loaded bind secret", condition.Message) - case "TLSConfigurationValid": - if caBundleConfigured { - require.Equal(t, "spec.tls is valid: using configured CA bundle", condition.Message) - } else { - require.Equal(t, "spec.tls is valid: no TLS configuration provided: using default root CA bundle from container image", condition.Message) - } - case "LDAPConnectionValid": - requireEventually.Equal(expectedLDAPConnectionValidMessage, condition.Message) - } - } - - requireEventually.ElementsMatch([][]string{ + assertions.ElementsMatch([][]string{ {"BindSecretValid", "True", "Success"}, {"TLSConfigurationValid", "True", "Success"}, {"LDAPConnectionValid", "True", "Success"}, - }, conditionsSummary) + }, conditionsSummaryFromActualConditions(t, + assertions, ldapIDP.Status.Conditions, caBundleConfigured, expectedLDAPConnectionValidMessage)) } func requireEventuallySuccessfulActiveDirectoryIdentityProviderConditions( t *testing.T, - requireEventually *require.Assertions, + assertions *require.Assertions, adIDP *idpv1alpha1.ActiveDirectoryIdentityProvider, expectedActiveDirectoryConnectionValidMessage string, caBundleConfigured bool, ) { t.Helper() - requireEventually.Len(adIDP.Status.Conditions, 4) - - conditionsSummary := [][]string{} - for _, condition := range adIDP.Status.Conditions { - conditionsSummary = append(conditionsSummary, []string{condition.Type, string(condition.Status), condition.Reason}) - t.Logf("Saw ActiveDirectoryIdentityProvider Status.Condition Type=%s Status=%s Reason=%s Message=%s", - condition.Type, string(condition.Status), condition.Reason, condition.Message) - switch condition.Type { - case "BindSecretValid": - requireEventually.Equal("loaded bind secret", condition.Message) - case "TLSConfigurationValid": - if caBundleConfigured { - require.Equal(t, "spec.tls is valid: using configured CA bundle", condition.Message) - } else { - require.Equal(t, "spec.tls is valid: no TLS configuration provided: using default root CA bundle from container image", condition.Message) - } - case "LDAPConnectionValid": - requireEventually.Equal(expectedActiveDirectoryConnectionValidMessage, condition.Message) - } - } + assertions.Len(adIDP.Status.Conditions, 4) expectedUserSearchReason := "" if adIDP.Spec.UserSearch.Base == "" || adIDP.Spec.GroupSearch.Base == "" { @@ -2966,12 +2873,41 @@ func requireEventuallySuccessfulActiveDirectoryIdentityProviderConditions( expectedUserSearchReason = "UsingConfigurationFromSpec" } - requireEventually.ElementsMatch([][]string{ + assertions.ElementsMatch([][]string{ {"BindSecretValid", "True", "Success"}, {"TLSConfigurationValid", "True", "Success"}, {"LDAPConnectionValid", "True", "Success"}, {"SearchBaseFound", "True", expectedUserSearchReason}, - }, conditionsSummary) + }, conditionsSummaryFromActualConditions(t, + assertions, adIDP.Status.Conditions, caBundleConfigured, expectedActiveDirectoryConnectionValidMessage)) +} + +func conditionsSummaryFromActualConditions( + t *testing.T, + assertions *require.Assertions, + conditions []metav1.Condition, + caBundleConfigured bool, + expectedLDAPConnectionValidMessage string, +) [][]string { + conditionsSummary := [][]string{} + for _, condition := range conditions { + conditionsSummary = append(conditionsSummary, []string{condition.Type, string(condition.Status), condition.Reason}) + t.Logf("Saw identity provider with Status.Condition Type=%s Status=%s Reason=%s Message=%s", + condition.Type, string(condition.Status), condition.Reason, condition.Message) + switch condition.Type { + case "BindSecretValid": + assertions.Equal("loaded bind secret", condition.Message) + case "TLSConfigurationValid": + if caBundleConfigured { + assertions.Equal("spec.tls is valid: using configured CA bundle", condition.Message) + } else { + assertions.Equal("spec.tls is valid: no TLS configuration provided: using default root CA bundle from container image", condition.Message) + } + case "LDAPConnectionValid": + assertions.Equal(expectedLDAPConnectionValidMessage, condition.Message) + } + } + return conditionsSummary } func testSupervisorLogin( From c3405095b23db9aa4e64eb42829c48814954bab9 Mon Sep 17 00:00:00 2001 From: Ashish Amarnath Date: Fri, 2 Aug 2024 23:28:57 -0700 Subject: [PATCH 85/99] Add integration tests for tls spec validation in JWTAuthenticator and WebhookAuthenticator Signed-off-by: Ashish Amarnath --- .../concierge_jwtauthenticator_status_test.go | 295 ++++++++++++++++++ ...cierge_webhookauthenticator_status_test.go | 220 +++++++++++++ 2 files changed, 515 insertions(+) diff --git a/test/integration/concierge_jwtauthenticator_status_test.go b/test/integration/concierge_jwtauthenticator_status_test.go index 32471f72e..4c992c193 100644 --- a/test/integration/concierge_jwtauthenticator_status_test.go +++ b/test/integration/concierge_jwtauthenticator_status_test.go @@ -164,6 +164,10 @@ func TestConciergeJWTAuthenticatorStatus_Parallel(t *testing.T) { unusedLocalhostPort := findRecentlyUnusedLocalhostPorts(t, 1)[0] + badCABundleConfigMap := testlib.CreateTestConfigMap(t, env.ConciergeNamespace, "ca-bundle", map[string]string{ + "ca.crt": "This is not a real CA bundle", + }) + tests := []struct { name string spec authenticationv1alpha1.JWTAuthenticatorSpec @@ -322,6 +326,297 @@ func TestConciergeJWTAuthenticatorStatus_Parallel(t *testing.T) { }, ), }, + { + name: "invalid when spec.tls supplies both certificateAuthorityData and certificateAuthorityDataSource", + spec: authenticationv1alpha1.JWTAuthenticatorSpec{ + Issuer: env.CLIUpstreamOIDC.Issuer, + Audience: "foo", + Claims: authenticationv1alpha1.JWTTokenClaims{ + Groups: "", + Username: "", + }, + TLS: &authenticationv1alpha1.TLSSpec{ + CertificateAuthorityData: "pretend-this-is-a-certificate", + CertificateAuthorityDataSource: &authenticationv1alpha1.CertificateAuthorityDataSourceSpec{ + Kind: "ConfigMap", + Name: "does-not-matter", + Key: "also-does-not-matter", + }, + }, + }, + wantConditions: []metav1.Condition{ + { + Type: "AuthenticatorValid", + Status: "Unknown", + Reason: "UnableToValidate", + Message: "unable to validate; see other conditions for details", + }, { + Type: "DiscoveryURLValid", + Status: "Unknown", + Reason: "UnableToValidate", + Message: "unable to validate; see other conditions for details", + }, { + Type: "IssuerURLValid", + Status: "True", + Reason: "Success", + Message: "issuer is a valid URL", + }, { + Type: "JWKSFetchValid", + Status: "Unknown", + Reason: "UnableToValidate", + Message: "unable to validate; see other conditions for details", + }, { + Type: "JWKSURLValid", + Status: "Unknown", + Reason: "UnableToValidate", + Message: "unable to validate; see other conditions for details", + }, { + Type: "Ready", + Status: "False", + Reason: "NotReady", + Message: "the JWTAuthenticator is not ready: see other conditions for details", + }, + { + Type: "TLSConfigurationValid", + Status: "False", + Reason: "InvalidTLSConfig", + Message: "spec.tls is invalid: both tls.certificateAuthorityDataSource and tls.certificateAuthorityData provided", + }, + }, + wantPhase: authenticationv1alpha1.JWTAuthenticatorPhaseError, + }, + { + name: "invalid when spec.tls.certificateAuthorityDataSource refers to a configmap that does not exist", + spec: authenticationv1alpha1.JWTAuthenticatorSpec{ + Issuer: env.CLIUpstreamOIDC.Issuer, + Audience: "foo", + Claims: authenticationv1alpha1.JWTTokenClaims{ + Groups: "", + Username: "", + }, + TLS: &authenticationv1alpha1.TLSSpec{ + CertificateAuthorityDataSource: &authenticationv1alpha1.CertificateAuthorityDataSourceSpec{ + Kind: "ConfigMap", + Name: "does-not-exist", + Key: "does-not-matter", + }, + }, + }, + wantConditions: []metav1.Condition{ + { + Type: "AuthenticatorValid", + Status: "Unknown", + Reason: "UnableToValidate", + Message: "unable to validate; see other conditions for details", + }, { + Type: "DiscoveryURLValid", + Status: "Unknown", + Reason: "UnableToValidate", + Message: "unable to validate; see other conditions for details", + }, { + Type: "IssuerURLValid", + Status: "True", + Reason: "Success", + Message: "issuer is a valid URL", + }, { + Type: "JWKSFetchValid", + Status: "Unknown", + Reason: "UnableToValidate", + Message: "unable to validate; see other conditions for details", + }, { + Type: "JWKSURLValid", + Status: "Unknown", + Reason: "UnableToValidate", + Message: "unable to validate; see other conditions for details", + }, { + Type: "Ready", + Status: "False", + Reason: "NotReady", + Message: "the JWTAuthenticator is not ready: see other conditions for details", + }, + { + Type: "TLSConfigurationValid", + Status: "False", + Reason: "InvalidTLSConfig", + Message: "spec.tls.certificateAuthorityDataSource is invalid: failed to get configmap \"concierge/does-not-exist\": configmap \"does-not-exist\" not found", + }, + }, + wantPhase: authenticationv1alpha1.JWTAuthenticatorPhaseError, + }, + { + name: "invalid when spec.tls.certificateAuthorityDataSource refers to a secret that does not exist", + spec: authenticationv1alpha1.JWTAuthenticatorSpec{ + Issuer: env.CLIUpstreamOIDC.Issuer, + Audience: "foo", + Claims: authenticationv1alpha1.JWTTokenClaims{ + Groups: "", + Username: "", + }, + TLS: &authenticationv1alpha1.TLSSpec{ + CertificateAuthorityDataSource: &authenticationv1alpha1.CertificateAuthorityDataSourceSpec{ + Kind: "Secret", + Name: "does-not-exist", + Key: "does-not-matter", + }, + }, + }, + wantConditions: []metav1.Condition{ + { + Type: "AuthenticatorValid", + Status: "Unknown", + Reason: "UnableToValidate", + Message: "unable to validate; see other conditions for details", + }, { + Type: "DiscoveryURLValid", + Status: "Unknown", + Reason: "UnableToValidate", + Message: "unable to validate; see other conditions for details", + }, { + Type: "IssuerURLValid", + Status: "True", + Reason: "Success", + Message: "issuer is a valid URL", + }, { + Type: "JWKSFetchValid", + Status: "Unknown", + Reason: "UnableToValidate", + Message: "unable to validate; see other conditions for details", + }, { + Type: "JWKSURLValid", + Status: "Unknown", + Reason: "UnableToValidate", + Message: "unable to validate; see other conditions for details", + }, { + Type: "Ready", + Status: "False", + Reason: "NotReady", + Message: "the JWTAuthenticator is not ready: see other conditions for details", + }, + { + Type: "TLSConfigurationValid", + Status: "False", + Reason: "InvalidTLSConfig", + Message: "spec.tls.certificateAuthorityDataSource is invalid: failed to get secret \"concierge/does-not-exist\": secret \"does-not-exist\" not found", + }, + }, + wantPhase: authenticationv1alpha1.JWTAuthenticatorPhaseError, + }, + { + name: "invalid when spec.tls.certificateAuthorityDataSource refers to a configmap that does not have valid PEM bytes", + spec: authenticationv1alpha1.JWTAuthenticatorSpec{ + Issuer: env.CLIUpstreamOIDC.Issuer, + Audience: "foo", + Claims: authenticationv1alpha1.JWTTokenClaims{ + Groups: "", + Username: "", + }, + TLS: &authenticationv1alpha1.TLSSpec{ + CertificateAuthorityDataSource: &authenticationv1alpha1.CertificateAuthorityDataSourceSpec{ + Kind: "ConfigMap", + Name: badCABundleConfigMap.Name, + Key: "ca.crt", + }, + }, + }, + wantConditions: []metav1.Condition{ + { + Type: "AuthenticatorValid", + Status: "Unknown", + Reason: "UnableToValidate", + Message: "unable to validate; see other conditions for details", + }, { + Type: "DiscoveryURLValid", + Status: "Unknown", + Reason: "UnableToValidate", + Message: "unable to validate; see other conditions for details", + }, { + Type: "IssuerURLValid", + Status: "True", + Reason: "Success", + Message: "issuer is a valid URL", + }, { + Type: "JWKSFetchValid", + Status: "Unknown", + Reason: "UnableToValidate", + Message: "unable to validate; see other conditions for details", + }, { + Type: "JWKSURLValid", + Status: "Unknown", + Reason: "UnableToValidate", + Message: "unable to validate; see other conditions for details", + }, { + Type: "Ready", + Status: "False", + Reason: "NotReady", + Message: "the JWTAuthenticator is not ready: see other conditions for details", + }, + { + Type: "TLSConfigurationValid", + Status: "False", + Reason: "InvalidTLSConfig", + Message: fmt.Sprintf("spec.tls.certificateAuthorityDataSource is invalid: key \"ca.crt\" with 28 bytes of data in configmap \"concierge/%s\" is not a PEM-encoded certificate (PEM certificates must begin with \"-----BEGIN CERTIFICATE-----\")", badCABundleConfigMap.Name), + }, + }, + wantPhase: authenticationv1alpha1.JWTAuthenticatorPhaseError, + }, + { + name: "invalid when spec.tls.certificateAuthorityDataSource refers to a key in a configmap that does not exist", + spec: authenticationv1alpha1.JWTAuthenticatorSpec{ + Issuer: env.CLIUpstreamOIDC.Issuer, + Audience: "foo", + Claims: authenticationv1alpha1.JWTTokenClaims{ + Groups: "", + Username: "", + }, + TLS: &authenticationv1alpha1.TLSSpec{ + CertificateAuthorityDataSource: &authenticationv1alpha1.CertificateAuthorityDataSourceSpec{ + Kind: "ConfigMap", + Name: badCABundleConfigMap.Name, + Key: "key-not-present", + }, + }, + }, + wantConditions: []metav1.Condition{ + { + Type: "AuthenticatorValid", + Status: "Unknown", + Reason: "UnableToValidate", + Message: "unable to validate; see other conditions for details", + }, { + Type: "DiscoveryURLValid", + Status: "Unknown", + Reason: "UnableToValidate", + Message: "unable to validate; see other conditions for details", + }, { + Type: "IssuerURLValid", + Status: "True", + Reason: "Success", + Message: "issuer is a valid URL", + }, { + Type: "JWKSFetchValid", + Status: "Unknown", + Reason: "UnableToValidate", + Message: "unable to validate; see other conditions for details", + }, { + Type: "JWKSURLValid", + Status: "Unknown", + Reason: "UnableToValidate", + Message: "unable to validate; see other conditions for details", + }, { + Type: "Ready", + Status: "False", + Reason: "NotReady", + Message: "the JWTAuthenticator is not ready: see other conditions for details", + }, + { + Type: "TLSConfigurationValid", + Status: "False", + Reason: "InvalidTLSConfig", + Message: fmt.Sprintf("spec.tls.certificateAuthorityDataSource is invalid: key \"key-not-present\" not found in configmap \"concierge/%s\"", badCABundleConfigMap.Name), + }, + }, + wantPhase: authenticationv1alpha1.JWTAuthenticatorPhaseError, + }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { diff --git a/test/integration/concierge_webhookauthenticator_status_test.go b/test/integration/concierge_webhookauthenticator_status_test.go index f74549a8d..910bcd57d 100644 --- a/test/integration/concierge_webhookauthenticator_status_test.go +++ b/test/integration/concierge_webhookauthenticator_status_test.go @@ -155,6 +155,10 @@ func TestConciergeWebhookAuthenticatorStatus_Parallel(t *testing.T) { caBundleSomePivotalCA := "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURVVENDQWptZ0F3SUJBZ0lWQUpzNStTbVRtaTJXeUI0bGJJRXBXaUs5a1RkUE1BMEdDU3FHU0liM0RRRUIKQ3dVQU1COHhDekFKQmdOVkJBWVRBbFZUTVJBd0RnWURWUVFLREFkUWFYWnZkR0ZzTUI0WERUSXdNRFV3TkRFMgpNamMxT0ZvWERUSTBNRFV3TlRFMk1qYzFPRm93SHpFTE1Ba0dBMVVFQmhNQ1ZWTXhFREFPQmdOVkJBb01CMUJwCmRtOTBZV3d3Z2dFaU1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQkR3QXdnZ0VLQW9JQkFRRERZWmZvWGR4Z2NXTEMKZEJtbHB5a0tBaG9JMlBuUWtsVFNXMno1cGcwaXJjOGFRL1E3MXZzMTRZYStmdWtFTGlvOTRZYWw4R01DdVFrbApMZ3AvUEE5N1VYelhQNDBpK25iNXcwRGpwWWd2dU9KQXJXMno2MFRnWE5NSFh3VHk4ME1SZEhpUFVWZ0VZd0JpCmtkNThzdEFVS1Y1MnBQTU1reTJjNy9BcFhJNmRXR2xjalUvaFBsNmtpRzZ5dEw2REtGYjJQRWV3MmdJM3pHZ2IKOFVVbnA1V05DZDd2WjNVY0ZHNXlsZEd3aGc3cnZ4U1ZLWi9WOEhCMGJmbjlxamlrSVcxWFM4dzdpUUNlQmdQMApYZWhKZmVITlZJaTJtZlczNlVQbWpMdnVKaGpqNDIrdFBQWndvdDkzdWtlcEgvbWpHcFJEVm9wamJyWGlpTUYrCkYxdnlPNGMxQWdNQkFBR2pnWU13Z1lBd0hRWURWUjBPQkJZRUZNTWJpSXFhdVkwajRVWWphWDl0bDJzby9LQ1IKTUI4R0ExVWRJd1FZTUJhQUZNTWJpSXFhdVkwajRVWWphWDl0bDJzby9LQ1JNQjBHQTFVZEpRUVdNQlFHQ0NzRwpBUVVGQndNQ0JnZ3JCZ0VGQlFjREFUQVBCZ05WSFJNQkFmOEVCVEFEQVFIL01BNEdBMVVkRHdFQi93UUVBd0lCCkJqQU5CZ2txaGtpRzl3MEJBUXNGQUFPQ0FRRUFYbEh4M2tIMDZwY2NDTDlEVE5qTnBCYnlVSytGd2R6T2IwWFYKcmpNaGtxdHVmdEpUUnR5T3hKZ0ZKNXhUR3pCdEtKamcrVU1pczBOV0t0VDBNWThVMU45U2c5SDl0RFpHRHBjVQpxMlVRU0Y4dXRQMVR3dnJIUzIrdzB2MUoxdHgrTEFiU0lmWmJCV0xXQ21EODUzRlVoWlFZekkvYXpFM28vd0p1CmlPUklMdUpNUk5vNlBXY3VLZmRFVkhaS1RTWnk3a25FcHNidGtsN3EwRE91eUFWdG9HVnlkb3VUR0FOdFhXK2YKczNUSTJjKzErZXg3L2RZOEJGQTFzNWFUOG5vZnU3T1RTTzdiS1kzSkRBUHZOeFQzKzVZUXJwNGR1Nmh0YUFMbAppOHNaRkhidmxpd2EzdlhxL3p1Y2JEaHEzQzBhZnAzV2ZwRGxwSlpvLy9QUUFKaTZLQT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K" + badCABundleConfigMap := testlib.CreateTestConfigMap(t, env.ConciergeNamespace, "ca-bundle", map[string]string{ + "ca.crt": "This is not a real CA bundle", + }) + tests := []struct { name string spec func() *authenticationv1alpha1.WebhookAuthenticatorSpec @@ -273,6 +277,222 @@ func TestConciergeWebhookAuthenticatorStatus_Parallel(t *testing.T) { }, ), }, + { + name: "invalid when spec.tls supplies both certificateAuthorityData and certificateAuthorityDataSource", + spec: func() *authenticationv1alpha1.WebhookAuthenticatorSpec { + webhookSpec := env.TestWebhook.DeepCopy() + webhookSpec.TLS = &authenticationv1alpha1.TLSSpec{ + CertificateAuthorityData: caBundleSomePivotalCA, + CertificateAuthorityDataSource: &authenticationv1alpha1.CertificateAuthorityDataSourceSpec{ + Kind: "ConfigMap", + Name: "does-not-matter", + Key: "also-does-not-matter", + }, + } + webhookSpec.Endpoint = "https://127.0.0.1:443/some-fake-endpoint" + return webhookSpec + }, + initialPhase: authenticationv1alpha1.WebhookAuthenticatorPhaseError, + finalConditions: replaceSomeConditions( + allSuccessfulWebhookAuthenticatorConditions(), + []metav1.Condition{ + { + Type: "Ready", + Status: "False", + Reason: "NotReady", + Message: "the WebhookAuthenticator is not ready: see other conditions for details", + }, { + Type: "AuthenticatorValid", + Status: "Unknown", + Reason: "UnableToValidate", + Message: "unable to validate; see other conditions for details", + }, { + Type: "WebhookConnectionValid", + Status: "Unknown", + Reason: "UnableToValidate", + Message: "unable to validate; see other conditions for details", + }, + { + Type: "TLSConfigurationValid", + Status: "False", + Reason: "InvalidTLSConfig", + Message: "spec.tls is invalid: both tls.certificateAuthorityDataSource and tls.certificateAuthorityData provided", + }, + }, + ), + }, + { + name: "invalid when spec.tls.certificateAuthorityDataSource refers to a configmap that does not exist", + spec: func() *authenticationv1alpha1.WebhookAuthenticatorSpec { + webhookSpec := env.TestWebhook.DeepCopy() + webhookSpec.TLS = &authenticationv1alpha1.TLSSpec{ + CertificateAuthorityDataSource: &authenticationv1alpha1.CertificateAuthorityDataSourceSpec{ + Kind: "ConfigMap", + Name: "does-not-exist", + Key: "does-not-matter", + }, + } + webhookSpec.Endpoint = "https://127.0.0.1:443/some-fake-endpoint" + return webhookSpec + }, + initialPhase: authenticationv1alpha1.WebhookAuthenticatorPhaseError, + finalConditions: replaceSomeConditions( + allSuccessfulWebhookAuthenticatorConditions(), + []metav1.Condition{ + { + Type: "Ready", + Status: "False", + Reason: "NotReady", + Message: "the WebhookAuthenticator is not ready: see other conditions for details", + }, { + Type: "AuthenticatorValid", + Status: "Unknown", + Reason: "UnableToValidate", + Message: "unable to validate; see other conditions for details", + }, { + Type: "WebhookConnectionValid", + Status: "Unknown", + Reason: "UnableToValidate", + Message: "unable to validate; see other conditions for details", + }, + { + Type: "TLSConfigurationValid", + Status: "False", + Reason: "InvalidTLSConfig", + Message: "spec.tls.certificateAuthorityDataSource is invalid: failed to get configmap \"concierge/does-not-exist\": configmap \"does-not-exist\" not found", + }, + }, + ), + }, + { + name: "invalid when spec.tls.certificateAuthorityDataSource refers to a secret that does not exist", + spec: func() *authenticationv1alpha1.WebhookAuthenticatorSpec { + webhookSpec := env.TestWebhook.DeepCopy() + webhookSpec.TLS = &authenticationv1alpha1.TLSSpec{ + CertificateAuthorityDataSource: &authenticationv1alpha1.CertificateAuthorityDataSourceSpec{ + Kind: "Secret", + Name: "does-not-exist", + Key: "does-not-matter", + }, + } + webhookSpec.Endpoint = "https://127.0.0.1:443/some-fake-endpoint" + return webhookSpec + }, + initialPhase: authenticationv1alpha1.WebhookAuthenticatorPhaseError, + finalConditions: replaceSomeConditions( + allSuccessfulWebhookAuthenticatorConditions(), + []metav1.Condition{ + { + Type: "Ready", + Status: "False", + Reason: "NotReady", + Message: "the WebhookAuthenticator is not ready: see other conditions for details", + }, { + Type: "AuthenticatorValid", + Status: "Unknown", + Reason: "UnableToValidate", + Message: "unable to validate; see other conditions for details", + }, { + Type: "WebhookConnectionValid", + Status: "Unknown", + Reason: "UnableToValidate", + Message: "unable to validate; see other conditions for details", + }, + { + Type: "TLSConfigurationValid", + Status: "False", + Reason: "InvalidTLSConfig", + Message: "spec.tls.certificateAuthorityDataSource is invalid: failed to get secret \"concierge/does-not-exist\": secret \"does-not-exist\" not found", + }, + }, + ), + }, + { + name: "invalid when spec.tls.certificateAuthorityDataSource refers to a configmap that does not have valid PEM bytes", + spec: func() *authenticationv1alpha1.WebhookAuthenticatorSpec { + webhookSpec := env.TestWebhook.DeepCopy() + webhookSpec.TLS = &authenticationv1alpha1.TLSSpec{ + CertificateAuthorityDataSource: &authenticationv1alpha1.CertificateAuthorityDataSourceSpec{ + Kind: "ConfigMap", + Name: badCABundleConfigMap.Name, + Key: "ca.crt", + }, + } + webhookSpec.Endpoint = "https://127.0.0.1:443/some-fake-endpoint" + return webhookSpec + }, + initialPhase: authenticationv1alpha1.WebhookAuthenticatorPhaseError, + finalConditions: replaceSomeConditions( + allSuccessfulWebhookAuthenticatorConditions(), + []metav1.Condition{ + { + Type: "Ready", + Status: "False", + Reason: "NotReady", + Message: "the WebhookAuthenticator is not ready: see other conditions for details", + }, { + Type: "AuthenticatorValid", + Status: "Unknown", + Reason: "UnableToValidate", + Message: "unable to validate; see other conditions for details", + }, { + Type: "WebhookConnectionValid", + Status: "Unknown", + Reason: "UnableToValidate", + Message: "unable to validate; see other conditions for details", + }, + { + Type: "TLSConfigurationValid", + Status: "False", + Reason: "InvalidTLSConfig", + Message: fmt.Sprintf("spec.tls.certificateAuthorityDataSource is invalid: key \"ca.crt\" with 28 bytes of data in configmap \"concierge/%s\" is not a PEM-encoded certificate (PEM certificates must begin with \"-----BEGIN CERTIFICATE-----\")", badCABundleConfigMap.Name), + }, + }, + ), + }, + { + name: "invalid when spec.tls.certificateAuthorityDataSource refers to a key in a configmap that does not exist", + spec: func() *authenticationv1alpha1.WebhookAuthenticatorSpec { + webhookSpec := env.TestWebhook.DeepCopy() + webhookSpec.TLS = &authenticationv1alpha1.TLSSpec{ + CertificateAuthorityDataSource: &authenticationv1alpha1.CertificateAuthorityDataSourceSpec{ + Kind: "ConfigMap", + Name: badCABundleConfigMap.Name, + Key: "key-not-present", + }, + } + webhookSpec.Endpoint = "https://127.0.0.1:443/some-fake-endpoint" + return webhookSpec + }, + initialPhase: authenticationv1alpha1.WebhookAuthenticatorPhaseError, + finalConditions: replaceSomeConditions( + allSuccessfulWebhookAuthenticatorConditions(), + []metav1.Condition{ + { + Type: "Ready", + Status: "False", + Reason: "NotReady", + Message: "the WebhookAuthenticator is not ready: see other conditions for details", + }, { + Type: "AuthenticatorValid", + Status: "Unknown", + Reason: "UnableToValidate", + Message: "unable to validate; see other conditions for details", + }, { + Type: "WebhookConnectionValid", + Status: "Unknown", + Reason: "UnableToValidate", + Message: "unable to validate; see other conditions for details", + }, + { + Type: "TLSConfigurationValid", + Status: "False", + Reason: "InvalidTLSConfig", + Message: fmt.Sprintf("spec.tls.certificateAuthorityDataSource is invalid: key \"key-not-present\" not found in configmap \"concierge/%s\"", badCABundleConfigMap.Name), + }, + }, + ), + }, } for _, test := range tests { tt := test From 59402bca7b0253c9d1e68e5ba96d2cfa2354397a Mon Sep 17 00:00:00 2001 From: Ashish Amarnath Date: Sat, 3 Aug 2024 01:06:31 -0700 Subject: [PATCH 86/99] add integration test for TLS config validation in OIDCIdentityProvider Signed-off-by: Ashish Amarnath --- test/integration/supervisor_upstream_test.go | 247 +++++++++++++++++++ 1 file changed, 247 insertions(+) diff --git a/test/integration/supervisor_upstream_test.go b/test/integration/supervisor_upstream_test.go index c20da7301..47f18a127 100644 --- a/test/integration/supervisor_upstream_test.go +++ b/test/integration/supervisor_upstream_test.go @@ -5,6 +5,7 @@ package integration import ( "encoding/base64" + "fmt" "testing" "github.com/stretchr/testify/require" @@ -126,6 +127,252 @@ oidc: issuer did not match the issuer returned by provider, expected "` + env.Su expectedTLSConfigValidCondition(env.SupervisorUpstreamOIDC.CABundle != ""), }) }) + + t.Run("invalid when tlsSpec supplies both certificateAuthorityData and certificateAuthorityDataSource", func(t *testing.T) { + t.Parallel() + spec := idpv1alpha1.OIDCIdentityProviderSpec{ + Issuer: env.SupervisorUpstreamOIDC.Issuer, + TLS: &idpv1alpha1.TLSSpec{ + CertificateAuthorityData: base64.StdEncoding.EncodeToString([]byte(env.SupervisorUpstreamOIDC.CABundle)), + CertificateAuthorityDataSource: &idpv1alpha1.CertificateAuthorityDataSourceSpec{ + Kind: "ConfigMap", + Name: "does=not-matter", + Key: "also-does-not-matter", + }, + }, + AuthorizationConfig: idpv1alpha1.OIDCAuthorizationConfig{ + AdditionalScopes: []string{"email", "profile"}, + }, + Client: idpv1alpha1.OIDCClient{ + SecretName: testlib.CreateOIDCClientCredentialsSecret(t, "test-client-id", "test-client-secret").Name, + }, + } + upstream := testlib.CreateTestOIDCIdentityProvider(t, spec, idpv1alpha1.PhaseError) + expectUpstreamConditions(t, upstream, []metav1.Condition{ + { + Type: "ClientCredentialsSecretValid", + Status: "True", + Reason: "Success", + Message: "loaded client credentials", + }, + { + Type: "OIDCDiscoverySucceeded", + Status: "False", + Reason: "InvalidTLSConfig", + Message: "spec.tls is invalid: both tls.certificateAuthorityDataSource and tls.certificateAuthorityData provided", + }, + { + Type: "AdditionalAuthorizeParametersValid", + Status: "True", + Reason: "Success", + Message: "additionalAuthorizeParameters parameter names are allowed", + }, + { + Type: "TLSConfigurationValid", + Status: "False", + Reason: "InvalidTLSConfig", + Message: "spec.tls is invalid: both tls.certificateAuthorityDataSource and tls.certificateAuthorityData provided", + }, + }) + }) + + t.Run("invalid when spec.tls.certificateAuthorityDataSource refers to a configmap that does not exist", func(t *testing.T) { + t.Parallel() + spec := idpv1alpha1.OIDCIdentityProviderSpec{ + Issuer: env.SupervisorUpstreamOIDC.Issuer, + TLS: &idpv1alpha1.TLSSpec{ + CertificateAuthorityDataSource: &idpv1alpha1.CertificateAuthorityDataSourceSpec{ + Kind: "ConfigMap", + Name: "does=not-exist", + Key: "does-not-matter", + }, + }, + AuthorizationConfig: idpv1alpha1.OIDCAuthorizationConfig{ + AdditionalScopes: []string{"email", "profile"}, + }, + Client: idpv1alpha1.OIDCClient{ + SecretName: testlib.CreateOIDCClientCredentialsSecret(t, "test-client-id", "test-client-secret").Name, + }, + } + upstream := testlib.CreateTestOIDCIdentityProvider(t, spec, idpv1alpha1.PhaseError) + expectUpstreamConditions(t, upstream, []metav1.Condition{ + { + Type: "ClientCredentialsSecretValid", + Status: "True", + Reason: "Success", + Message: "loaded client credentials", + }, + { + Type: "OIDCDiscoverySucceeded", + Status: "False", + Reason: "InvalidTLSConfig", + Message: "spec.tls.certificateAuthorityDataSource is invalid: failed to get configmap \"supervisor/does=not-exist\": configmap \"does=not-exist\" not found", + }, + { + Type: "AdditionalAuthorizeParametersValid", + Status: "True", + Reason: "Success", + Message: "additionalAuthorizeParameters parameter names are allowed", + }, + { + Type: "TLSConfigurationValid", + Status: "False", + Reason: "InvalidTLSConfig", + Message: "spec.tls.certificateAuthorityDataSource is invalid: failed to get configmap \"supervisor/does=not-exist\": configmap \"does=not-exist\" not found", + }, + }) + }) + + t.Run("invalid when spec.tls.certificateAuthorityDataSource refers to a secret that does not exist", func(t *testing.T) { + t.Parallel() + spec := idpv1alpha1.OIDCIdentityProviderSpec{ + Issuer: env.SupervisorUpstreamOIDC.Issuer, + TLS: &idpv1alpha1.TLSSpec{ + CertificateAuthorityDataSource: &idpv1alpha1.CertificateAuthorityDataSourceSpec{ + Kind: "Secret", + Name: "does=not-exist", + Key: "does-not-matter", + }, + }, + AuthorizationConfig: idpv1alpha1.OIDCAuthorizationConfig{ + AdditionalScopes: []string{"email", "profile"}, + }, + Client: idpv1alpha1.OIDCClient{ + SecretName: testlib.CreateOIDCClientCredentialsSecret(t, "test-client-id", "test-client-secret").Name, + }, + } + upstream := testlib.CreateTestOIDCIdentityProvider(t, spec, idpv1alpha1.PhaseError) + expectUpstreamConditions(t, upstream, []metav1.Condition{ + { + Type: "ClientCredentialsSecretValid", + Status: "True", + Reason: "Success", + Message: "loaded client credentials", + }, + { + Type: "OIDCDiscoverySucceeded", + Status: "False", + Reason: "InvalidTLSConfig", + Message: "spec.tls.certificateAuthorityDataSource is invalid: failed to get secret \"supervisor/does=not-exist\": secret \"does=not-exist\" not found", + }, + { + Type: "AdditionalAuthorizeParametersValid", + Status: "True", + Reason: "Success", + Message: "additionalAuthorizeParameters parameter names are allowed", + }, + { + Type: "TLSConfigurationValid", + Status: "False", + Reason: "InvalidTLSConfig", + Message: "spec.tls.certificateAuthorityDataSource is invalid: failed to get secret \"supervisor/does=not-exist\": secret \"does=not-exist\" not found", + }, + }) + }) + + t.Run("invalid when spec.tls.certificateAuthorityDataSource refers to a configmap that does not have valid PEM bytes", func(t *testing.T) { + t.Parallel() + + badCABundleConfigMap := testlib.CreateTestConfigMap(t, env.SupervisorNamespace, "ca-bundle", map[string]string{ + "ca.crt": "This is not a real CA bundle", + }) + + spec := idpv1alpha1.OIDCIdentityProviderSpec{ + Issuer: env.SupervisorUpstreamOIDC.Issuer, + TLS: &idpv1alpha1.TLSSpec{ + CertificateAuthorityDataSource: &idpv1alpha1.CertificateAuthorityDataSourceSpec{ + Kind: "ConfigMap", + Name: badCABundleConfigMap.Name, + Key: "ca.crt", + }, + }, + AuthorizationConfig: idpv1alpha1.OIDCAuthorizationConfig{ + AdditionalScopes: []string{"email", "profile"}, + }, + Client: idpv1alpha1.OIDCClient{ + SecretName: testlib.CreateOIDCClientCredentialsSecret(t, "test-client-id", "test-client-secret").Name, + }, + } + upstream := testlib.CreateTestOIDCIdentityProvider(t, spec, idpv1alpha1.PhaseError) + expectUpstreamConditions(t, upstream, []metav1.Condition{ + { + Type: "ClientCredentialsSecretValid", + Status: "True", + Reason: "Success", + Message: "loaded client credentials", + }, + { + Type: "OIDCDiscoverySucceeded", + Status: "False", + Reason: "InvalidTLSConfig", + Message: fmt.Sprintf("spec.tls.certificateAuthorityDataSource is invalid: failed to get configmap \"supervisor/%s\": configmap \"%s\" not found", badCABundleConfigMap.Name, badCABundleConfigMap.Name), + }, + { + Type: "AdditionalAuthorizeParametersValid", + Status: "True", + Reason: "Success", + Message: "additionalAuthorizeParameters parameter names are allowed", + }, + { + Type: "TLSConfigurationValid", + Status: "False", + Reason: "InvalidTLSConfig", + Message: fmt.Sprintf("spec.tls.certificateAuthorityDataSource is invalid: failed to get configmap \"supervisor/%s\": configmap \"%s\" not found", badCABundleConfigMap.Name, badCABundleConfigMap.Name), + }, + }) + }) + + t.Run("invalid when spec.tls.certificateAuthorityDataSource refers to a key in a configmap that does not exist", func(t *testing.T) { + t.Parallel() + + badCABundleConfigMap := testlib.CreateTestConfigMap(t, env.SupervisorNamespace, "ca-bundle", map[string]string{ + "ca.crt": "This is not a real CA bundle", + }) + + spec := idpv1alpha1.OIDCIdentityProviderSpec{ + Issuer: env.SupervisorUpstreamOIDC.Issuer, + TLS: &idpv1alpha1.TLSSpec{ + CertificateAuthorityDataSource: &idpv1alpha1.CertificateAuthorityDataSourceSpec{ + Kind: "ConfigMap", + Name: badCABundleConfigMap.Name, + Key: "key-not-present", + }, + }, + AuthorizationConfig: idpv1alpha1.OIDCAuthorizationConfig{ + AdditionalScopes: []string{"email", "profile"}, + }, + Client: idpv1alpha1.OIDCClient{ + SecretName: testlib.CreateOIDCClientCredentialsSecret(t, "test-client-id", "test-client-secret").Name, + }, + } + upstream := testlib.CreateTestOIDCIdentityProvider(t, spec, idpv1alpha1.PhaseError) + expectUpstreamConditions(t, upstream, []metav1.Condition{ + { + Type: "ClientCredentialsSecretValid", + Status: "True", + Reason: "Success", + Message: "loaded client credentials", + }, + { + Type: "OIDCDiscoverySucceeded", + Status: "False", + Reason: "InvalidTLSConfig", + Message: fmt.Sprintf("spec.tls.certificateAuthorityDataSource is invalid: key \"key-not-present\" not found in configmap \"supervisor/%s\"", badCABundleConfigMap.Name), + }, + { + Type: "AdditionalAuthorizeParametersValid", + Status: "True", + Reason: "Success", + Message: "additionalAuthorizeParameters parameter names are allowed", + }, + { + Type: "TLSConfigurationValid", + Status: "False", + Reason: "InvalidTLSConfig", + Message: fmt.Sprintf("spec.tls.certificateAuthorityDataSource is invalid: key \"key-not-present\" not found in configmap \"supervisor/%s\"", badCABundleConfigMap.Name), + }, + }) + }) } func expectUpstreamConditions(t *testing.T, upstream *idpv1alpha1.OIDCIdentityProvider, expected []metav1.Condition) { From 23129da3e239ea1a47ca55df1bb0c8f9d99639a8 Mon Sep 17 00:00:00 2001 From: Ashish Amarnath Date: Sat, 3 Aug 2024 01:08:21 -0700 Subject: [PATCH 87/99] add integration test for TLS config validation in GitHubIdentityProvider Signed-off-by: Ashish Amarnath --- .../integration/supervisor_github_idp_test.go | 386 +++++++++++++++++- 1 file changed, 384 insertions(+), 2 deletions(-) diff --git a/test/integration/supervisor_github_idp_test.go b/test/integration/supervisor_github_idp_test.go index 3a135dbc1..ed0781668 100644 --- a/test/integration/supervisor_github_idp_test.go +++ b/test/integration/supervisor_github_idp_test.go @@ -328,10 +328,15 @@ func TestGitHubIDPSetsDefaultsWithKubectl_Parallel(t *testing.T) { func TestGitHubIDPPhaseAndConditions_Parallel(t *testing.T) { // These operations must be performed in the Supervisor's namespace so that the controller can find GitHubIdentityProvider - supervisorNamespace := testlib.IntegrationEnv(t).SupervisorNamespace + env := testlib.IntegrationEnv(t) + supervisorNamespace := env.SupervisorNamespace ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute) t.Cleanup(cancel) + badCABundleConfigMap := testlib.CreateTestConfigMap(t, supervisorNamespace, "ca-bundle", map[string]string{ + "ca.crt": "This is not a real CA bundle", + }) + kubernetesClient := testlib.NewKubernetesClientset(t) secretsClient := kubernetesClient.CoreV1().Secrets(supervisorNamespace) gitHubIDPClient := testlib.NewSupervisorClientset(t).IDPV1alpha1().GitHubIdentityProviders(supervisorNamespace) @@ -483,6 +488,382 @@ func TestGitHubIDPPhaseAndConditions_Parallel(t *testing.T) { }, }, }, + { + name: "invalid when spec.githubAPI.tls supplies both certificateAuthorityData and certificateAuthorityDataSource", + secrets: []*corev1.Secret{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: happySecretName, + }, + Type: "secrets.pinniped.dev/github-client", + Data: map[string][]byte{ + "clientID": []byte("foo"), + "clientSecret": []byte("bar"), + }, + }, + }, + idps: []*idpv1alpha1.GitHubIdentityProvider{ + { + Spec: idpv1alpha1.GitHubIdentityProviderSpec{ + GitHubAPI: idpv1alpha1.GitHubAPIConfig{ + TLS: &idpv1alpha1.TLSSpec{ + CertificateAuthorityData: "this is not a CA bundle", + CertificateAuthorityDataSource: &idpv1alpha1.CertificateAuthorityDataSourceSpec{ + Kind: "ConfigMap", + Name: "does-not-matter", + Key: "also-does-not-matter", + }, + }, + Host: ptr.To("github.com"), + }, + AllowAuthentication: idpv1alpha1.GitHubAllowAuthenticationSpec{ + Organizations: idpv1alpha1.GitHubOrganizationsSpec{ + Policy: ptr.To(idpv1alpha1.GitHubAllowedAuthOrganizationsPolicyAllGitHubUsers), + }, + }, + }, + }, + }, + wantPhase: idpv1alpha1.GitHubPhaseError, + wantConditions: []*metav1.Condition{ + { + Type: "ClaimsValid", + Status: metav1.ConditionTrue, + Reason: "Success", + Message: "spec.claims are valid", + }, + { + Type: "ClientCredentialsSecretValid", + Status: metav1.ConditionTrue, + Reason: "Success", + Message: fmt.Sprintf("clientID and clientSecret have been read from spec.client.SecretName (%q)", happySecretName), + }, + { + Type: "GitHubConnectionValid", + Status: "Unknown", + Reason: "UnableToValidate", + Message: "unable to validate; see other conditions for details", + }, + { + Type: "HostValid", + Status: metav1.ConditionTrue, + Reason: "Success", + Message: `spec.githubAPI.host ("github.com") is valid`, + }, + { + Type: "OrganizationsPolicyValid", + Status: metav1.ConditionTrue, + Reason: "Success", + Message: `spec.allowAuthentication.organizations.policy ("AllGitHubUsers") is valid`, + }, + { + Type: "TLSConfigurationValid", + Status: metav1.ConditionFalse, + Reason: "InvalidTLSConfig", + Message: "spec.githubAPI.tls is invalid: both tls.certificateAuthorityDataSource and tls.certificateAuthorityData provided", + }, + }, + }, + { + name: "invalid when spec.githubAPI.tls.certificateAuthorityDataSource refers to a configmap that does not exist", + secrets: []*corev1.Secret{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: happySecretName, + }, + Type: "secrets.pinniped.dev/github-client", + Data: map[string][]byte{ + "clientID": []byte("foo"), + "clientSecret": []byte("bar"), + }, + }, + }, + idps: []*idpv1alpha1.GitHubIdentityProvider{ + { + Spec: idpv1alpha1.GitHubIdentityProviderSpec{ + GitHubAPI: idpv1alpha1.GitHubAPIConfig{ + TLS: &idpv1alpha1.TLSSpec{ + CertificateAuthorityDataSource: &idpv1alpha1.CertificateAuthorityDataSourceSpec{ + Kind: "ConfigMap", + Name: "does-not-exist", + Key: "does-not-matter", + }, + }, + Host: ptr.To("github.com"), + }, + AllowAuthentication: idpv1alpha1.GitHubAllowAuthenticationSpec{ + Organizations: idpv1alpha1.GitHubOrganizationsSpec{ + Policy: ptr.To(idpv1alpha1.GitHubAllowedAuthOrganizationsPolicyAllGitHubUsers), + }, + }, + }, + }, + }, + wantPhase: idpv1alpha1.GitHubPhaseError, + wantConditions: []*metav1.Condition{ + { + Type: "ClaimsValid", + Status: metav1.ConditionTrue, + Reason: "Success", + Message: "spec.claims are valid", + }, + { + Type: "ClientCredentialsSecretValid", + Status: metav1.ConditionTrue, + Reason: "Success", + Message: fmt.Sprintf("clientID and clientSecret have been read from spec.client.SecretName (%q)", happySecretName), + }, + { + Type: "GitHubConnectionValid", + Status: "Unknown", + Reason: "UnableToValidate", + Message: "unable to validate; see other conditions for details", + }, + { + Type: "HostValid", + Status: metav1.ConditionTrue, + Reason: "Success", + Message: `spec.githubAPI.host ("github.com") is valid`, + }, + { + Type: "OrganizationsPolicyValid", + Status: metav1.ConditionTrue, + Reason: "Success", + Message: `spec.allowAuthentication.organizations.policy ("AllGitHubUsers") is valid`, + }, + { + Type: "TLSConfigurationValid", + Status: metav1.ConditionFalse, + Reason: "InvalidTLSConfig", + Message: "spec.githubAPI.tls.certificateAuthorityDataSource is invalid: failed to get configmap \"supervisor/does-not-exist\": configmap \"does-not-exist\" not found", + }, + }, + }, + { + name: "invalid when spec.githubAPI.tls.certificateAuthorityDataSource refers to a secret that does not exist", + secrets: []*corev1.Secret{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: happySecretName, + }, + Type: "secrets.pinniped.dev/github-client", + Data: map[string][]byte{ + "clientID": []byte("foo"), + "clientSecret": []byte("bar"), + }, + }, + }, + idps: []*idpv1alpha1.GitHubIdentityProvider{ + { + Spec: idpv1alpha1.GitHubIdentityProviderSpec{ + GitHubAPI: idpv1alpha1.GitHubAPIConfig{ + TLS: &idpv1alpha1.TLSSpec{ + CertificateAuthorityDataSource: &idpv1alpha1.CertificateAuthorityDataSourceSpec{ + Kind: "Secret", + Name: "does-not-exist", + Key: "does-not-matter", + }, + }, + Host: ptr.To("github.com"), + }, + AllowAuthentication: idpv1alpha1.GitHubAllowAuthenticationSpec{ + Organizations: idpv1alpha1.GitHubOrganizationsSpec{ + Policy: ptr.To(idpv1alpha1.GitHubAllowedAuthOrganizationsPolicyAllGitHubUsers), + }, + }, + }, + }, + }, + wantPhase: idpv1alpha1.GitHubPhaseError, + wantConditions: []*metav1.Condition{ + { + Type: "ClaimsValid", + Status: metav1.ConditionTrue, + Reason: "Success", + Message: "spec.claims are valid", + }, + { + Type: "ClientCredentialsSecretValid", + Status: metav1.ConditionTrue, + Reason: "Success", + Message: fmt.Sprintf("clientID and clientSecret have been read from spec.client.SecretName (%q)", happySecretName), + }, + { + Type: "GitHubConnectionValid", + Status: "Unknown", + Reason: "UnableToValidate", + Message: "unable to validate; see other conditions for details", + }, + { + Type: "HostValid", + Status: metav1.ConditionTrue, + Reason: "Success", + Message: `spec.githubAPI.host ("github.com") is valid`, + }, + { + Type: "OrganizationsPolicyValid", + Status: metav1.ConditionTrue, + Reason: "Success", + Message: `spec.allowAuthentication.organizations.policy ("AllGitHubUsers") is valid`, + }, + { + Type: "TLSConfigurationValid", + Status: metav1.ConditionFalse, + Reason: "InvalidTLSConfig", + Message: "spec.githubAPI.tls.certificateAuthorityDataSource is invalid: failed to get secret \"supervisor/does-not-exist\": secret \"does-not-exist\" not found", + }, + }, + }, + { + name: "invalid when spec.githubAPI.tls.certificateAuthorityDataSource refers to a configmap that does not have valid PEM bytes", + secrets: []*corev1.Secret{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: happySecretName, + }, + Type: "secrets.pinniped.dev/github-client", + Data: map[string][]byte{ + "clientID": []byte("foo"), + "clientSecret": []byte("bar"), + }, + }, + }, + idps: []*idpv1alpha1.GitHubIdentityProvider{ + { + Spec: idpv1alpha1.GitHubIdentityProviderSpec{ + GitHubAPI: idpv1alpha1.GitHubAPIConfig{ + TLS: &idpv1alpha1.TLSSpec{ + CertificateAuthorityDataSource: &idpv1alpha1.CertificateAuthorityDataSourceSpec{ + Kind: "ConfigMap", + Name: badCABundleConfigMap.Name, + Key: "ca.crt", + }, + }, + Host: ptr.To("github.com"), + }, + AllowAuthentication: idpv1alpha1.GitHubAllowAuthenticationSpec{ + Organizations: idpv1alpha1.GitHubOrganizationsSpec{ + Policy: ptr.To(idpv1alpha1.GitHubAllowedAuthOrganizationsPolicyAllGitHubUsers), + }, + }, + }, + }, + }, + wantPhase: idpv1alpha1.GitHubPhaseError, + wantConditions: []*metav1.Condition{ + { + Type: "ClaimsValid", + Status: metav1.ConditionTrue, + Reason: "Success", + Message: "spec.claims are valid", + }, + { + Type: "ClientCredentialsSecretValid", + Status: metav1.ConditionTrue, + Reason: "Success", + Message: fmt.Sprintf("clientID and clientSecret have been read from spec.client.SecretName (%q)", happySecretName), + }, + { + Type: "GitHubConnectionValid", + Status: "Unknown", + Reason: "UnableToValidate", + Message: "unable to validate; see other conditions for details", + }, + { + Type: "HostValid", + Status: metav1.ConditionTrue, + Reason: "Success", + Message: `spec.githubAPI.host ("github.com") is valid`, + }, + { + Type: "OrganizationsPolicyValid", + Status: metav1.ConditionTrue, + Reason: "Success", + Message: `spec.allowAuthentication.organizations.policy ("AllGitHubUsers") is valid`, + }, + { + Type: "TLSConfigurationValid", + Status: metav1.ConditionFalse, + Reason: "InvalidTLSConfig", + Message: fmt.Sprintf("spec.githubAPI.tls.certificateAuthorityDataSource is invalid: key \"ca.crt\" with 28 bytes of data in configmap \"supervisor/%s\" is not a PEM-encoded certificate (PEM certificates must begin with \"-----BEGIN CERTIFICATE-----\")", badCABundleConfigMap.Name), + }, + }, + }, + { + name: "invalid when spec.githubAPI.tls.certificateAuthorityDataSource refers to a key in a configmap that does not exist", + secrets: []*corev1.Secret{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: happySecretName, + }, + Type: "secrets.pinniped.dev/github-client", + Data: map[string][]byte{ + "clientID": []byte("foo"), + "clientSecret": []byte("bar"), + }, + }, + }, + idps: []*idpv1alpha1.GitHubIdentityProvider{ + { + Spec: idpv1alpha1.GitHubIdentityProviderSpec{ + GitHubAPI: idpv1alpha1.GitHubAPIConfig{ + TLS: &idpv1alpha1.TLSSpec{ + CertificateAuthorityDataSource: &idpv1alpha1.CertificateAuthorityDataSourceSpec{ + Kind: "ConfigMap", + Name: badCABundleConfigMap.Name, + Key: "key-not-present", + }, + }, + Host: ptr.To("github.com"), + }, + AllowAuthentication: idpv1alpha1.GitHubAllowAuthenticationSpec{ + Organizations: idpv1alpha1.GitHubOrganizationsSpec{ + Policy: ptr.To(idpv1alpha1.GitHubAllowedAuthOrganizationsPolicyAllGitHubUsers), + }, + }, + }, + }, + }, + wantPhase: idpv1alpha1.GitHubPhaseError, + wantConditions: []*metav1.Condition{ + { + Type: "ClaimsValid", + Status: metav1.ConditionTrue, + Reason: "Success", + Message: "spec.claims are valid", + }, + { + Type: "ClientCredentialsSecretValid", + Status: metav1.ConditionTrue, + Reason: "Success", + Message: fmt.Sprintf("clientID and clientSecret have been read from spec.client.SecretName (%q)", happySecretName), + }, + { + Type: "GitHubConnectionValid", + Status: "Unknown", + Reason: "UnableToValidate", + Message: "unable to validate; see other conditions for details", + }, + { + Type: "HostValid", + Status: metav1.ConditionTrue, + Reason: "Success", + Message: `spec.githubAPI.host ("github.com") is valid`, + }, + { + Type: "OrganizationsPolicyValid", + Status: metav1.ConditionTrue, + Reason: "Success", + Message: `spec.allowAuthentication.organizations.policy ("AllGitHubUsers") is valid`, + }, + { + Type: "TLSConfigurationValid", + Status: metav1.ConditionFalse, + Reason: "InvalidTLSConfig", + Message: fmt.Sprintf("spec.githubAPI.tls.certificateAuthorityDataSource is invalid: key \"key-not-present\" not found in configmap \"supervisor/%s\"", badCABundleConfigMap.Name), + }, + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -582,7 +963,8 @@ func TestGitHubIDPInWrongNamespace_Parallel(t *testing.T) { func TestGitHubIDPSecretInOtherNamespace_Parallel(t *testing.T) { // The GitHubIdentityProvider must be in the same namespace as the controller - supervisorNamespace := testlib.IntegrationEnv(t).SupervisorNamespace + env := testlib.IntegrationEnv(t) + supervisorNamespace := env.SupervisorNamespace ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute) t.Cleanup(cancel) From a40c88ebf3ec704981eaa18048ddaba22433c532 Mon Sep 17 00:00:00 2001 From: Ryan Richard Date: Sat, 3 Aug 2024 14:01:44 -0700 Subject: [PATCH 88/99] document allowed enum values and default values in all CR spec fields --- apis/concierge/authentication/v1alpha1/types_tls.go.tmpl | 1 + .../concierge/config/v1alpha1/types_credentialissuer.go.tmpl | 4 +++- .../config/v1alpha1/types_federationdomain.go.tmpl | 4 +++- .../idp/v1alpha1/types_githubidentityprovider.go.tmpl | 5 ++++- apis/supervisor/idp/v1alpha1/types_tls.go.tmpl | 1 + 5 files changed, 12 insertions(+), 3 deletions(-) diff --git a/apis/concierge/authentication/v1alpha1/types_tls.go.tmpl b/apis/concierge/authentication/v1alpha1/types_tls.go.tmpl index 883dc7fc7..db9f4ceff 100644 --- a/apis/concierge/authentication/v1alpha1/types_tls.go.tmpl +++ b/apis/concierge/authentication/v1alpha1/types_tls.go.tmpl @@ -7,6 +7,7 @@ package v1alpha1 type CertificateAuthorityDataSourceSpec struct { // Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. // Secrets must be of type kubernetes.io/tls or Opaque. + // Allowed values are "Secret" or "ConfigMap". // +kubebuilder:validation:Enum=Secret;ConfigMap Kind string `json:"kind"` // Name is the resource name of the secret or configmap from which to read the CA bundle. diff --git a/apis/concierge/config/v1alpha1/types_credentialissuer.go.tmpl b/apis/concierge/config/v1alpha1/types_credentialissuer.go.tmpl index 0ee0f0dbf..de976f5c1 100644 --- a/apis/concierge/config/v1alpha1/types_credentialissuer.go.tmpl +++ b/apis/concierge/config/v1alpha1/types_credentialissuer.go.tmpl @@ -1,4 +1,4 @@ -// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved. +// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package v1alpha1 @@ -49,6 +49,7 @@ type CredentialIssuerSpec struct { } // ImpersonationProxyMode enumerates the configuration modes for the impersonation proxy. +// Allowed values are "auto", "enabled", or "disabled". // // +kubebuilder:validation:Enum=auto;enabled;disabled type ImpersonationProxyMode string @@ -65,6 +66,7 @@ const ( ) // ImpersonationProxyServiceType enumerates the types of service that can be provisioned for the impersonation proxy. +// Allowed values are "LoadBalancer", "ClusterIP", or "None". // // +kubebuilder:validation:Enum=LoadBalancer;ClusterIP;None type ImpersonationProxyServiceType string diff --git a/apis/supervisor/config/v1alpha1/types_federationdomain.go.tmpl b/apis/supervisor/config/v1alpha1/types_federationdomain.go.tmpl index 95f7da282..d1a6e6278 100644 --- a/apis/supervisor/config/v1alpha1/types_federationdomain.go.tmpl +++ b/apis/supervisor/config/v1alpha1/types_federationdomain.go.tmpl @@ -1,4 +1,4 @@ -// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved. +// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package v1alpha1 @@ -55,6 +55,7 @@ type FederationDomainTransformsConstant struct { Name string `json:"name"` // Type determines the type of the constant, and indicates which other field should be non-empty. + // Allowed values are "string" or "stringList". // +kubebuilder:validation:Enum=string;stringList Type string `json:"type"` @@ -70,6 +71,7 @@ type FederationDomainTransformsConstant struct { // FederationDomainTransformsExpression defines a transform expression. type FederationDomainTransformsExpression struct { // Type determines the type of the expression. It must be one of the supported types. + // Allowed values are "policy/v1", "username/v1", or "groups/v1". // +kubebuilder:validation:Enum=policy/v1;username/v1;groups/v1 Type string `json:"type"` diff --git a/apis/supervisor/idp/v1alpha1/types_githubidentityprovider.go.tmpl b/apis/supervisor/idp/v1alpha1/types_githubidentityprovider.go.tmpl index c84f46dbd..437974778 100644 --- a/apis/supervisor/idp/v1alpha1/types_githubidentityprovider.go.tmpl +++ b/apis/supervisor/idp/v1alpha1/types_githubidentityprovider.go.tmpl @@ -167,7 +167,10 @@ type GitHubClientSpec struct { } type GitHubOrganizationsSpec struct { - // Policy must be set to "AllGitHubUsers" if allowed is empty. + // Allowed values are "OnlyUsersFromAllowedOrganizations" or "AllGitHubUsers". + // Defaults to "OnlyUsersFromAllowedOrganizations". + // + // Must be set to "AllGitHubUsers" if the allowed field is empty. // // This field only exists to ensure that Pinniped administrators are aware that an empty list of // allowedOrganizations means all GitHub users are allowed to log in. diff --git a/apis/supervisor/idp/v1alpha1/types_tls.go.tmpl b/apis/supervisor/idp/v1alpha1/types_tls.go.tmpl index 407a5cde5..70f1c71cc 100644 --- a/apis/supervisor/idp/v1alpha1/types_tls.go.tmpl +++ b/apis/supervisor/idp/v1alpha1/types_tls.go.tmpl @@ -7,6 +7,7 @@ package v1alpha1 type CertificateAuthorityDataSourceSpec struct { // Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. // Secrets must be of type kubernetes.io/tls or Opaque. + // Allowed values are "Secret" or "ConfigMap". // +kubebuilder:validation:Enum=Secret;ConfigMap Kind string `json:"kind"` // Name is the resource name of the secret or configmap from which to read the CA bundle. From 67de14a3b8419541156a328db388d75414d08f7a Mon Sep 17 00:00:00 2001 From: Ryan Richard Date: Sat, 3 Aug 2024 14:05:30 -0700 Subject: [PATCH 89/99] ran codegen on previous commit's changes --- ...ion.concierge.pinniped.dev_jwtauthenticators.yaml | 1 + ...concierge.pinniped.dev_webhookauthenticators.yaml | 1 + ...ig.supervisor.pinniped.dev_federationdomains.yaml | 10 ++++++---- ...inniped.dev_activedirectoryidentityproviders.yaml | 1 + ...ervisor.pinniped.dev_githubidentityproviders.yaml | 7 ++++++- ...upervisor.pinniped.dev_ldapidentityproviders.yaml | 1 + ...upervisor.pinniped.dev_oidcidentityproviders.yaml | 1 + generated/1.24/README.adoc | 12 +++++++++++- .../concierge/authentication/v1alpha1/types_tls.go | 1 + .../config/v1alpha1/types_credentialissuer.go | 4 +++- .../config/v1alpha1/types_federationdomain.go | 4 +++- .../idp/v1alpha1/types_githubidentityprovider.go | 5 ++++- .../1.24/apis/supervisor/idp/v1alpha1/types_tls.go | 1 + ...ion.concierge.pinniped.dev_jwtauthenticators.yaml | 1 + ...concierge.pinniped.dev_webhookauthenticators.yaml | 1 + ...ig.supervisor.pinniped.dev_federationdomains.yaml | 10 ++++++---- ...inniped.dev_activedirectoryidentityproviders.yaml | 1 + ...ervisor.pinniped.dev_githubidentityproviders.yaml | 7 ++++++- ...upervisor.pinniped.dev_ldapidentityproviders.yaml | 1 + ...upervisor.pinniped.dev_oidcidentityproviders.yaml | 1 + generated/1.25/README.adoc | 12 +++++++++++- .../concierge/authentication/v1alpha1/types_tls.go | 1 + .../config/v1alpha1/types_credentialissuer.go | 4 +++- .../config/v1alpha1/types_federationdomain.go | 4 +++- .../idp/v1alpha1/types_githubidentityprovider.go | 5 ++++- .../1.25/apis/supervisor/idp/v1alpha1/types_tls.go | 1 + ...ion.concierge.pinniped.dev_jwtauthenticators.yaml | 1 + ...concierge.pinniped.dev_webhookauthenticators.yaml | 1 + ...ig.supervisor.pinniped.dev_federationdomains.yaml | 10 ++++++---- ...inniped.dev_activedirectoryidentityproviders.yaml | 1 + ...ervisor.pinniped.dev_githubidentityproviders.yaml | 7 ++++++- ...upervisor.pinniped.dev_ldapidentityproviders.yaml | 1 + ...upervisor.pinniped.dev_oidcidentityproviders.yaml | 1 + generated/1.26/README.adoc | 12 +++++++++++- .../concierge/authentication/v1alpha1/types_tls.go | 1 + .../config/v1alpha1/types_credentialissuer.go | 4 +++- .../config/v1alpha1/types_federationdomain.go | 4 +++- .../idp/v1alpha1/types_githubidentityprovider.go | 5 ++++- .../1.26/apis/supervisor/idp/v1alpha1/types_tls.go | 1 + ...ion.concierge.pinniped.dev_jwtauthenticators.yaml | 1 + ...concierge.pinniped.dev_webhookauthenticators.yaml | 1 + ...ig.supervisor.pinniped.dev_federationdomains.yaml | 10 ++++++---- ...inniped.dev_activedirectoryidentityproviders.yaml | 1 + ...ervisor.pinniped.dev_githubidentityproviders.yaml | 7 ++++++- ...upervisor.pinniped.dev_ldapidentityproviders.yaml | 1 + ...upervisor.pinniped.dev_oidcidentityproviders.yaml | 1 + generated/1.27/README.adoc | 12 +++++++++++- .../concierge/authentication/v1alpha1/types_tls.go | 1 + .../config/v1alpha1/types_credentialissuer.go | 4 +++- .../config/v1alpha1/types_federationdomain.go | 4 +++- .../idp/v1alpha1/types_githubidentityprovider.go | 5 ++++- .../1.27/apis/supervisor/idp/v1alpha1/types_tls.go | 1 + ...ion.concierge.pinniped.dev_jwtauthenticators.yaml | 1 + ...concierge.pinniped.dev_webhookauthenticators.yaml | 1 + ...ig.supervisor.pinniped.dev_federationdomains.yaml | 10 ++++++---- ...inniped.dev_activedirectoryidentityproviders.yaml | 1 + ...ervisor.pinniped.dev_githubidentityproviders.yaml | 7 ++++++- ...upervisor.pinniped.dev_ldapidentityproviders.yaml | 1 + ...upervisor.pinniped.dev_oidcidentityproviders.yaml | 1 + generated/1.28/README.adoc | 12 +++++++++++- .../concierge/authentication/v1alpha1/types_tls.go | 1 + .../config/v1alpha1/types_credentialissuer.go | 4 +++- .../config/v1alpha1/types_federationdomain.go | 4 +++- .../idp/v1alpha1/types_githubidentityprovider.go | 5 ++++- .../1.28/apis/supervisor/idp/v1alpha1/types_tls.go | 1 + ...ion.concierge.pinniped.dev_jwtauthenticators.yaml | 1 + ...concierge.pinniped.dev_webhookauthenticators.yaml | 1 + ...ig.supervisor.pinniped.dev_federationdomains.yaml | 10 ++++++---- ...inniped.dev_activedirectoryidentityproviders.yaml | 1 + ...ervisor.pinniped.dev_githubidentityproviders.yaml | 7 ++++++- ...upervisor.pinniped.dev_ldapidentityproviders.yaml | 1 + ...upervisor.pinniped.dev_oidcidentityproviders.yaml | 1 + generated/1.29/README.adoc | 12 +++++++++++- .../concierge/authentication/v1alpha1/types_tls.go | 1 + .../config/v1alpha1/types_credentialissuer.go | 4 +++- .../config/v1alpha1/types_federationdomain.go | 4 +++- .../idp/v1alpha1/types_githubidentityprovider.go | 5 ++++- .../1.29/apis/supervisor/idp/v1alpha1/types_tls.go | 1 + ...ion.concierge.pinniped.dev_jwtauthenticators.yaml | 1 + ...concierge.pinniped.dev_webhookauthenticators.yaml | 1 + ...ig.supervisor.pinniped.dev_federationdomains.yaml | 10 ++++++---- ...inniped.dev_activedirectoryidentityproviders.yaml | 1 + ...ervisor.pinniped.dev_githubidentityproviders.yaml | 7 ++++++- ...upervisor.pinniped.dev_ldapidentityproviders.yaml | 1 + ...upervisor.pinniped.dev_oidcidentityproviders.yaml | 1 + generated/1.30/README.adoc | 12 +++++++++++- .../concierge/authentication/v1alpha1/types_tls.go | 1 + .../config/v1alpha1/types_credentialissuer.go | 4 +++- .../config/v1alpha1/types_federationdomain.go | 4 +++- .../idp/v1alpha1/types_githubidentityprovider.go | 5 ++++- .../1.30/apis/supervisor/idp/v1alpha1/types_tls.go | 1 + ...ion.concierge.pinniped.dev_jwtauthenticators.yaml | 1 + ...concierge.pinniped.dev_webhookauthenticators.yaml | 1 + ...ig.supervisor.pinniped.dev_federationdomains.yaml | 10 ++++++---- ...inniped.dev_activedirectoryidentityproviders.yaml | 1 + ...ervisor.pinniped.dev_githubidentityproviders.yaml | 7 ++++++- ...upervisor.pinniped.dev_ldapidentityproviders.yaml | 1 + ...upervisor.pinniped.dev_oidcidentityproviders.yaml | 1 + generated/latest/README.adoc | 12 +++++++++++- .../concierge/authentication/v1alpha1/types_tls.go | 1 + .../config/v1alpha1/types_credentialissuer.go | 4 +++- .../config/v1alpha1/types_federationdomain.go | 4 +++- .../idp/v1alpha1/types_githubidentityprovider.go | 5 ++++- .../latest/apis/supervisor/idp/v1alpha1/types_tls.go | 1 + 104 files changed, 320 insertions(+), 72 deletions(-) diff --git a/deploy/concierge/authentication.concierge.pinniped.dev_jwtauthenticators.yaml b/deploy/concierge/authentication.concierge.pinniped.dev_jwtauthenticators.yaml index 838f11edf..5366768a8 100644 --- a/deploy/concierge/authentication.concierge.pinniped.dev_jwtauthenticators.yaml +++ b/deploy/concierge/authentication.concierge.pinniped.dev_jwtauthenticators.yaml @@ -111,6 +111,7 @@ spec: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". enum: - Secret - ConfigMap diff --git a/deploy/concierge/authentication.concierge.pinniped.dev_webhookauthenticators.yaml b/deploy/concierge/authentication.concierge.pinniped.dev_webhookauthenticators.yaml index 0133d62fc..3f4f32dc9 100644 --- a/deploy/concierge/authentication.concierge.pinniped.dev_webhookauthenticators.yaml +++ b/deploy/concierge/authentication.concierge.pinniped.dev_webhookauthenticators.yaml @@ -82,6 +82,7 @@ spec: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". enum: - Secret - ConfigMap diff --git a/deploy/supervisor/config.supervisor.pinniped.dev_federationdomains.yaml b/deploy/supervisor/config.supervisor.pinniped.dev_federationdomains.yaml index 033513431..678263520 100644 --- a/deploy/supervisor/config.supervisor.pinniped.dev_federationdomains.yaml +++ b/deploy/supervisor/config.supervisor.pinniped.dev_federationdomains.yaml @@ -143,8 +143,9 @@ spec: Type is "string", and is otherwise ignored. type: string type: - description: Type determines the type of the constant, - and indicates which other field should be non-empty. + description: |- + Type determines the type of the constant, and indicates which other field should be non-empty. + Allowed values are "string" or "stringList". enum: - string - stringList @@ -262,8 +263,9 @@ spec: an authentication attempt. When empty, a default message will be used. type: string type: - description: Type determines the type of the expression. - It must be one of the supported types. + description: |- + Type determines the type of the expression. It must be one of the supported types. + Allowed values are "policy/v1", "username/v1", or "groups/v1". enum: - policy/v1 - username/v1 diff --git a/deploy/supervisor/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml b/deploy/supervisor/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml index dcc1836b6..9a5ec7c90 100644 --- a/deploy/supervisor/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml +++ b/deploy/supervisor/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml @@ -186,6 +186,7 @@ spec: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". enum: - Secret - ConfigMap diff --git a/deploy/supervisor/idp.supervisor.pinniped.dev_githubidentityproviders.yaml b/deploy/supervisor/idp.supervisor.pinniped.dev_githubidentityproviders.yaml index 669377e7c..4cf2b9bca 100644 --- a/deploy/supervisor/idp.supervisor.pinniped.dev_githubidentityproviders.yaml +++ b/deploy/supervisor/idp.supervisor.pinniped.dev_githubidentityproviders.yaml @@ -89,7 +89,11 @@ spec: policy: default: OnlyUsersFromAllowedOrganizations description: |- - Policy must be set to "AllGitHubUsers" if allowed is empty. + Allowed values are "OnlyUsersFromAllowedOrganizations" or "AllGitHubUsers". + Defaults to "OnlyUsersFromAllowedOrganizations". + + + Must be set to "AllGitHubUsers" if the allowed field is empty. This field only exists to ensure that Pinniped administrators are aware that an empty list of @@ -241,6 +245,7 @@ spec: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". enum: - Secret - ConfigMap diff --git a/deploy/supervisor/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml b/deploy/supervisor/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml index e1fb91934..08b616aed 100644 --- a/deploy/supervisor/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml +++ b/deploy/supervisor/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml @@ -177,6 +177,7 @@ spec: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". enum: - Secret - ConfigMap diff --git a/deploy/supervisor/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml b/deploy/supervisor/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml index 83ae89781..75efc06fc 100644 --- a/deploy/supervisor/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml +++ b/deploy/supervisor/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml @@ -227,6 +227,7 @@ spec: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". enum: - Secret - ConfigMap diff --git a/generated/1.24/README.adoc b/generated/1.24/README.adoc index ef1d03c8b..2029b4436 100644 --- a/generated/1.24/README.adoc +++ b/generated/1.24/README.adoc @@ -38,6 +38,7 @@ CertificateAuthorityDataSourceSpec provides a source for CA bundle used for clie | Field | Description | *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + +Allowed values are "Secret" or "ConfigMap". + | *`name`* __string__ | Name is the resource name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. + | *`key`* __string__ | Key is the key name within the secret or configmap from which to read the CA bundle. + @@ -528,6 +529,7 @@ ImpersonationProxyInfo describes the parameters for the impersonation proxy on t ==== ImpersonationProxyMode (string) ImpersonationProxyMode enumerates the configuration modes for the impersonation proxy. +Allowed values are "auto", "enabled", or "disabled". .Appears In: **** @@ -564,6 +566,7 @@ This is not supported on all cloud providers. + ==== ImpersonationProxyServiceType (string) ImpersonationProxyServiceType enumerates the types of service that can be provisioned for the impersonation proxy. +Allowed values are "LoadBalancer", "ClusterIP", or "None". .Appears In: **** @@ -953,6 +956,7 @@ the transform expressions. This is a union type, and Type is the discriminator f | Field | Description | *`name`* __string__ | Name determines the name of the constant. It must be a valid identifier name. + | *`type`* __string__ | Type determines the type of the constant, and indicates which other field should be non-empty. + +Allowed values are "string" or "stringList". + | *`stringValue`* __string__ | StringValue should hold the value when Type is "string", and is otherwise ignored. + | *`stringListValue`* __string array__ | StringListValue should hold the value when Type is "stringList", and is otherwise ignored. + |=== @@ -1019,6 +1023,7 @@ FederationDomainTransformsExpression defines a transform expression. |=== | Field | Description | *`type`* __string__ | Type determines the type of the expression. It must be one of the supported types. + +Allowed values are "policy/v1", "username/v1", or "groups/v1". + | *`expression`* __string__ | Expression is a CEL expression that will be evaluated based on the Type during an authentication. + | *`message`* __string__ | Message is only used when Type is policy/v1. It defines an error message to be used when the policy rejects + an authentication attempt. When empty, a default message will be used. + @@ -1685,6 +1690,7 @@ CertificateAuthorityDataSourceSpec provides a source for CA bundle used for clie | Field | Description | *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + +Allowed values are "Secret" or "ConfigMap". + | *`name`* __string__ | Name is the resource name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. + | *`key`* __string__ | Key is the key name within the secret or configmap from which to read the CA bundle. + @@ -1938,7 +1944,11 @@ GitHubIdentityProviderStatus is the status of an GitHub identity provider. [cols="25a,75a", options="header"] |=== | Field | Description -| *`policy`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-24-apis-supervisor-idp-v1alpha1-githuballowedauthorganizationspolicy[$$GitHubAllowedAuthOrganizationsPolicy$$]__ | Policy must be set to "AllGitHubUsers" if allowed is empty. + +| *`policy`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-24-apis-supervisor-idp-v1alpha1-githuballowedauthorganizationspolicy[$$GitHubAllowedAuthOrganizationsPolicy$$]__ | Allowed values are "OnlyUsersFromAllowedOrganizations" or "AllGitHubUsers". + +Defaults to "OnlyUsersFromAllowedOrganizations". + + + +Must be set to "AllGitHubUsers" if the allowed field is empty. + This field only exists to ensure that Pinniped administrators are aware that an empty list of + diff --git a/generated/1.24/apis/concierge/authentication/v1alpha1/types_tls.go b/generated/1.24/apis/concierge/authentication/v1alpha1/types_tls.go index 883dc7fc7..db9f4ceff 100644 --- a/generated/1.24/apis/concierge/authentication/v1alpha1/types_tls.go +++ b/generated/1.24/apis/concierge/authentication/v1alpha1/types_tls.go @@ -7,6 +7,7 @@ package v1alpha1 type CertificateAuthorityDataSourceSpec struct { // Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. // Secrets must be of type kubernetes.io/tls or Opaque. + // Allowed values are "Secret" or "ConfigMap". // +kubebuilder:validation:Enum=Secret;ConfigMap Kind string `json:"kind"` // Name is the resource name of the secret or configmap from which to read the CA bundle. diff --git a/generated/1.24/apis/concierge/config/v1alpha1/types_credentialissuer.go b/generated/1.24/apis/concierge/config/v1alpha1/types_credentialissuer.go index 0ee0f0dbf..de976f5c1 100644 --- a/generated/1.24/apis/concierge/config/v1alpha1/types_credentialissuer.go +++ b/generated/1.24/apis/concierge/config/v1alpha1/types_credentialissuer.go @@ -1,4 +1,4 @@ -// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved. +// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package v1alpha1 @@ -49,6 +49,7 @@ type CredentialIssuerSpec struct { } // ImpersonationProxyMode enumerates the configuration modes for the impersonation proxy. +// Allowed values are "auto", "enabled", or "disabled". // // +kubebuilder:validation:Enum=auto;enabled;disabled type ImpersonationProxyMode string @@ -65,6 +66,7 @@ const ( ) // ImpersonationProxyServiceType enumerates the types of service that can be provisioned for the impersonation proxy. +// Allowed values are "LoadBalancer", "ClusterIP", or "None". // // +kubebuilder:validation:Enum=LoadBalancer;ClusterIP;None type ImpersonationProxyServiceType string diff --git a/generated/1.24/apis/supervisor/config/v1alpha1/types_federationdomain.go b/generated/1.24/apis/supervisor/config/v1alpha1/types_federationdomain.go index 95f7da282..d1a6e6278 100644 --- a/generated/1.24/apis/supervisor/config/v1alpha1/types_federationdomain.go +++ b/generated/1.24/apis/supervisor/config/v1alpha1/types_federationdomain.go @@ -1,4 +1,4 @@ -// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved. +// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package v1alpha1 @@ -55,6 +55,7 @@ type FederationDomainTransformsConstant struct { Name string `json:"name"` // Type determines the type of the constant, and indicates which other field should be non-empty. + // Allowed values are "string" or "stringList". // +kubebuilder:validation:Enum=string;stringList Type string `json:"type"` @@ -70,6 +71,7 @@ type FederationDomainTransformsConstant struct { // FederationDomainTransformsExpression defines a transform expression. type FederationDomainTransformsExpression struct { // Type determines the type of the expression. It must be one of the supported types. + // Allowed values are "policy/v1", "username/v1", or "groups/v1". // +kubebuilder:validation:Enum=policy/v1;username/v1;groups/v1 Type string `json:"type"` diff --git a/generated/1.24/apis/supervisor/idp/v1alpha1/types_githubidentityprovider.go b/generated/1.24/apis/supervisor/idp/v1alpha1/types_githubidentityprovider.go index c84f46dbd..437974778 100644 --- a/generated/1.24/apis/supervisor/idp/v1alpha1/types_githubidentityprovider.go +++ b/generated/1.24/apis/supervisor/idp/v1alpha1/types_githubidentityprovider.go @@ -167,7 +167,10 @@ type GitHubClientSpec struct { } type GitHubOrganizationsSpec struct { - // Policy must be set to "AllGitHubUsers" if allowed is empty. + // Allowed values are "OnlyUsersFromAllowedOrganizations" or "AllGitHubUsers". + // Defaults to "OnlyUsersFromAllowedOrganizations". + // + // Must be set to "AllGitHubUsers" if the allowed field is empty. // // This field only exists to ensure that Pinniped administrators are aware that an empty list of // allowedOrganizations means all GitHub users are allowed to log in. diff --git a/generated/1.24/apis/supervisor/idp/v1alpha1/types_tls.go b/generated/1.24/apis/supervisor/idp/v1alpha1/types_tls.go index 407a5cde5..70f1c71cc 100644 --- a/generated/1.24/apis/supervisor/idp/v1alpha1/types_tls.go +++ b/generated/1.24/apis/supervisor/idp/v1alpha1/types_tls.go @@ -7,6 +7,7 @@ package v1alpha1 type CertificateAuthorityDataSourceSpec struct { // Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. // Secrets must be of type kubernetes.io/tls or Opaque. + // Allowed values are "Secret" or "ConfigMap". // +kubebuilder:validation:Enum=Secret;ConfigMap Kind string `json:"kind"` // Name is the resource name of the secret or configmap from which to read the CA bundle. diff --git a/generated/1.24/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml b/generated/1.24/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml index 838f11edf..5366768a8 100644 --- a/generated/1.24/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml +++ b/generated/1.24/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml @@ -111,6 +111,7 @@ spec: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". enum: - Secret - ConfigMap diff --git a/generated/1.24/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml b/generated/1.24/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml index 0133d62fc..3f4f32dc9 100644 --- a/generated/1.24/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml +++ b/generated/1.24/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml @@ -82,6 +82,7 @@ spec: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". enum: - Secret - ConfigMap diff --git a/generated/1.24/crds/config.supervisor.pinniped.dev_federationdomains.yaml b/generated/1.24/crds/config.supervisor.pinniped.dev_federationdomains.yaml index d43c7406d..b7390da91 100644 --- a/generated/1.24/crds/config.supervisor.pinniped.dev_federationdomains.yaml +++ b/generated/1.24/crds/config.supervisor.pinniped.dev_federationdomains.yaml @@ -143,8 +143,9 @@ spec: Type is "string", and is otherwise ignored. type: string type: - description: Type determines the type of the constant, - and indicates which other field should be non-empty. + description: |- + Type determines the type of the constant, and indicates which other field should be non-empty. + Allowed values are "string" or "stringList". enum: - string - stringList @@ -262,8 +263,9 @@ spec: an authentication attempt. When empty, a default message will be used. type: string type: - description: Type determines the type of the expression. - It must be one of the supported types. + description: |- + Type determines the type of the expression. It must be one of the supported types. + Allowed values are "policy/v1", "username/v1", or "groups/v1". enum: - policy/v1 - username/v1 diff --git a/generated/1.24/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml b/generated/1.24/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml index dcc1836b6..9a5ec7c90 100644 --- a/generated/1.24/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml +++ b/generated/1.24/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml @@ -186,6 +186,7 @@ spec: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". enum: - Secret - ConfigMap diff --git a/generated/1.24/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml b/generated/1.24/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml index 669377e7c..4cf2b9bca 100644 --- a/generated/1.24/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml +++ b/generated/1.24/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml @@ -89,7 +89,11 @@ spec: policy: default: OnlyUsersFromAllowedOrganizations description: |- - Policy must be set to "AllGitHubUsers" if allowed is empty. + Allowed values are "OnlyUsersFromAllowedOrganizations" or "AllGitHubUsers". + Defaults to "OnlyUsersFromAllowedOrganizations". + + + Must be set to "AllGitHubUsers" if the allowed field is empty. This field only exists to ensure that Pinniped administrators are aware that an empty list of @@ -241,6 +245,7 @@ spec: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". enum: - Secret - ConfigMap diff --git a/generated/1.24/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml b/generated/1.24/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml index e1fb91934..08b616aed 100644 --- a/generated/1.24/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml +++ b/generated/1.24/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml @@ -177,6 +177,7 @@ spec: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". enum: - Secret - ConfigMap diff --git a/generated/1.24/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml b/generated/1.24/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml index 83ae89781..75efc06fc 100644 --- a/generated/1.24/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml +++ b/generated/1.24/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml @@ -227,6 +227,7 @@ spec: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". enum: - Secret - ConfigMap diff --git a/generated/1.25/README.adoc b/generated/1.25/README.adoc index ac3c245ca..9797af1d9 100644 --- a/generated/1.25/README.adoc +++ b/generated/1.25/README.adoc @@ -38,6 +38,7 @@ CertificateAuthorityDataSourceSpec provides a source for CA bundle used for clie | Field | Description | *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + +Allowed values are "Secret" or "ConfigMap". + | *`name`* __string__ | Name is the resource name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. + | *`key`* __string__ | Key is the key name within the secret or configmap from which to read the CA bundle. + @@ -528,6 +529,7 @@ ImpersonationProxyInfo describes the parameters for the impersonation proxy on t ==== ImpersonationProxyMode (string) ImpersonationProxyMode enumerates the configuration modes for the impersonation proxy. +Allowed values are "auto", "enabled", or "disabled". .Appears In: **** @@ -564,6 +566,7 @@ This is not supported on all cloud providers. + ==== ImpersonationProxyServiceType (string) ImpersonationProxyServiceType enumerates the types of service that can be provisioned for the impersonation proxy. +Allowed values are "LoadBalancer", "ClusterIP", or "None". .Appears In: **** @@ -953,6 +956,7 @@ the transform expressions. This is a union type, and Type is the discriminator f | Field | Description | *`name`* __string__ | Name determines the name of the constant. It must be a valid identifier name. + | *`type`* __string__ | Type determines the type of the constant, and indicates which other field should be non-empty. + +Allowed values are "string" or "stringList". + | *`stringValue`* __string__ | StringValue should hold the value when Type is "string", and is otherwise ignored. + | *`stringListValue`* __string array__ | StringListValue should hold the value when Type is "stringList", and is otherwise ignored. + |=== @@ -1019,6 +1023,7 @@ FederationDomainTransformsExpression defines a transform expression. |=== | Field | Description | *`type`* __string__ | Type determines the type of the expression. It must be one of the supported types. + +Allowed values are "policy/v1", "username/v1", or "groups/v1". + | *`expression`* __string__ | Expression is a CEL expression that will be evaluated based on the Type during an authentication. + | *`message`* __string__ | Message is only used when Type is policy/v1. It defines an error message to be used when the policy rejects + an authentication attempt. When empty, a default message will be used. + @@ -1685,6 +1690,7 @@ CertificateAuthorityDataSourceSpec provides a source for CA bundle used for clie | Field | Description | *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + +Allowed values are "Secret" or "ConfigMap". + | *`name`* __string__ | Name is the resource name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. + | *`key`* __string__ | Key is the key name within the secret or configmap from which to read the CA bundle. + @@ -1938,7 +1944,11 @@ GitHubIdentityProviderStatus is the status of an GitHub identity provider. [cols="25a,75a", options="header"] |=== | Field | Description -| *`policy`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-25-apis-supervisor-idp-v1alpha1-githuballowedauthorganizationspolicy[$$GitHubAllowedAuthOrganizationsPolicy$$]__ | Policy must be set to "AllGitHubUsers" if allowed is empty. + +| *`policy`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-25-apis-supervisor-idp-v1alpha1-githuballowedauthorganizationspolicy[$$GitHubAllowedAuthOrganizationsPolicy$$]__ | Allowed values are "OnlyUsersFromAllowedOrganizations" or "AllGitHubUsers". + +Defaults to "OnlyUsersFromAllowedOrganizations". + + + +Must be set to "AllGitHubUsers" if the allowed field is empty. + This field only exists to ensure that Pinniped administrators are aware that an empty list of + diff --git a/generated/1.25/apis/concierge/authentication/v1alpha1/types_tls.go b/generated/1.25/apis/concierge/authentication/v1alpha1/types_tls.go index 883dc7fc7..db9f4ceff 100644 --- a/generated/1.25/apis/concierge/authentication/v1alpha1/types_tls.go +++ b/generated/1.25/apis/concierge/authentication/v1alpha1/types_tls.go @@ -7,6 +7,7 @@ package v1alpha1 type CertificateAuthorityDataSourceSpec struct { // Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. // Secrets must be of type kubernetes.io/tls or Opaque. + // Allowed values are "Secret" or "ConfigMap". // +kubebuilder:validation:Enum=Secret;ConfigMap Kind string `json:"kind"` // Name is the resource name of the secret or configmap from which to read the CA bundle. diff --git a/generated/1.25/apis/concierge/config/v1alpha1/types_credentialissuer.go b/generated/1.25/apis/concierge/config/v1alpha1/types_credentialissuer.go index 0ee0f0dbf..de976f5c1 100644 --- a/generated/1.25/apis/concierge/config/v1alpha1/types_credentialissuer.go +++ b/generated/1.25/apis/concierge/config/v1alpha1/types_credentialissuer.go @@ -1,4 +1,4 @@ -// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved. +// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package v1alpha1 @@ -49,6 +49,7 @@ type CredentialIssuerSpec struct { } // ImpersonationProxyMode enumerates the configuration modes for the impersonation proxy. +// Allowed values are "auto", "enabled", or "disabled". // // +kubebuilder:validation:Enum=auto;enabled;disabled type ImpersonationProxyMode string @@ -65,6 +66,7 @@ const ( ) // ImpersonationProxyServiceType enumerates the types of service that can be provisioned for the impersonation proxy. +// Allowed values are "LoadBalancer", "ClusterIP", or "None". // // +kubebuilder:validation:Enum=LoadBalancer;ClusterIP;None type ImpersonationProxyServiceType string diff --git a/generated/1.25/apis/supervisor/config/v1alpha1/types_federationdomain.go b/generated/1.25/apis/supervisor/config/v1alpha1/types_federationdomain.go index 95f7da282..d1a6e6278 100644 --- a/generated/1.25/apis/supervisor/config/v1alpha1/types_federationdomain.go +++ b/generated/1.25/apis/supervisor/config/v1alpha1/types_federationdomain.go @@ -1,4 +1,4 @@ -// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved. +// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package v1alpha1 @@ -55,6 +55,7 @@ type FederationDomainTransformsConstant struct { Name string `json:"name"` // Type determines the type of the constant, and indicates which other field should be non-empty. + // Allowed values are "string" or "stringList". // +kubebuilder:validation:Enum=string;stringList Type string `json:"type"` @@ -70,6 +71,7 @@ type FederationDomainTransformsConstant struct { // FederationDomainTransformsExpression defines a transform expression. type FederationDomainTransformsExpression struct { // Type determines the type of the expression. It must be one of the supported types. + // Allowed values are "policy/v1", "username/v1", or "groups/v1". // +kubebuilder:validation:Enum=policy/v1;username/v1;groups/v1 Type string `json:"type"` diff --git a/generated/1.25/apis/supervisor/idp/v1alpha1/types_githubidentityprovider.go b/generated/1.25/apis/supervisor/idp/v1alpha1/types_githubidentityprovider.go index c84f46dbd..437974778 100644 --- a/generated/1.25/apis/supervisor/idp/v1alpha1/types_githubidentityprovider.go +++ b/generated/1.25/apis/supervisor/idp/v1alpha1/types_githubidentityprovider.go @@ -167,7 +167,10 @@ type GitHubClientSpec struct { } type GitHubOrganizationsSpec struct { - // Policy must be set to "AllGitHubUsers" if allowed is empty. + // Allowed values are "OnlyUsersFromAllowedOrganizations" or "AllGitHubUsers". + // Defaults to "OnlyUsersFromAllowedOrganizations". + // + // Must be set to "AllGitHubUsers" if the allowed field is empty. // // This field only exists to ensure that Pinniped administrators are aware that an empty list of // allowedOrganizations means all GitHub users are allowed to log in. diff --git a/generated/1.25/apis/supervisor/idp/v1alpha1/types_tls.go b/generated/1.25/apis/supervisor/idp/v1alpha1/types_tls.go index 407a5cde5..70f1c71cc 100644 --- a/generated/1.25/apis/supervisor/idp/v1alpha1/types_tls.go +++ b/generated/1.25/apis/supervisor/idp/v1alpha1/types_tls.go @@ -7,6 +7,7 @@ package v1alpha1 type CertificateAuthorityDataSourceSpec struct { // Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. // Secrets must be of type kubernetes.io/tls or Opaque. + // Allowed values are "Secret" or "ConfigMap". // +kubebuilder:validation:Enum=Secret;ConfigMap Kind string `json:"kind"` // Name is the resource name of the secret or configmap from which to read the CA bundle. diff --git a/generated/1.25/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml b/generated/1.25/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml index 838f11edf..5366768a8 100644 --- a/generated/1.25/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml +++ b/generated/1.25/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml @@ -111,6 +111,7 @@ spec: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". enum: - Secret - ConfigMap diff --git a/generated/1.25/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml b/generated/1.25/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml index 0133d62fc..3f4f32dc9 100644 --- a/generated/1.25/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml +++ b/generated/1.25/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml @@ -82,6 +82,7 @@ spec: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". enum: - Secret - ConfigMap diff --git a/generated/1.25/crds/config.supervisor.pinniped.dev_federationdomains.yaml b/generated/1.25/crds/config.supervisor.pinniped.dev_federationdomains.yaml index d43c7406d..b7390da91 100644 --- a/generated/1.25/crds/config.supervisor.pinniped.dev_federationdomains.yaml +++ b/generated/1.25/crds/config.supervisor.pinniped.dev_federationdomains.yaml @@ -143,8 +143,9 @@ spec: Type is "string", and is otherwise ignored. type: string type: - description: Type determines the type of the constant, - and indicates which other field should be non-empty. + description: |- + Type determines the type of the constant, and indicates which other field should be non-empty. + Allowed values are "string" or "stringList". enum: - string - stringList @@ -262,8 +263,9 @@ spec: an authentication attempt. When empty, a default message will be used. type: string type: - description: Type determines the type of the expression. - It must be one of the supported types. + description: |- + Type determines the type of the expression. It must be one of the supported types. + Allowed values are "policy/v1", "username/v1", or "groups/v1". enum: - policy/v1 - username/v1 diff --git a/generated/1.25/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml b/generated/1.25/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml index dcc1836b6..9a5ec7c90 100644 --- a/generated/1.25/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml +++ b/generated/1.25/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml @@ -186,6 +186,7 @@ spec: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". enum: - Secret - ConfigMap diff --git a/generated/1.25/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml b/generated/1.25/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml index 669377e7c..4cf2b9bca 100644 --- a/generated/1.25/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml +++ b/generated/1.25/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml @@ -89,7 +89,11 @@ spec: policy: default: OnlyUsersFromAllowedOrganizations description: |- - Policy must be set to "AllGitHubUsers" if allowed is empty. + Allowed values are "OnlyUsersFromAllowedOrganizations" or "AllGitHubUsers". + Defaults to "OnlyUsersFromAllowedOrganizations". + + + Must be set to "AllGitHubUsers" if the allowed field is empty. This field only exists to ensure that Pinniped administrators are aware that an empty list of @@ -241,6 +245,7 @@ spec: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". enum: - Secret - ConfigMap diff --git a/generated/1.25/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml b/generated/1.25/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml index e1fb91934..08b616aed 100644 --- a/generated/1.25/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml +++ b/generated/1.25/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml @@ -177,6 +177,7 @@ spec: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". enum: - Secret - ConfigMap diff --git a/generated/1.25/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml b/generated/1.25/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml index 83ae89781..75efc06fc 100644 --- a/generated/1.25/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml +++ b/generated/1.25/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml @@ -227,6 +227,7 @@ spec: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". enum: - Secret - ConfigMap diff --git a/generated/1.26/README.adoc b/generated/1.26/README.adoc index 22635e322..312c1f4b0 100644 --- a/generated/1.26/README.adoc +++ b/generated/1.26/README.adoc @@ -38,6 +38,7 @@ CertificateAuthorityDataSourceSpec provides a source for CA bundle used for clie | Field | Description | *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + +Allowed values are "Secret" or "ConfigMap". + | *`name`* __string__ | Name is the resource name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. + | *`key`* __string__ | Key is the key name within the secret or configmap from which to read the CA bundle. + @@ -528,6 +529,7 @@ ImpersonationProxyInfo describes the parameters for the impersonation proxy on t ==== ImpersonationProxyMode (string) ImpersonationProxyMode enumerates the configuration modes for the impersonation proxy. +Allowed values are "auto", "enabled", or "disabled". .Appears In: **** @@ -564,6 +566,7 @@ This is not supported on all cloud providers. + ==== ImpersonationProxyServiceType (string) ImpersonationProxyServiceType enumerates the types of service that can be provisioned for the impersonation proxy. +Allowed values are "LoadBalancer", "ClusterIP", or "None". .Appears In: **** @@ -953,6 +956,7 @@ the transform expressions. This is a union type, and Type is the discriminator f | Field | Description | *`name`* __string__ | Name determines the name of the constant. It must be a valid identifier name. + | *`type`* __string__ | Type determines the type of the constant, and indicates which other field should be non-empty. + +Allowed values are "string" or "stringList". + | *`stringValue`* __string__ | StringValue should hold the value when Type is "string", and is otherwise ignored. + | *`stringListValue`* __string array__ | StringListValue should hold the value when Type is "stringList", and is otherwise ignored. + |=== @@ -1019,6 +1023,7 @@ FederationDomainTransformsExpression defines a transform expression. |=== | Field | Description | *`type`* __string__ | Type determines the type of the expression. It must be one of the supported types. + +Allowed values are "policy/v1", "username/v1", or "groups/v1". + | *`expression`* __string__ | Expression is a CEL expression that will be evaluated based on the Type during an authentication. + | *`message`* __string__ | Message is only used when Type is policy/v1. It defines an error message to be used when the policy rejects + an authentication attempt. When empty, a default message will be used. + @@ -1685,6 +1690,7 @@ CertificateAuthorityDataSourceSpec provides a source for CA bundle used for clie | Field | Description | *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + +Allowed values are "Secret" or "ConfigMap". + | *`name`* __string__ | Name is the resource name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. + | *`key`* __string__ | Key is the key name within the secret or configmap from which to read the CA bundle. + @@ -1938,7 +1944,11 @@ GitHubIdentityProviderStatus is the status of an GitHub identity provider. [cols="25a,75a", options="header"] |=== | Field | Description -| *`policy`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-26-apis-supervisor-idp-v1alpha1-githuballowedauthorganizationspolicy[$$GitHubAllowedAuthOrganizationsPolicy$$]__ | Policy must be set to "AllGitHubUsers" if allowed is empty. + +| *`policy`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-26-apis-supervisor-idp-v1alpha1-githuballowedauthorganizationspolicy[$$GitHubAllowedAuthOrganizationsPolicy$$]__ | Allowed values are "OnlyUsersFromAllowedOrganizations" or "AllGitHubUsers". + +Defaults to "OnlyUsersFromAllowedOrganizations". + + + +Must be set to "AllGitHubUsers" if the allowed field is empty. + This field only exists to ensure that Pinniped administrators are aware that an empty list of + diff --git a/generated/1.26/apis/concierge/authentication/v1alpha1/types_tls.go b/generated/1.26/apis/concierge/authentication/v1alpha1/types_tls.go index 883dc7fc7..db9f4ceff 100644 --- a/generated/1.26/apis/concierge/authentication/v1alpha1/types_tls.go +++ b/generated/1.26/apis/concierge/authentication/v1alpha1/types_tls.go @@ -7,6 +7,7 @@ package v1alpha1 type CertificateAuthorityDataSourceSpec struct { // Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. // Secrets must be of type kubernetes.io/tls or Opaque. + // Allowed values are "Secret" or "ConfigMap". // +kubebuilder:validation:Enum=Secret;ConfigMap Kind string `json:"kind"` // Name is the resource name of the secret or configmap from which to read the CA bundle. diff --git a/generated/1.26/apis/concierge/config/v1alpha1/types_credentialissuer.go b/generated/1.26/apis/concierge/config/v1alpha1/types_credentialissuer.go index 0ee0f0dbf..de976f5c1 100644 --- a/generated/1.26/apis/concierge/config/v1alpha1/types_credentialissuer.go +++ b/generated/1.26/apis/concierge/config/v1alpha1/types_credentialissuer.go @@ -1,4 +1,4 @@ -// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved. +// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package v1alpha1 @@ -49,6 +49,7 @@ type CredentialIssuerSpec struct { } // ImpersonationProxyMode enumerates the configuration modes for the impersonation proxy. +// Allowed values are "auto", "enabled", or "disabled". // // +kubebuilder:validation:Enum=auto;enabled;disabled type ImpersonationProxyMode string @@ -65,6 +66,7 @@ const ( ) // ImpersonationProxyServiceType enumerates the types of service that can be provisioned for the impersonation proxy. +// Allowed values are "LoadBalancer", "ClusterIP", or "None". // // +kubebuilder:validation:Enum=LoadBalancer;ClusterIP;None type ImpersonationProxyServiceType string diff --git a/generated/1.26/apis/supervisor/config/v1alpha1/types_federationdomain.go b/generated/1.26/apis/supervisor/config/v1alpha1/types_federationdomain.go index 95f7da282..d1a6e6278 100644 --- a/generated/1.26/apis/supervisor/config/v1alpha1/types_federationdomain.go +++ b/generated/1.26/apis/supervisor/config/v1alpha1/types_federationdomain.go @@ -1,4 +1,4 @@ -// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved. +// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package v1alpha1 @@ -55,6 +55,7 @@ type FederationDomainTransformsConstant struct { Name string `json:"name"` // Type determines the type of the constant, and indicates which other field should be non-empty. + // Allowed values are "string" or "stringList". // +kubebuilder:validation:Enum=string;stringList Type string `json:"type"` @@ -70,6 +71,7 @@ type FederationDomainTransformsConstant struct { // FederationDomainTransformsExpression defines a transform expression. type FederationDomainTransformsExpression struct { // Type determines the type of the expression. It must be one of the supported types. + // Allowed values are "policy/v1", "username/v1", or "groups/v1". // +kubebuilder:validation:Enum=policy/v1;username/v1;groups/v1 Type string `json:"type"` diff --git a/generated/1.26/apis/supervisor/idp/v1alpha1/types_githubidentityprovider.go b/generated/1.26/apis/supervisor/idp/v1alpha1/types_githubidentityprovider.go index c84f46dbd..437974778 100644 --- a/generated/1.26/apis/supervisor/idp/v1alpha1/types_githubidentityprovider.go +++ b/generated/1.26/apis/supervisor/idp/v1alpha1/types_githubidentityprovider.go @@ -167,7 +167,10 @@ type GitHubClientSpec struct { } type GitHubOrganizationsSpec struct { - // Policy must be set to "AllGitHubUsers" if allowed is empty. + // Allowed values are "OnlyUsersFromAllowedOrganizations" or "AllGitHubUsers". + // Defaults to "OnlyUsersFromAllowedOrganizations". + // + // Must be set to "AllGitHubUsers" if the allowed field is empty. // // This field only exists to ensure that Pinniped administrators are aware that an empty list of // allowedOrganizations means all GitHub users are allowed to log in. diff --git a/generated/1.26/apis/supervisor/idp/v1alpha1/types_tls.go b/generated/1.26/apis/supervisor/idp/v1alpha1/types_tls.go index 407a5cde5..70f1c71cc 100644 --- a/generated/1.26/apis/supervisor/idp/v1alpha1/types_tls.go +++ b/generated/1.26/apis/supervisor/idp/v1alpha1/types_tls.go @@ -7,6 +7,7 @@ package v1alpha1 type CertificateAuthorityDataSourceSpec struct { // Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. // Secrets must be of type kubernetes.io/tls or Opaque. + // Allowed values are "Secret" or "ConfigMap". // +kubebuilder:validation:Enum=Secret;ConfigMap Kind string `json:"kind"` // Name is the resource name of the secret or configmap from which to read the CA bundle. diff --git a/generated/1.26/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml b/generated/1.26/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml index 838f11edf..5366768a8 100644 --- a/generated/1.26/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml +++ b/generated/1.26/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml @@ -111,6 +111,7 @@ spec: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". enum: - Secret - ConfigMap diff --git a/generated/1.26/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml b/generated/1.26/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml index 0133d62fc..3f4f32dc9 100644 --- a/generated/1.26/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml +++ b/generated/1.26/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml @@ -82,6 +82,7 @@ spec: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". enum: - Secret - ConfigMap diff --git a/generated/1.26/crds/config.supervisor.pinniped.dev_federationdomains.yaml b/generated/1.26/crds/config.supervisor.pinniped.dev_federationdomains.yaml index d43c7406d..b7390da91 100644 --- a/generated/1.26/crds/config.supervisor.pinniped.dev_federationdomains.yaml +++ b/generated/1.26/crds/config.supervisor.pinniped.dev_federationdomains.yaml @@ -143,8 +143,9 @@ spec: Type is "string", and is otherwise ignored. type: string type: - description: Type determines the type of the constant, - and indicates which other field should be non-empty. + description: |- + Type determines the type of the constant, and indicates which other field should be non-empty. + Allowed values are "string" or "stringList". enum: - string - stringList @@ -262,8 +263,9 @@ spec: an authentication attempt. When empty, a default message will be used. type: string type: - description: Type determines the type of the expression. - It must be one of the supported types. + description: |- + Type determines the type of the expression. It must be one of the supported types. + Allowed values are "policy/v1", "username/v1", or "groups/v1". enum: - policy/v1 - username/v1 diff --git a/generated/1.26/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml b/generated/1.26/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml index dcc1836b6..9a5ec7c90 100644 --- a/generated/1.26/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml +++ b/generated/1.26/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml @@ -186,6 +186,7 @@ spec: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". enum: - Secret - ConfigMap diff --git a/generated/1.26/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml b/generated/1.26/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml index 669377e7c..4cf2b9bca 100644 --- a/generated/1.26/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml +++ b/generated/1.26/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml @@ -89,7 +89,11 @@ spec: policy: default: OnlyUsersFromAllowedOrganizations description: |- - Policy must be set to "AllGitHubUsers" if allowed is empty. + Allowed values are "OnlyUsersFromAllowedOrganizations" or "AllGitHubUsers". + Defaults to "OnlyUsersFromAllowedOrganizations". + + + Must be set to "AllGitHubUsers" if the allowed field is empty. This field only exists to ensure that Pinniped administrators are aware that an empty list of @@ -241,6 +245,7 @@ spec: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". enum: - Secret - ConfigMap diff --git a/generated/1.26/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml b/generated/1.26/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml index e1fb91934..08b616aed 100644 --- a/generated/1.26/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml +++ b/generated/1.26/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml @@ -177,6 +177,7 @@ spec: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". enum: - Secret - ConfigMap diff --git a/generated/1.26/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml b/generated/1.26/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml index 83ae89781..75efc06fc 100644 --- a/generated/1.26/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml +++ b/generated/1.26/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml @@ -227,6 +227,7 @@ spec: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". enum: - Secret - ConfigMap diff --git a/generated/1.27/README.adoc b/generated/1.27/README.adoc index 4408cc279..f973f3223 100644 --- a/generated/1.27/README.adoc +++ b/generated/1.27/README.adoc @@ -38,6 +38,7 @@ CertificateAuthorityDataSourceSpec provides a source for CA bundle used for clie | Field | Description | *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + +Allowed values are "Secret" or "ConfigMap". + | *`name`* __string__ | Name is the resource name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. + | *`key`* __string__ | Key is the key name within the secret or configmap from which to read the CA bundle. + @@ -528,6 +529,7 @@ ImpersonationProxyInfo describes the parameters for the impersonation proxy on t ==== ImpersonationProxyMode (string) ImpersonationProxyMode enumerates the configuration modes for the impersonation proxy. +Allowed values are "auto", "enabled", or "disabled". .Appears In: **** @@ -564,6 +566,7 @@ This is not supported on all cloud providers. + ==== ImpersonationProxyServiceType (string) ImpersonationProxyServiceType enumerates the types of service that can be provisioned for the impersonation proxy. +Allowed values are "LoadBalancer", "ClusterIP", or "None". .Appears In: **** @@ -953,6 +956,7 @@ the transform expressions. This is a union type, and Type is the discriminator f | Field | Description | *`name`* __string__ | Name determines the name of the constant. It must be a valid identifier name. + | *`type`* __string__ | Type determines the type of the constant, and indicates which other field should be non-empty. + +Allowed values are "string" or "stringList". + | *`stringValue`* __string__ | StringValue should hold the value when Type is "string", and is otherwise ignored. + | *`stringListValue`* __string array__ | StringListValue should hold the value when Type is "stringList", and is otherwise ignored. + |=== @@ -1019,6 +1023,7 @@ FederationDomainTransformsExpression defines a transform expression. |=== | Field | Description | *`type`* __string__ | Type determines the type of the expression. It must be one of the supported types. + +Allowed values are "policy/v1", "username/v1", or "groups/v1". + | *`expression`* __string__ | Expression is a CEL expression that will be evaluated based on the Type during an authentication. + | *`message`* __string__ | Message is only used when Type is policy/v1. It defines an error message to be used when the policy rejects + an authentication attempt. When empty, a default message will be used. + @@ -1685,6 +1690,7 @@ CertificateAuthorityDataSourceSpec provides a source for CA bundle used for clie | Field | Description | *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + +Allowed values are "Secret" or "ConfigMap". + | *`name`* __string__ | Name is the resource name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. + | *`key`* __string__ | Key is the key name within the secret or configmap from which to read the CA bundle. + @@ -1938,7 +1944,11 @@ GitHubIdentityProviderStatus is the status of an GitHub identity provider. [cols="25a,75a", options="header"] |=== | Field | Description -| *`policy`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-27-apis-supervisor-idp-v1alpha1-githuballowedauthorganizationspolicy[$$GitHubAllowedAuthOrganizationsPolicy$$]__ | Policy must be set to "AllGitHubUsers" if allowed is empty. + +| *`policy`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-27-apis-supervisor-idp-v1alpha1-githuballowedauthorganizationspolicy[$$GitHubAllowedAuthOrganizationsPolicy$$]__ | Allowed values are "OnlyUsersFromAllowedOrganizations" or "AllGitHubUsers". + +Defaults to "OnlyUsersFromAllowedOrganizations". + + + +Must be set to "AllGitHubUsers" if the allowed field is empty. + This field only exists to ensure that Pinniped administrators are aware that an empty list of + diff --git a/generated/1.27/apis/concierge/authentication/v1alpha1/types_tls.go b/generated/1.27/apis/concierge/authentication/v1alpha1/types_tls.go index 883dc7fc7..db9f4ceff 100644 --- a/generated/1.27/apis/concierge/authentication/v1alpha1/types_tls.go +++ b/generated/1.27/apis/concierge/authentication/v1alpha1/types_tls.go @@ -7,6 +7,7 @@ package v1alpha1 type CertificateAuthorityDataSourceSpec struct { // Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. // Secrets must be of type kubernetes.io/tls or Opaque. + // Allowed values are "Secret" or "ConfigMap". // +kubebuilder:validation:Enum=Secret;ConfigMap Kind string `json:"kind"` // Name is the resource name of the secret or configmap from which to read the CA bundle. diff --git a/generated/1.27/apis/concierge/config/v1alpha1/types_credentialissuer.go b/generated/1.27/apis/concierge/config/v1alpha1/types_credentialissuer.go index 0ee0f0dbf..de976f5c1 100644 --- a/generated/1.27/apis/concierge/config/v1alpha1/types_credentialissuer.go +++ b/generated/1.27/apis/concierge/config/v1alpha1/types_credentialissuer.go @@ -1,4 +1,4 @@ -// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved. +// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package v1alpha1 @@ -49,6 +49,7 @@ type CredentialIssuerSpec struct { } // ImpersonationProxyMode enumerates the configuration modes for the impersonation proxy. +// Allowed values are "auto", "enabled", or "disabled". // // +kubebuilder:validation:Enum=auto;enabled;disabled type ImpersonationProxyMode string @@ -65,6 +66,7 @@ const ( ) // ImpersonationProxyServiceType enumerates the types of service that can be provisioned for the impersonation proxy. +// Allowed values are "LoadBalancer", "ClusterIP", or "None". // // +kubebuilder:validation:Enum=LoadBalancer;ClusterIP;None type ImpersonationProxyServiceType string diff --git a/generated/1.27/apis/supervisor/config/v1alpha1/types_federationdomain.go b/generated/1.27/apis/supervisor/config/v1alpha1/types_federationdomain.go index 95f7da282..d1a6e6278 100644 --- a/generated/1.27/apis/supervisor/config/v1alpha1/types_federationdomain.go +++ b/generated/1.27/apis/supervisor/config/v1alpha1/types_federationdomain.go @@ -1,4 +1,4 @@ -// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved. +// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package v1alpha1 @@ -55,6 +55,7 @@ type FederationDomainTransformsConstant struct { Name string `json:"name"` // Type determines the type of the constant, and indicates which other field should be non-empty. + // Allowed values are "string" or "stringList". // +kubebuilder:validation:Enum=string;stringList Type string `json:"type"` @@ -70,6 +71,7 @@ type FederationDomainTransformsConstant struct { // FederationDomainTransformsExpression defines a transform expression. type FederationDomainTransformsExpression struct { // Type determines the type of the expression. It must be one of the supported types. + // Allowed values are "policy/v1", "username/v1", or "groups/v1". // +kubebuilder:validation:Enum=policy/v1;username/v1;groups/v1 Type string `json:"type"` diff --git a/generated/1.27/apis/supervisor/idp/v1alpha1/types_githubidentityprovider.go b/generated/1.27/apis/supervisor/idp/v1alpha1/types_githubidentityprovider.go index c84f46dbd..437974778 100644 --- a/generated/1.27/apis/supervisor/idp/v1alpha1/types_githubidentityprovider.go +++ b/generated/1.27/apis/supervisor/idp/v1alpha1/types_githubidentityprovider.go @@ -167,7 +167,10 @@ type GitHubClientSpec struct { } type GitHubOrganizationsSpec struct { - // Policy must be set to "AllGitHubUsers" if allowed is empty. + // Allowed values are "OnlyUsersFromAllowedOrganizations" or "AllGitHubUsers". + // Defaults to "OnlyUsersFromAllowedOrganizations". + // + // Must be set to "AllGitHubUsers" if the allowed field is empty. // // This field only exists to ensure that Pinniped administrators are aware that an empty list of // allowedOrganizations means all GitHub users are allowed to log in. diff --git a/generated/1.27/apis/supervisor/idp/v1alpha1/types_tls.go b/generated/1.27/apis/supervisor/idp/v1alpha1/types_tls.go index 407a5cde5..70f1c71cc 100644 --- a/generated/1.27/apis/supervisor/idp/v1alpha1/types_tls.go +++ b/generated/1.27/apis/supervisor/idp/v1alpha1/types_tls.go @@ -7,6 +7,7 @@ package v1alpha1 type CertificateAuthorityDataSourceSpec struct { // Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. // Secrets must be of type kubernetes.io/tls or Opaque. + // Allowed values are "Secret" or "ConfigMap". // +kubebuilder:validation:Enum=Secret;ConfigMap Kind string `json:"kind"` // Name is the resource name of the secret or configmap from which to read the CA bundle. diff --git a/generated/1.27/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml b/generated/1.27/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml index 838f11edf..5366768a8 100644 --- a/generated/1.27/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml +++ b/generated/1.27/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml @@ -111,6 +111,7 @@ spec: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". enum: - Secret - ConfigMap diff --git a/generated/1.27/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml b/generated/1.27/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml index 0133d62fc..3f4f32dc9 100644 --- a/generated/1.27/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml +++ b/generated/1.27/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml @@ -82,6 +82,7 @@ spec: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". enum: - Secret - ConfigMap diff --git a/generated/1.27/crds/config.supervisor.pinniped.dev_federationdomains.yaml b/generated/1.27/crds/config.supervisor.pinniped.dev_federationdomains.yaml index d43c7406d..b7390da91 100644 --- a/generated/1.27/crds/config.supervisor.pinniped.dev_federationdomains.yaml +++ b/generated/1.27/crds/config.supervisor.pinniped.dev_federationdomains.yaml @@ -143,8 +143,9 @@ spec: Type is "string", and is otherwise ignored. type: string type: - description: Type determines the type of the constant, - and indicates which other field should be non-empty. + description: |- + Type determines the type of the constant, and indicates which other field should be non-empty. + Allowed values are "string" or "stringList". enum: - string - stringList @@ -262,8 +263,9 @@ spec: an authentication attempt. When empty, a default message will be used. type: string type: - description: Type determines the type of the expression. - It must be one of the supported types. + description: |- + Type determines the type of the expression. It must be one of the supported types. + Allowed values are "policy/v1", "username/v1", or "groups/v1". enum: - policy/v1 - username/v1 diff --git a/generated/1.27/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml b/generated/1.27/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml index dcc1836b6..9a5ec7c90 100644 --- a/generated/1.27/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml +++ b/generated/1.27/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml @@ -186,6 +186,7 @@ spec: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". enum: - Secret - ConfigMap diff --git a/generated/1.27/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml b/generated/1.27/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml index 669377e7c..4cf2b9bca 100644 --- a/generated/1.27/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml +++ b/generated/1.27/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml @@ -89,7 +89,11 @@ spec: policy: default: OnlyUsersFromAllowedOrganizations description: |- - Policy must be set to "AllGitHubUsers" if allowed is empty. + Allowed values are "OnlyUsersFromAllowedOrganizations" or "AllGitHubUsers". + Defaults to "OnlyUsersFromAllowedOrganizations". + + + Must be set to "AllGitHubUsers" if the allowed field is empty. This field only exists to ensure that Pinniped administrators are aware that an empty list of @@ -241,6 +245,7 @@ spec: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". enum: - Secret - ConfigMap diff --git a/generated/1.27/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml b/generated/1.27/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml index e1fb91934..08b616aed 100644 --- a/generated/1.27/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml +++ b/generated/1.27/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml @@ -177,6 +177,7 @@ spec: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". enum: - Secret - ConfigMap diff --git a/generated/1.27/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml b/generated/1.27/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml index 83ae89781..75efc06fc 100644 --- a/generated/1.27/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml +++ b/generated/1.27/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml @@ -227,6 +227,7 @@ spec: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". enum: - Secret - ConfigMap diff --git a/generated/1.28/README.adoc b/generated/1.28/README.adoc index 65ef835b8..52bf0d702 100644 --- a/generated/1.28/README.adoc +++ b/generated/1.28/README.adoc @@ -38,6 +38,7 @@ CertificateAuthorityDataSourceSpec provides a source for CA bundle used for clie | Field | Description | *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + +Allowed values are "Secret" or "ConfigMap". + | *`name`* __string__ | Name is the resource name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. + | *`key`* __string__ | Key is the key name within the secret or configmap from which to read the CA bundle. + @@ -528,6 +529,7 @@ ImpersonationProxyInfo describes the parameters for the impersonation proxy on t ==== ImpersonationProxyMode (string) ImpersonationProxyMode enumerates the configuration modes for the impersonation proxy. +Allowed values are "auto", "enabled", or "disabled". .Appears In: **** @@ -564,6 +566,7 @@ This is not supported on all cloud providers. + ==== ImpersonationProxyServiceType (string) ImpersonationProxyServiceType enumerates the types of service that can be provisioned for the impersonation proxy. +Allowed values are "LoadBalancer", "ClusterIP", or "None". .Appears In: **** @@ -953,6 +956,7 @@ the transform expressions. This is a union type, and Type is the discriminator f | Field | Description | *`name`* __string__ | Name determines the name of the constant. It must be a valid identifier name. + | *`type`* __string__ | Type determines the type of the constant, and indicates which other field should be non-empty. + +Allowed values are "string" or "stringList". + | *`stringValue`* __string__ | StringValue should hold the value when Type is "string", and is otherwise ignored. + | *`stringListValue`* __string array__ | StringListValue should hold the value when Type is "stringList", and is otherwise ignored. + |=== @@ -1019,6 +1023,7 @@ FederationDomainTransformsExpression defines a transform expression. |=== | Field | Description | *`type`* __string__ | Type determines the type of the expression. It must be one of the supported types. + +Allowed values are "policy/v1", "username/v1", or "groups/v1". + | *`expression`* __string__ | Expression is a CEL expression that will be evaluated based on the Type during an authentication. + | *`message`* __string__ | Message is only used when Type is policy/v1. It defines an error message to be used when the policy rejects + an authentication attempt. When empty, a default message will be used. + @@ -1685,6 +1690,7 @@ CertificateAuthorityDataSourceSpec provides a source for CA bundle used for clie | Field | Description | *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + +Allowed values are "Secret" or "ConfigMap". + | *`name`* __string__ | Name is the resource name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. + | *`key`* __string__ | Key is the key name within the secret or configmap from which to read the CA bundle. + @@ -1938,7 +1944,11 @@ GitHubIdentityProviderStatus is the status of an GitHub identity provider. [cols="25a,75a", options="header"] |=== | Field | Description -| *`policy`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-28-apis-supervisor-idp-v1alpha1-githuballowedauthorganizationspolicy[$$GitHubAllowedAuthOrganizationsPolicy$$]__ | Policy must be set to "AllGitHubUsers" if allowed is empty. + +| *`policy`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-28-apis-supervisor-idp-v1alpha1-githuballowedauthorganizationspolicy[$$GitHubAllowedAuthOrganizationsPolicy$$]__ | Allowed values are "OnlyUsersFromAllowedOrganizations" or "AllGitHubUsers". + +Defaults to "OnlyUsersFromAllowedOrganizations". + + + +Must be set to "AllGitHubUsers" if the allowed field is empty. + This field only exists to ensure that Pinniped administrators are aware that an empty list of + diff --git a/generated/1.28/apis/concierge/authentication/v1alpha1/types_tls.go b/generated/1.28/apis/concierge/authentication/v1alpha1/types_tls.go index 883dc7fc7..db9f4ceff 100644 --- a/generated/1.28/apis/concierge/authentication/v1alpha1/types_tls.go +++ b/generated/1.28/apis/concierge/authentication/v1alpha1/types_tls.go @@ -7,6 +7,7 @@ package v1alpha1 type CertificateAuthorityDataSourceSpec struct { // Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. // Secrets must be of type kubernetes.io/tls or Opaque. + // Allowed values are "Secret" or "ConfigMap". // +kubebuilder:validation:Enum=Secret;ConfigMap Kind string `json:"kind"` // Name is the resource name of the secret or configmap from which to read the CA bundle. diff --git a/generated/1.28/apis/concierge/config/v1alpha1/types_credentialissuer.go b/generated/1.28/apis/concierge/config/v1alpha1/types_credentialissuer.go index 0ee0f0dbf..de976f5c1 100644 --- a/generated/1.28/apis/concierge/config/v1alpha1/types_credentialissuer.go +++ b/generated/1.28/apis/concierge/config/v1alpha1/types_credentialissuer.go @@ -1,4 +1,4 @@ -// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved. +// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package v1alpha1 @@ -49,6 +49,7 @@ type CredentialIssuerSpec struct { } // ImpersonationProxyMode enumerates the configuration modes for the impersonation proxy. +// Allowed values are "auto", "enabled", or "disabled". // // +kubebuilder:validation:Enum=auto;enabled;disabled type ImpersonationProxyMode string @@ -65,6 +66,7 @@ const ( ) // ImpersonationProxyServiceType enumerates the types of service that can be provisioned for the impersonation proxy. +// Allowed values are "LoadBalancer", "ClusterIP", or "None". // // +kubebuilder:validation:Enum=LoadBalancer;ClusterIP;None type ImpersonationProxyServiceType string diff --git a/generated/1.28/apis/supervisor/config/v1alpha1/types_federationdomain.go b/generated/1.28/apis/supervisor/config/v1alpha1/types_federationdomain.go index 95f7da282..d1a6e6278 100644 --- a/generated/1.28/apis/supervisor/config/v1alpha1/types_federationdomain.go +++ b/generated/1.28/apis/supervisor/config/v1alpha1/types_federationdomain.go @@ -1,4 +1,4 @@ -// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved. +// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package v1alpha1 @@ -55,6 +55,7 @@ type FederationDomainTransformsConstant struct { Name string `json:"name"` // Type determines the type of the constant, and indicates which other field should be non-empty. + // Allowed values are "string" or "stringList". // +kubebuilder:validation:Enum=string;stringList Type string `json:"type"` @@ -70,6 +71,7 @@ type FederationDomainTransformsConstant struct { // FederationDomainTransformsExpression defines a transform expression. type FederationDomainTransformsExpression struct { // Type determines the type of the expression. It must be one of the supported types. + // Allowed values are "policy/v1", "username/v1", or "groups/v1". // +kubebuilder:validation:Enum=policy/v1;username/v1;groups/v1 Type string `json:"type"` diff --git a/generated/1.28/apis/supervisor/idp/v1alpha1/types_githubidentityprovider.go b/generated/1.28/apis/supervisor/idp/v1alpha1/types_githubidentityprovider.go index c84f46dbd..437974778 100644 --- a/generated/1.28/apis/supervisor/idp/v1alpha1/types_githubidentityprovider.go +++ b/generated/1.28/apis/supervisor/idp/v1alpha1/types_githubidentityprovider.go @@ -167,7 +167,10 @@ type GitHubClientSpec struct { } type GitHubOrganizationsSpec struct { - // Policy must be set to "AllGitHubUsers" if allowed is empty. + // Allowed values are "OnlyUsersFromAllowedOrganizations" or "AllGitHubUsers". + // Defaults to "OnlyUsersFromAllowedOrganizations". + // + // Must be set to "AllGitHubUsers" if the allowed field is empty. // // This field only exists to ensure that Pinniped administrators are aware that an empty list of // allowedOrganizations means all GitHub users are allowed to log in. diff --git a/generated/1.28/apis/supervisor/idp/v1alpha1/types_tls.go b/generated/1.28/apis/supervisor/idp/v1alpha1/types_tls.go index 407a5cde5..70f1c71cc 100644 --- a/generated/1.28/apis/supervisor/idp/v1alpha1/types_tls.go +++ b/generated/1.28/apis/supervisor/idp/v1alpha1/types_tls.go @@ -7,6 +7,7 @@ package v1alpha1 type CertificateAuthorityDataSourceSpec struct { // Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. // Secrets must be of type kubernetes.io/tls or Opaque. + // Allowed values are "Secret" or "ConfigMap". // +kubebuilder:validation:Enum=Secret;ConfigMap Kind string `json:"kind"` // Name is the resource name of the secret or configmap from which to read the CA bundle. diff --git a/generated/1.28/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml b/generated/1.28/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml index 838f11edf..5366768a8 100644 --- a/generated/1.28/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml +++ b/generated/1.28/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml @@ -111,6 +111,7 @@ spec: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". enum: - Secret - ConfigMap diff --git a/generated/1.28/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml b/generated/1.28/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml index 0133d62fc..3f4f32dc9 100644 --- a/generated/1.28/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml +++ b/generated/1.28/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml @@ -82,6 +82,7 @@ spec: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". enum: - Secret - ConfigMap diff --git a/generated/1.28/crds/config.supervisor.pinniped.dev_federationdomains.yaml b/generated/1.28/crds/config.supervisor.pinniped.dev_federationdomains.yaml index d43c7406d..b7390da91 100644 --- a/generated/1.28/crds/config.supervisor.pinniped.dev_federationdomains.yaml +++ b/generated/1.28/crds/config.supervisor.pinniped.dev_federationdomains.yaml @@ -143,8 +143,9 @@ spec: Type is "string", and is otherwise ignored. type: string type: - description: Type determines the type of the constant, - and indicates which other field should be non-empty. + description: |- + Type determines the type of the constant, and indicates which other field should be non-empty. + Allowed values are "string" or "stringList". enum: - string - stringList @@ -262,8 +263,9 @@ spec: an authentication attempt. When empty, a default message will be used. type: string type: - description: Type determines the type of the expression. - It must be one of the supported types. + description: |- + Type determines the type of the expression. It must be one of the supported types. + Allowed values are "policy/v1", "username/v1", or "groups/v1". enum: - policy/v1 - username/v1 diff --git a/generated/1.28/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml b/generated/1.28/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml index dcc1836b6..9a5ec7c90 100644 --- a/generated/1.28/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml +++ b/generated/1.28/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml @@ -186,6 +186,7 @@ spec: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". enum: - Secret - ConfigMap diff --git a/generated/1.28/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml b/generated/1.28/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml index 669377e7c..4cf2b9bca 100644 --- a/generated/1.28/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml +++ b/generated/1.28/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml @@ -89,7 +89,11 @@ spec: policy: default: OnlyUsersFromAllowedOrganizations description: |- - Policy must be set to "AllGitHubUsers" if allowed is empty. + Allowed values are "OnlyUsersFromAllowedOrganizations" or "AllGitHubUsers". + Defaults to "OnlyUsersFromAllowedOrganizations". + + + Must be set to "AllGitHubUsers" if the allowed field is empty. This field only exists to ensure that Pinniped administrators are aware that an empty list of @@ -241,6 +245,7 @@ spec: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". enum: - Secret - ConfigMap diff --git a/generated/1.28/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml b/generated/1.28/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml index e1fb91934..08b616aed 100644 --- a/generated/1.28/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml +++ b/generated/1.28/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml @@ -177,6 +177,7 @@ spec: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". enum: - Secret - ConfigMap diff --git a/generated/1.28/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml b/generated/1.28/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml index 83ae89781..75efc06fc 100644 --- a/generated/1.28/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml +++ b/generated/1.28/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml @@ -227,6 +227,7 @@ spec: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". enum: - Secret - ConfigMap diff --git a/generated/1.29/README.adoc b/generated/1.29/README.adoc index b5ae5e4a4..5350ab237 100644 --- a/generated/1.29/README.adoc +++ b/generated/1.29/README.adoc @@ -38,6 +38,7 @@ CertificateAuthorityDataSourceSpec provides a source for CA bundle used for clie | Field | Description | *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + +Allowed values are "Secret" or "ConfigMap". + | *`name`* __string__ | Name is the resource name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. + | *`key`* __string__ | Key is the key name within the secret or configmap from which to read the CA bundle. + @@ -528,6 +529,7 @@ ImpersonationProxyInfo describes the parameters for the impersonation proxy on t ==== ImpersonationProxyMode (string) ImpersonationProxyMode enumerates the configuration modes for the impersonation proxy. +Allowed values are "auto", "enabled", or "disabled". .Appears In: **** @@ -564,6 +566,7 @@ This is not supported on all cloud providers. + ==== ImpersonationProxyServiceType (string) ImpersonationProxyServiceType enumerates the types of service that can be provisioned for the impersonation proxy. +Allowed values are "LoadBalancer", "ClusterIP", or "None". .Appears In: **** @@ -953,6 +956,7 @@ the transform expressions. This is a union type, and Type is the discriminator f | Field | Description | *`name`* __string__ | Name determines the name of the constant. It must be a valid identifier name. + | *`type`* __string__ | Type determines the type of the constant, and indicates which other field should be non-empty. + +Allowed values are "string" or "stringList". + | *`stringValue`* __string__ | StringValue should hold the value when Type is "string", and is otherwise ignored. + | *`stringListValue`* __string array__ | StringListValue should hold the value when Type is "stringList", and is otherwise ignored. + |=== @@ -1019,6 +1023,7 @@ FederationDomainTransformsExpression defines a transform expression. |=== | Field | Description | *`type`* __string__ | Type determines the type of the expression. It must be one of the supported types. + +Allowed values are "policy/v1", "username/v1", or "groups/v1". + | *`expression`* __string__ | Expression is a CEL expression that will be evaluated based on the Type during an authentication. + | *`message`* __string__ | Message is only used when Type is policy/v1. It defines an error message to be used when the policy rejects + an authentication attempt. When empty, a default message will be used. + @@ -1685,6 +1690,7 @@ CertificateAuthorityDataSourceSpec provides a source for CA bundle used for clie | Field | Description | *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + +Allowed values are "Secret" or "ConfigMap". + | *`name`* __string__ | Name is the resource name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. + | *`key`* __string__ | Key is the key name within the secret or configmap from which to read the CA bundle. + @@ -1938,7 +1944,11 @@ GitHubIdentityProviderStatus is the status of an GitHub identity provider. [cols="25a,75a", options="header"] |=== | Field | Description -| *`policy`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-29-apis-supervisor-idp-v1alpha1-githuballowedauthorganizationspolicy[$$GitHubAllowedAuthOrganizationsPolicy$$]__ | Policy must be set to "AllGitHubUsers" if allowed is empty. + +| *`policy`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-29-apis-supervisor-idp-v1alpha1-githuballowedauthorganizationspolicy[$$GitHubAllowedAuthOrganizationsPolicy$$]__ | Allowed values are "OnlyUsersFromAllowedOrganizations" or "AllGitHubUsers". + +Defaults to "OnlyUsersFromAllowedOrganizations". + + + +Must be set to "AllGitHubUsers" if the allowed field is empty. + This field only exists to ensure that Pinniped administrators are aware that an empty list of + diff --git a/generated/1.29/apis/concierge/authentication/v1alpha1/types_tls.go b/generated/1.29/apis/concierge/authentication/v1alpha1/types_tls.go index 883dc7fc7..db9f4ceff 100644 --- a/generated/1.29/apis/concierge/authentication/v1alpha1/types_tls.go +++ b/generated/1.29/apis/concierge/authentication/v1alpha1/types_tls.go @@ -7,6 +7,7 @@ package v1alpha1 type CertificateAuthorityDataSourceSpec struct { // Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. // Secrets must be of type kubernetes.io/tls or Opaque. + // Allowed values are "Secret" or "ConfigMap". // +kubebuilder:validation:Enum=Secret;ConfigMap Kind string `json:"kind"` // Name is the resource name of the secret or configmap from which to read the CA bundle. diff --git a/generated/1.29/apis/concierge/config/v1alpha1/types_credentialissuer.go b/generated/1.29/apis/concierge/config/v1alpha1/types_credentialissuer.go index 0ee0f0dbf..de976f5c1 100644 --- a/generated/1.29/apis/concierge/config/v1alpha1/types_credentialissuer.go +++ b/generated/1.29/apis/concierge/config/v1alpha1/types_credentialissuer.go @@ -1,4 +1,4 @@ -// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved. +// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package v1alpha1 @@ -49,6 +49,7 @@ type CredentialIssuerSpec struct { } // ImpersonationProxyMode enumerates the configuration modes for the impersonation proxy. +// Allowed values are "auto", "enabled", or "disabled". // // +kubebuilder:validation:Enum=auto;enabled;disabled type ImpersonationProxyMode string @@ -65,6 +66,7 @@ const ( ) // ImpersonationProxyServiceType enumerates the types of service that can be provisioned for the impersonation proxy. +// Allowed values are "LoadBalancer", "ClusterIP", or "None". // // +kubebuilder:validation:Enum=LoadBalancer;ClusterIP;None type ImpersonationProxyServiceType string diff --git a/generated/1.29/apis/supervisor/config/v1alpha1/types_federationdomain.go b/generated/1.29/apis/supervisor/config/v1alpha1/types_federationdomain.go index 95f7da282..d1a6e6278 100644 --- a/generated/1.29/apis/supervisor/config/v1alpha1/types_federationdomain.go +++ b/generated/1.29/apis/supervisor/config/v1alpha1/types_federationdomain.go @@ -1,4 +1,4 @@ -// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved. +// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package v1alpha1 @@ -55,6 +55,7 @@ type FederationDomainTransformsConstant struct { Name string `json:"name"` // Type determines the type of the constant, and indicates which other field should be non-empty. + // Allowed values are "string" or "stringList". // +kubebuilder:validation:Enum=string;stringList Type string `json:"type"` @@ -70,6 +71,7 @@ type FederationDomainTransformsConstant struct { // FederationDomainTransformsExpression defines a transform expression. type FederationDomainTransformsExpression struct { // Type determines the type of the expression. It must be one of the supported types. + // Allowed values are "policy/v1", "username/v1", or "groups/v1". // +kubebuilder:validation:Enum=policy/v1;username/v1;groups/v1 Type string `json:"type"` diff --git a/generated/1.29/apis/supervisor/idp/v1alpha1/types_githubidentityprovider.go b/generated/1.29/apis/supervisor/idp/v1alpha1/types_githubidentityprovider.go index c84f46dbd..437974778 100644 --- a/generated/1.29/apis/supervisor/idp/v1alpha1/types_githubidentityprovider.go +++ b/generated/1.29/apis/supervisor/idp/v1alpha1/types_githubidentityprovider.go @@ -167,7 +167,10 @@ type GitHubClientSpec struct { } type GitHubOrganizationsSpec struct { - // Policy must be set to "AllGitHubUsers" if allowed is empty. + // Allowed values are "OnlyUsersFromAllowedOrganizations" or "AllGitHubUsers". + // Defaults to "OnlyUsersFromAllowedOrganizations". + // + // Must be set to "AllGitHubUsers" if the allowed field is empty. // // This field only exists to ensure that Pinniped administrators are aware that an empty list of // allowedOrganizations means all GitHub users are allowed to log in. diff --git a/generated/1.29/apis/supervisor/idp/v1alpha1/types_tls.go b/generated/1.29/apis/supervisor/idp/v1alpha1/types_tls.go index 407a5cde5..70f1c71cc 100644 --- a/generated/1.29/apis/supervisor/idp/v1alpha1/types_tls.go +++ b/generated/1.29/apis/supervisor/idp/v1alpha1/types_tls.go @@ -7,6 +7,7 @@ package v1alpha1 type CertificateAuthorityDataSourceSpec struct { // Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. // Secrets must be of type kubernetes.io/tls or Opaque. + // Allowed values are "Secret" or "ConfigMap". // +kubebuilder:validation:Enum=Secret;ConfigMap Kind string `json:"kind"` // Name is the resource name of the secret or configmap from which to read the CA bundle. diff --git a/generated/1.29/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml b/generated/1.29/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml index 838f11edf..5366768a8 100644 --- a/generated/1.29/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml +++ b/generated/1.29/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml @@ -111,6 +111,7 @@ spec: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". enum: - Secret - ConfigMap diff --git a/generated/1.29/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml b/generated/1.29/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml index 0133d62fc..3f4f32dc9 100644 --- a/generated/1.29/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml +++ b/generated/1.29/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml @@ -82,6 +82,7 @@ spec: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". enum: - Secret - ConfigMap diff --git a/generated/1.29/crds/config.supervisor.pinniped.dev_federationdomains.yaml b/generated/1.29/crds/config.supervisor.pinniped.dev_federationdomains.yaml index d43c7406d..b7390da91 100644 --- a/generated/1.29/crds/config.supervisor.pinniped.dev_federationdomains.yaml +++ b/generated/1.29/crds/config.supervisor.pinniped.dev_federationdomains.yaml @@ -143,8 +143,9 @@ spec: Type is "string", and is otherwise ignored. type: string type: - description: Type determines the type of the constant, - and indicates which other field should be non-empty. + description: |- + Type determines the type of the constant, and indicates which other field should be non-empty. + Allowed values are "string" or "stringList". enum: - string - stringList @@ -262,8 +263,9 @@ spec: an authentication attempt. When empty, a default message will be used. type: string type: - description: Type determines the type of the expression. - It must be one of the supported types. + description: |- + Type determines the type of the expression. It must be one of the supported types. + Allowed values are "policy/v1", "username/v1", or "groups/v1". enum: - policy/v1 - username/v1 diff --git a/generated/1.29/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml b/generated/1.29/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml index dcc1836b6..9a5ec7c90 100644 --- a/generated/1.29/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml +++ b/generated/1.29/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml @@ -186,6 +186,7 @@ spec: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". enum: - Secret - ConfigMap diff --git a/generated/1.29/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml b/generated/1.29/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml index 669377e7c..4cf2b9bca 100644 --- a/generated/1.29/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml +++ b/generated/1.29/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml @@ -89,7 +89,11 @@ spec: policy: default: OnlyUsersFromAllowedOrganizations description: |- - Policy must be set to "AllGitHubUsers" if allowed is empty. + Allowed values are "OnlyUsersFromAllowedOrganizations" or "AllGitHubUsers". + Defaults to "OnlyUsersFromAllowedOrganizations". + + + Must be set to "AllGitHubUsers" if the allowed field is empty. This field only exists to ensure that Pinniped administrators are aware that an empty list of @@ -241,6 +245,7 @@ spec: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". enum: - Secret - ConfigMap diff --git a/generated/1.29/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml b/generated/1.29/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml index e1fb91934..08b616aed 100644 --- a/generated/1.29/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml +++ b/generated/1.29/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml @@ -177,6 +177,7 @@ spec: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". enum: - Secret - ConfigMap diff --git a/generated/1.29/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml b/generated/1.29/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml index 83ae89781..75efc06fc 100644 --- a/generated/1.29/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml +++ b/generated/1.29/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml @@ -227,6 +227,7 @@ spec: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". enum: - Secret - ConfigMap diff --git a/generated/1.30/README.adoc b/generated/1.30/README.adoc index 0b9021c99..23ad2e25b 100644 --- a/generated/1.30/README.adoc +++ b/generated/1.30/README.adoc @@ -38,6 +38,7 @@ CertificateAuthorityDataSourceSpec provides a source for CA bundle used for clie | Field | Description | *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + +Allowed values are "Secret" or "ConfigMap". + | *`name`* __string__ | Name is the resource name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. + | *`key`* __string__ | Key is the key name within the secret or configmap from which to read the CA bundle. + @@ -528,6 +529,7 @@ ImpersonationProxyInfo describes the parameters for the impersonation proxy on t ==== ImpersonationProxyMode (string) ImpersonationProxyMode enumerates the configuration modes for the impersonation proxy. +Allowed values are "auto", "enabled", or "disabled". .Appears In: **** @@ -564,6 +566,7 @@ This is not supported on all cloud providers. + ==== ImpersonationProxyServiceType (string) ImpersonationProxyServiceType enumerates the types of service that can be provisioned for the impersonation proxy. +Allowed values are "LoadBalancer", "ClusterIP", or "None". .Appears In: **** @@ -953,6 +956,7 @@ the transform expressions. This is a union type, and Type is the discriminator f | Field | Description | *`name`* __string__ | Name determines the name of the constant. It must be a valid identifier name. + | *`type`* __string__ | Type determines the type of the constant, and indicates which other field should be non-empty. + +Allowed values are "string" or "stringList". + | *`stringValue`* __string__ | StringValue should hold the value when Type is "string", and is otherwise ignored. + | *`stringListValue`* __string array__ | StringListValue should hold the value when Type is "stringList", and is otherwise ignored. + |=== @@ -1019,6 +1023,7 @@ FederationDomainTransformsExpression defines a transform expression. |=== | Field | Description | *`type`* __string__ | Type determines the type of the expression. It must be one of the supported types. + +Allowed values are "policy/v1", "username/v1", or "groups/v1". + | *`expression`* __string__ | Expression is a CEL expression that will be evaluated based on the Type during an authentication. + | *`message`* __string__ | Message is only used when Type is policy/v1. It defines an error message to be used when the policy rejects + an authentication attempt. When empty, a default message will be used. + @@ -1685,6 +1690,7 @@ CertificateAuthorityDataSourceSpec provides a source for CA bundle used for clie | Field | Description | *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + +Allowed values are "Secret" or "ConfigMap". + | *`name`* __string__ | Name is the resource name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. + | *`key`* __string__ | Key is the key name within the secret or configmap from which to read the CA bundle. + @@ -1938,7 +1944,11 @@ GitHubIdentityProviderStatus is the status of an GitHub identity provider. [cols="25a,75a", options="header"] |=== | Field | Description -| *`policy`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-supervisor-idp-v1alpha1-githuballowedauthorganizationspolicy[$$GitHubAllowedAuthOrganizationsPolicy$$]__ | Policy must be set to "AllGitHubUsers" if allowed is empty. + +| *`policy`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-supervisor-idp-v1alpha1-githuballowedauthorganizationspolicy[$$GitHubAllowedAuthOrganizationsPolicy$$]__ | Allowed values are "OnlyUsersFromAllowedOrganizations" or "AllGitHubUsers". + +Defaults to "OnlyUsersFromAllowedOrganizations". + + + +Must be set to "AllGitHubUsers" if the allowed field is empty. + This field only exists to ensure that Pinniped administrators are aware that an empty list of + diff --git a/generated/1.30/apis/concierge/authentication/v1alpha1/types_tls.go b/generated/1.30/apis/concierge/authentication/v1alpha1/types_tls.go index 883dc7fc7..db9f4ceff 100644 --- a/generated/1.30/apis/concierge/authentication/v1alpha1/types_tls.go +++ b/generated/1.30/apis/concierge/authentication/v1alpha1/types_tls.go @@ -7,6 +7,7 @@ package v1alpha1 type CertificateAuthorityDataSourceSpec struct { // Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. // Secrets must be of type kubernetes.io/tls or Opaque. + // Allowed values are "Secret" or "ConfigMap". // +kubebuilder:validation:Enum=Secret;ConfigMap Kind string `json:"kind"` // Name is the resource name of the secret or configmap from which to read the CA bundle. diff --git a/generated/1.30/apis/concierge/config/v1alpha1/types_credentialissuer.go b/generated/1.30/apis/concierge/config/v1alpha1/types_credentialissuer.go index 0ee0f0dbf..de976f5c1 100644 --- a/generated/1.30/apis/concierge/config/v1alpha1/types_credentialissuer.go +++ b/generated/1.30/apis/concierge/config/v1alpha1/types_credentialissuer.go @@ -1,4 +1,4 @@ -// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved. +// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package v1alpha1 @@ -49,6 +49,7 @@ type CredentialIssuerSpec struct { } // ImpersonationProxyMode enumerates the configuration modes for the impersonation proxy. +// Allowed values are "auto", "enabled", or "disabled". // // +kubebuilder:validation:Enum=auto;enabled;disabled type ImpersonationProxyMode string @@ -65,6 +66,7 @@ const ( ) // ImpersonationProxyServiceType enumerates the types of service that can be provisioned for the impersonation proxy. +// Allowed values are "LoadBalancer", "ClusterIP", or "None". // // +kubebuilder:validation:Enum=LoadBalancer;ClusterIP;None type ImpersonationProxyServiceType string diff --git a/generated/1.30/apis/supervisor/config/v1alpha1/types_federationdomain.go b/generated/1.30/apis/supervisor/config/v1alpha1/types_federationdomain.go index 95f7da282..d1a6e6278 100644 --- a/generated/1.30/apis/supervisor/config/v1alpha1/types_federationdomain.go +++ b/generated/1.30/apis/supervisor/config/v1alpha1/types_federationdomain.go @@ -1,4 +1,4 @@ -// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved. +// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package v1alpha1 @@ -55,6 +55,7 @@ type FederationDomainTransformsConstant struct { Name string `json:"name"` // Type determines the type of the constant, and indicates which other field should be non-empty. + // Allowed values are "string" or "stringList". // +kubebuilder:validation:Enum=string;stringList Type string `json:"type"` @@ -70,6 +71,7 @@ type FederationDomainTransformsConstant struct { // FederationDomainTransformsExpression defines a transform expression. type FederationDomainTransformsExpression struct { // Type determines the type of the expression. It must be one of the supported types. + // Allowed values are "policy/v1", "username/v1", or "groups/v1". // +kubebuilder:validation:Enum=policy/v1;username/v1;groups/v1 Type string `json:"type"` diff --git a/generated/1.30/apis/supervisor/idp/v1alpha1/types_githubidentityprovider.go b/generated/1.30/apis/supervisor/idp/v1alpha1/types_githubidentityprovider.go index c84f46dbd..437974778 100644 --- a/generated/1.30/apis/supervisor/idp/v1alpha1/types_githubidentityprovider.go +++ b/generated/1.30/apis/supervisor/idp/v1alpha1/types_githubidentityprovider.go @@ -167,7 +167,10 @@ type GitHubClientSpec struct { } type GitHubOrganizationsSpec struct { - // Policy must be set to "AllGitHubUsers" if allowed is empty. + // Allowed values are "OnlyUsersFromAllowedOrganizations" or "AllGitHubUsers". + // Defaults to "OnlyUsersFromAllowedOrganizations". + // + // Must be set to "AllGitHubUsers" if the allowed field is empty. // // This field only exists to ensure that Pinniped administrators are aware that an empty list of // allowedOrganizations means all GitHub users are allowed to log in. diff --git a/generated/1.30/apis/supervisor/idp/v1alpha1/types_tls.go b/generated/1.30/apis/supervisor/idp/v1alpha1/types_tls.go index 407a5cde5..70f1c71cc 100644 --- a/generated/1.30/apis/supervisor/idp/v1alpha1/types_tls.go +++ b/generated/1.30/apis/supervisor/idp/v1alpha1/types_tls.go @@ -7,6 +7,7 @@ package v1alpha1 type CertificateAuthorityDataSourceSpec struct { // Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. // Secrets must be of type kubernetes.io/tls or Opaque. + // Allowed values are "Secret" or "ConfigMap". // +kubebuilder:validation:Enum=Secret;ConfigMap Kind string `json:"kind"` // Name is the resource name of the secret or configmap from which to read the CA bundle. diff --git a/generated/1.30/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml b/generated/1.30/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml index 838f11edf..5366768a8 100644 --- a/generated/1.30/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml +++ b/generated/1.30/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml @@ -111,6 +111,7 @@ spec: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". enum: - Secret - ConfigMap diff --git a/generated/1.30/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml b/generated/1.30/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml index 0133d62fc..3f4f32dc9 100644 --- a/generated/1.30/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml +++ b/generated/1.30/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml @@ -82,6 +82,7 @@ spec: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". enum: - Secret - ConfigMap diff --git a/generated/1.30/crds/config.supervisor.pinniped.dev_federationdomains.yaml b/generated/1.30/crds/config.supervisor.pinniped.dev_federationdomains.yaml index 033513431..678263520 100644 --- a/generated/1.30/crds/config.supervisor.pinniped.dev_federationdomains.yaml +++ b/generated/1.30/crds/config.supervisor.pinniped.dev_federationdomains.yaml @@ -143,8 +143,9 @@ spec: Type is "string", and is otherwise ignored. type: string type: - description: Type determines the type of the constant, - and indicates which other field should be non-empty. + description: |- + Type determines the type of the constant, and indicates which other field should be non-empty. + Allowed values are "string" or "stringList". enum: - string - stringList @@ -262,8 +263,9 @@ spec: an authentication attempt. When empty, a default message will be used. type: string type: - description: Type determines the type of the expression. - It must be one of the supported types. + description: |- + Type determines the type of the expression. It must be one of the supported types. + Allowed values are "policy/v1", "username/v1", or "groups/v1". enum: - policy/v1 - username/v1 diff --git a/generated/1.30/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml b/generated/1.30/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml index dcc1836b6..9a5ec7c90 100644 --- a/generated/1.30/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml +++ b/generated/1.30/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml @@ -186,6 +186,7 @@ spec: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". enum: - Secret - ConfigMap diff --git a/generated/1.30/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml b/generated/1.30/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml index 669377e7c..4cf2b9bca 100644 --- a/generated/1.30/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml +++ b/generated/1.30/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml @@ -89,7 +89,11 @@ spec: policy: default: OnlyUsersFromAllowedOrganizations description: |- - Policy must be set to "AllGitHubUsers" if allowed is empty. + Allowed values are "OnlyUsersFromAllowedOrganizations" or "AllGitHubUsers". + Defaults to "OnlyUsersFromAllowedOrganizations". + + + Must be set to "AllGitHubUsers" if the allowed field is empty. This field only exists to ensure that Pinniped administrators are aware that an empty list of @@ -241,6 +245,7 @@ spec: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". enum: - Secret - ConfigMap diff --git a/generated/1.30/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml b/generated/1.30/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml index e1fb91934..08b616aed 100644 --- a/generated/1.30/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml +++ b/generated/1.30/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml @@ -177,6 +177,7 @@ spec: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". enum: - Secret - ConfigMap diff --git a/generated/1.30/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml b/generated/1.30/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml index 83ae89781..75efc06fc 100644 --- a/generated/1.30/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml +++ b/generated/1.30/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml @@ -227,6 +227,7 @@ spec: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". enum: - Secret - ConfigMap diff --git a/generated/latest/README.adoc b/generated/latest/README.adoc index 0b9021c99..23ad2e25b 100644 --- a/generated/latest/README.adoc +++ b/generated/latest/README.adoc @@ -38,6 +38,7 @@ CertificateAuthorityDataSourceSpec provides a source for CA bundle used for clie | Field | Description | *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + +Allowed values are "Secret" or "ConfigMap". + | *`name`* __string__ | Name is the resource name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. + | *`key`* __string__ | Key is the key name within the secret or configmap from which to read the CA bundle. + @@ -528,6 +529,7 @@ ImpersonationProxyInfo describes the parameters for the impersonation proxy on t ==== ImpersonationProxyMode (string) ImpersonationProxyMode enumerates the configuration modes for the impersonation proxy. +Allowed values are "auto", "enabled", or "disabled". .Appears In: **** @@ -564,6 +566,7 @@ This is not supported on all cloud providers. + ==== ImpersonationProxyServiceType (string) ImpersonationProxyServiceType enumerates the types of service that can be provisioned for the impersonation proxy. +Allowed values are "LoadBalancer", "ClusterIP", or "None". .Appears In: **** @@ -953,6 +956,7 @@ the transform expressions. This is a union type, and Type is the discriminator f | Field | Description | *`name`* __string__ | Name determines the name of the constant. It must be a valid identifier name. + | *`type`* __string__ | Type determines the type of the constant, and indicates which other field should be non-empty. + +Allowed values are "string" or "stringList". + | *`stringValue`* __string__ | StringValue should hold the value when Type is "string", and is otherwise ignored. + | *`stringListValue`* __string array__ | StringListValue should hold the value when Type is "stringList", and is otherwise ignored. + |=== @@ -1019,6 +1023,7 @@ FederationDomainTransformsExpression defines a transform expression. |=== | Field | Description | *`type`* __string__ | Type determines the type of the expression. It must be one of the supported types. + +Allowed values are "policy/v1", "username/v1", or "groups/v1". + | *`expression`* __string__ | Expression is a CEL expression that will be evaluated based on the Type during an authentication. + | *`message`* __string__ | Message is only used when Type is policy/v1. It defines an error message to be used when the policy rejects + an authentication attempt. When empty, a default message will be used. + @@ -1685,6 +1690,7 @@ CertificateAuthorityDataSourceSpec provides a source for CA bundle used for clie | Field | Description | *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + Secrets must be of type kubernetes.io/tls or Opaque. + +Allowed values are "Secret" or "ConfigMap". + | *`name`* __string__ | Name is the resource name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. + | *`key`* __string__ | Key is the key name within the secret or configmap from which to read the CA bundle. + @@ -1938,7 +1944,11 @@ GitHubIdentityProviderStatus is the status of an GitHub identity provider. [cols="25a,75a", options="header"] |=== | Field | Description -| *`policy`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-supervisor-idp-v1alpha1-githuballowedauthorganizationspolicy[$$GitHubAllowedAuthOrganizationsPolicy$$]__ | Policy must be set to "AllGitHubUsers" if allowed is empty. + +| *`policy`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-supervisor-idp-v1alpha1-githuballowedauthorganizationspolicy[$$GitHubAllowedAuthOrganizationsPolicy$$]__ | Allowed values are "OnlyUsersFromAllowedOrganizations" or "AllGitHubUsers". + +Defaults to "OnlyUsersFromAllowedOrganizations". + + + +Must be set to "AllGitHubUsers" if the allowed field is empty. + This field only exists to ensure that Pinniped administrators are aware that an empty list of + diff --git a/generated/latest/apis/concierge/authentication/v1alpha1/types_tls.go b/generated/latest/apis/concierge/authentication/v1alpha1/types_tls.go index 883dc7fc7..db9f4ceff 100644 --- a/generated/latest/apis/concierge/authentication/v1alpha1/types_tls.go +++ b/generated/latest/apis/concierge/authentication/v1alpha1/types_tls.go @@ -7,6 +7,7 @@ package v1alpha1 type CertificateAuthorityDataSourceSpec struct { // Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. // Secrets must be of type kubernetes.io/tls or Opaque. + // Allowed values are "Secret" or "ConfigMap". // +kubebuilder:validation:Enum=Secret;ConfigMap Kind string `json:"kind"` // Name is the resource name of the secret or configmap from which to read the CA bundle. diff --git a/generated/latest/apis/concierge/config/v1alpha1/types_credentialissuer.go b/generated/latest/apis/concierge/config/v1alpha1/types_credentialissuer.go index 0ee0f0dbf..de976f5c1 100644 --- a/generated/latest/apis/concierge/config/v1alpha1/types_credentialissuer.go +++ b/generated/latest/apis/concierge/config/v1alpha1/types_credentialissuer.go @@ -1,4 +1,4 @@ -// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved. +// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package v1alpha1 @@ -49,6 +49,7 @@ type CredentialIssuerSpec struct { } // ImpersonationProxyMode enumerates the configuration modes for the impersonation proxy. +// Allowed values are "auto", "enabled", or "disabled". // // +kubebuilder:validation:Enum=auto;enabled;disabled type ImpersonationProxyMode string @@ -65,6 +66,7 @@ const ( ) // ImpersonationProxyServiceType enumerates the types of service that can be provisioned for the impersonation proxy. +// Allowed values are "LoadBalancer", "ClusterIP", or "None". // // +kubebuilder:validation:Enum=LoadBalancer;ClusterIP;None type ImpersonationProxyServiceType string diff --git a/generated/latest/apis/supervisor/config/v1alpha1/types_federationdomain.go b/generated/latest/apis/supervisor/config/v1alpha1/types_federationdomain.go index 95f7da282..d1a6e6278 100644 --- a/generated/latest/apis/supervisor/config/v1alpha1/types_federationdomain.go +++ b/generated/latest/apis/supervisor/config/v1alpha1/types_federationdomain.go @@ -1,4 +1,4 @@ -// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved. +// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package v1alpha1 @@ -55,6 +55,7 @@ type FederationDomainTransformsConstant struct { Name string `json:"name"` // Type determines the type of the constant, and indicates which other field should be non-empty. + // Allowed values are "string" or "stringList". // +kubebuilder:validation:Enum=string;stringList Type string `json:"type"` @@ -70,6 +71,7 @@ type FederationDomainTransformsConstant struct { // FederationDomainTransformsExpression defines a transform expression. type FederationDomainTransformsExpression struct { // Type determines the type of the expression. It must be one of the supported types. + // Allowed values are "policy/v1", "username/v1", or "groups/v1". // +kubebuilder:validation:Enum=policy/v1;username/v1;groups/v1 Type string `json:"type"` diff --git a/generated/latest/apis/supervisor/idp/v1alpha1/types_githubidentityprovider.go b/generated/latest/apis/supervisor/idp/v1alpha1/types_githubidentityprovider.go index c84f46dbd..437974778 100644 --- a/generated/latest/apis/supervisor/idp/v1alpha1/types_githubidentityprovider.go +++ b/generated/latest/apis/supervisor/idp/v1alpha1/types_githubidentityprovider.go @@ -167,7 +167,10 @@ type GitHubClientSpec struct { } type GitHubOrganizationsSpec struct { - // Policy must be set to "AllGitHubUsers" if allowed is empty. + // Allowed values are "OnlyUsersFromAllowedOrganizations" or "AllGitHubUsers". + // Defaults to "OnlyUsersFromAllowedOrganizations". + // + // Must be set to "AllGitHubUsers" if the allowed field is empty. // // This field only exists to ensure that Pinniped administrators are aware that an empty list of // allowedOrganizations means all GitHub users are allowed to log in. diff --git a/generated/latest/apis/supervisor/idp/v1alpha1/types_tls.go b/generated/latest/apis/supervisor/idp/v1alpha1/types_tls.go index 407a5cde5..70f1c71cc 100644 --- a/generated/latest/apis/supervisor/idp/v1alpha1/types_tls.go +++ b/generated/latest/apis/supervisor/idp/v1alpha1/types_tls.go @@ -7,6 +7,7 @@ package v1alpha1 type CertificateAuthorityDataSourceSpec struct { // Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. // Secrets must be of type kubernetes.io/tls or Opaque. + // Allowed values are "Secret" or "ConfigMap". // +kubebuilder:validation:Enum=Secret;ConfigMap Kind string `json:"kind"` // Name is the resource name of the secret or configmap from which to read the CA bundle. From 2ebf9d3d00530a38cda9390a9dce1ff2718d4e5d Mon Sep 17 00:00:00 2001 From: Ryan Richard Date: Sat, 3 Aug 2024 14:28:45 -0700 Subject: [PATCH 90/99] minor test refactor --- test/integration/concierge_tls_spec_test.go | 67 +++++++++++--------- test/integration/supervisor_tls_spec_test.go | 20 +++--- 2 files changed, 44 insertions(+), 43 deletions(-) diff --git a/test/integration/concierge_tls_spec_test.go b/test/integration/concierge_tls_spec_test.go index 783a17b29..85d8103db 100644 --- a/test/integration/concierge_tls_spec_test.go +++ b/test/integration/concierge_tls_spec_test.go @@ -163,51 +163,42 @@ func TestTLSSpecKubeBuilderValidationConcierge_Parallel(t *testing.T) { indentedTLSYAML := strings.ReplaceAll(tc.tlsYAML, "\n", "\n ") t.Run("apply webhook authenticator", func(t *testing.T) { - webhookResourceName := "test-webhook-authenticator-" + testlib.RandHex(t, 7) - webhookYamlBytes := []byte(fmt.Sprintf(webhookAuthenticatorYamlTemplate, - env.APIGroupSuffix, webhookResourceName, env.TestWebhook.Endpoint, indentedTLSYAML)) + resourceName := "test-webhook-authenticator-" + testlib.RandHex(t, 7) + yamlBytes := []byte(fmt.Sprintf(webhookAuthenticatorYamlTemplate, + env.APIGroupSuffix, resourceName, env.TestWebhook.Endpoint, indentedTLSYAML)) - performKubectlApply( - t, - webhookYamlBytes, + stdOut, stdErr, err := performKubectlApply(t, resourceName, yamlBytes) + requireKubectlApplyResult(t, stdOut, stdErr, err, fmt.Sprintf(`webhookauthenticator.authentication.concierge.%s`, env.APIGroupSuffix), tc.expectedErrorSnippets, "WebhookAuthenticator", - webhookResourceName, + resourceName, ) }) t.Run("apply jwt authenticator", func(t *testing.T) { _, supervisorIssuer := env.InferSupervisorIssuerURL(t) - jwtAuthenticatorResourceName := "test-jwt-authenticator-" + testlib.RandHex(t, 7) - jwtAuthenticatorYamlBytes := []byte(fmt.Sprintf(jwtAuthenticatorYamlTemplate, - env.APIGroupSuffix, jwtAuthenticatorResourceName, supervisorIssuer, indentedTLSYAML)) + resourceName := "test-jwt-authenticator-" + testlib.RandHex(t, 7) + yamlBytes := []byte(fmt.Sprintf(jwtAuthenticatorYamlTemplate, + env.APIGroupSuffix, resourceName, supervisorIssuer, indentedTLSYAML)) - performKubectlApply( - t, - jwtAuthenticatorYamlBytes, + stdOut, stdErr, err := performKubectlApply(t, resourceName, yamlBytes) + requireKubectlApplyResult(t, stdOut, stdErr, err, fmt.Sprintf(`jwtauthenticator.authentication.concierge.%s`, env.APIGroupSuffix), tc.expectedErrorSnippets, "JWTAuthenticator", - jwtAuthenticatorResourceName, + resourceName, ) }) }) } } -func performKubectlApply( - t *testing.T, - yamlBytes []byte, - expectedSuccessPrefix string, - expectedErrorSnippets []string, - resourceType string, - resourceName string, -) { +func performKubectlApply(t *testing.T, resourceName string, yamlBytes []byte) (string, string, error) { t.Helper() - yamlFilepath := filepath.Join(t.TempDir(), fmt.Sprintf("tls-spec-validation-%s.yaml", resourceName)) + yamlFilepath := filepath.Join(t.TempDir(), fmt.Sprintf("test-perform-kubectl-apply-%s.yaml", resourceName)) require.NoError(t, os.WriteFile(yamlFilepath, yamlBytes, 0600)) @@ -227,17 +218,31 @@ func performKubectlApply( require.NoError(t, exec.Command("kubectl", []string{"delete", "--ignore-not-found", "-f", yamlFilepath}...).Run()) }) - if len(expectedErrorSnippets) > 0 { - actualErrorString := strings.TrimSuffix(stdErr.String(), "\n") - for i, snippet := range expectedErrorSnippets { + return stdOut.String(), stdErr.String(), err +} + +func requireKubectlApplyResult( + t *testing.T, + kubectlStdOut string, + kubectlStdErr string, + kubectlErr error, + wantSuccessPrefix string, + wantErrorSnippets []string, + wantResourceType string, + wantResourceName string, +) { + if len(wantErrorSnippets) > 0 { + require.Error(t, kubectlErr) + actualErrorString := strings.TrimSuffix(kubectlStdErr, "\n") + for i, snippet := range wantErrorSnippets { if i == 0 { - snippet = fmt.Sprintf(snippet, resourceType, resourceName) + snippet = fmt.Sprintf(snippet, wantResourceType, wantResourceName) } require.Contains(t, actualErrorString, snippet) } - return + } else { + require.Empty(t, kubectlStdErr) + require.Regexp(t, regexp.QuoteMeta(wantSuccessPrefix)+regexp.QuoteMeta(fmt.Sprintf("/%s created\n", wantResourceName)), kubectlStdOut) + require.NoError(t, kubectlErr) } - require.Empty(t, stdErr.String()) - require.Regexp(t, regexp.QuoteMeta(expectedSuccessPrefix)+regexp.QuoteMeta(fmt.Sprintf("/%s created\n", resourceName)), stdOut.String()) - require.NoError(t, err) } diff --git a/test/integration/supervisor_tls_spec_test.go b/test/integration/supervisor_tls_spec_test.go index 12990eedc..6eb978b63 100644 --- a/test/integration/supervisor_tls_spec_test.go +++ b/test/integration/supervisor_tls_spec_test.go @@ -223,9 +223,8 @@ func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { yamlBytes := []byte(fmt.Sprintf(oidcIDPTemplate, env.APIGroupSuffix, resourceName, env.SupervisorUpstreamOIDC.Issuer, indentedTLSYAML)) - performKubectlApply( - t, - yamlBytes, + stdOut, stdErr, err := performKubectlApply(t, resourceName, yamlBytes) + requireKubectlApplyResult(t, stdOut, stdErr, err, fmt.Sprintf(`oidcidentityprovider.idp.supervisor.%s`, env.APIGroupSuffix), tc.expectedErrorSnippets, "OIDCIdentityProvider", @@ -238,9 +237,8 @@ func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { yamlBytes := []byte(fmt.Sprintf(ldapIDPTemplate, env.APIGroupSuffix, resourceName, env.SupervisorUpstreamLDAP.Host, indentedTLSYAML)) - performKubectlApply( - t, - yamlBytes, + stdOut, stdErr, err := performKubectlApply(t, resourceName, yamlBytes) + requireKubectlApplyResult(t, stdOut, stdErr, err, fmt.Sprintf(`ldapidentityprovider.idp.supervisor.%s`, env.APIGroupSuffix), tc.expectedErrorSnippets, "LDAPIdentityProvider", @@ -253,9 +251,8 @@ func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { yamlBytes := []byte(fmt.Sprintf(activeDirectoryIDPTemplate, env.APIGroupSuffix, resourceName, env.SupervisorUpstreamLDAP.Host, indentedTLSYAML)) - performKubectlApply( - t, - yamlBytes, + stdOut, stdErr, err := performKubectlApply(t, resourceName, yamlBytes) + requireKubectlApplyResult(t, stdOut, stdErr, err, fmt.Sprintf(`activedirectoryidentityprovider.idp.supervisor.%s`, env.APIGroupSuffix), tc.expectedErrorSnippets, "ActiveDirectoryIdentityProvider", @@ -271,9 +268,8 @@ func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { yamlBytes := []byte(fmt.Sprintf(githubIDPTemplate, env.APIGroupSuffix, resourceName, indentedTLSYAMLForGitHub)) - performKubectlApply( - t, - yamlBytes, + stdOut, stdErr, err := performKubectlApply(t, resourceName, yamlBytes) + requireKubectlApplyResult(t, stdOut, stdErr, err, fmt.Sprintf(`githubidentityprovider.idp.supervisor.%s`, env.APIGroupSuffix), tc.expectedGitHubErrorSnippets, "GitHubIdentityProvider", From db2d7c8c50c7ec04565bb2389b1412c185aad37c Mon Sep 17 00:00:00 2001 From: Ryan Richard Date: Sat, 3 Aug 2024 16:35:44 -0700 Subject: [PATCH 91/99] assert on condition message in concierge_tls_spec_test.go and supervisor_tls_spec_test.go --- test/integration/concierge_tls_spec_test.go | 395 +++++++++++++++---- test/integration/supervisor_tls_spec_test.go | 332 +++++++++++----- 2 files changed, 535 insertions(+), 192 deletions(-) diff --git a/test/integration/concierge_tls_spec_test.go b/test/integration/concierge_tls_spec_test.go index 85d8103db..2aaefff1b 100644 --- a/test/integration/concierge_tls_spec_test.go +++ b/test/integration/concierge_tls_spec_test.go @@ -12,18 +12,26 @@ import ( "regexp" "strings" "testing" + "time" "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "go.pinniped.dev/internal/certauthority" "go.pinniped.dev/internal/here" "go.pinniped.dev/test/testlib" ) // TestTLSSpecKubeBuilderValidationConcierge_Parallel tests kubebuilder validation on the TLSSpec // in Pinniped concierge CRDs for both WebhookAuthenticators and JWTAuthenticators. -func TestTLSSpecKubeBuilderValidationConcierge_Parallel(t *testing.T) { +func TestTLSSpecValidationConcierge_Parallel(t *testing.T) { env := testlib.IntegrationEnv(t) + ca, err := certauthority.New("pinniped-test", 24*time.Hour) + require.NoError(t, err) + indentedCAPEM := indentForHeredoc(string(ca.Bundle())) + webhookAuthenticatorYamlTemplate := here.Doc(` apiVersion: authentication.concierge.%s/v1alpha1 kind: WebhookAuthenticator @@ -46,110 +54,149 @@ func TestTLSSpecKubeBuilderValidationConcierge_Parallel(t *testing.T) { `) testCases := []struct { - name string - tlsYAML string - expectedErrorSnippets []string + name string + + tlsYAML func(secretOrConfigmapName string) string + + secretOrConfigmapKind string + secretType string + secretOrConfigmapDataYAML string + + wantErrorSnippets []string + wantTLSValidConditionMessage string }{ { name: "should disallow certificate authority data source with missing name", - tlsYAML: here.Doc(` - tls: - certificateAuthorityDataSource: - kind: Secret - key: bar - `), - expectedErrorSnippets: []string{`The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.name: Required value`}, + tlsYAML: func(secretOrConfigmapName string) string { + return here.Doc(` + tls: + certificateAuthorityDataSource: + kind: Secret + key: bar + `) + }, + wantErrorSnippets: []string{`The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.name: Required value`}, }, { name: "should disallow certificate authority data source with empty value for name", - tlsYAML: here.Doc(` - tls: - certificateAuthorityDataSource: - kind: Secret - name: "" - key: bar - `), - expectedErrorSnippets: []string{`The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.name: Invalid value: "": spec.tls.certificateAuthorityDataSource.name in body should be at least 1 chars long`}, + tlsYAML: func(secretOrConfigmapName string) string { + return here.Doc(` + tls: + certificateAuthorityDataSource: + kind: Secret + name: "" + key: bar + `) + }, + wantErrorSnippets: []string{`The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.name: Invalid value: "": spec.tls.certificateAuthorityDataSource.name in body should be at least 1 chars long`}, }, { name: "should disallow certificate authority data source with missing key", - tlsYAML: here.Doc(` - tls: - certificateAuthorityDataSource: - kind: Secret - name: foo - `), - expectedErrorSnippets: []string{`The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.key: Required value`}, + tlsYAML: func(secretOrConfigmapName string) string { + return here.Doc(` + tls: + certificateAuthorityDataSource: + kind: Secret + name: foo + `) + }, + wantErrorSnippets: []string{`The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.key: Required value`}, }, { name: "should disallow certificate authority data source with empty value for key", - tlsYAML: here.Doc(` - tls: - certificateAuthorityDataSource: - kind: Secret - name: foo - key: "" - `), - expectedErrorSnippets: []string{`The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.key: Invalid value: "": spec.tls.certificateAuthorityDataSource.key in body should be at least 1 chars long`}, + tlsYAML: func(secretOrConfigmapName string) string { + return here.Doc(` + tls: + certificateAuthorityDataSource: + kind: Secret + name: foo + key: "" + `) + }, + wantErrorSnippets: []string{`The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.key: Invalid value: "": spec.tls.certificateAuthorityDataSource.key in body should be at least 1 chars long`}, }, { name: "should disallow certificate authority data source with missing kind", - tlsYAML: here.Doc(` - tls: - certificateAuthorityDataSource: - name: foo - key: bar - `), - expectedErrorSnippets: []string{`The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.kind: Required value`}, + tlsYAML: func(secretOrConfigmapName string) string { + return here.Doc(` + tls: + certificateAuthorityDataSource: + name: foo + key: bar + `) + }, + wantErrorSnippets: []string{`The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.kind: Required value`}, }, { name: "should disallow certificate authority data source with empty value for kind", - tlsYAML: here.Doc(` - tls: - certificateAuthorityDataSource: - kind: "" - name: foo - key: bar - `), - expectedErrorSnippets: []string{`The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.kind: Unsupported value: "": supported values: "Secret", "ConfigMap"`}, + tlsYAML: func(secretOrConfigmapName string) string { + return here.Doc(` + tls: + certificateAuthorityDataSource: + kind: "" + name: foo + key: bar + `) + }, + wantErrorSnippets: []string{`The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.kind: Unsupported value: "": supported values: "Secret", "ConfigMap"`}, }, { name: "should disallow certificate authority data source with invalid kind", - tlsYAML: here.Doc(` - tls: - certificateAuthorityDataSource: - kind: sorcery - name: foo - key: bar - `), - expectedErrorSnippets: []string{`The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.kind: Unsupported value: "sorcery": supported values: "Secret", "ConfigMap"`}, + tlsYAML: func(secretOrConfigmapName string) string { + return here.Doc(` + tls: + certificateAuthorityDataSource: + kind: sorcery + name: foo + key: bar + `) + }, + wantErrorSnippets: []string{`The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.kind: Unsupported value: "sorcery": supported values: "Secret", "ConfigMap"`}, }, { - name: "should create a custom resource passing all validations using a Secret source", - tlsYAML: here.Doc(` - tls: - certificateAuthorityDataSource: - kind: Secret - name: foo - key: bar - `), - expectedErrorSnippets: nil, + name: "should create a custom resource passing all validations using a Secret source of type Opaque", + secretOrConfigmapKind: "Secret", + secretType: string(corev1.SecretTypeOpaque), + secretOrConfigmapDataYAML: here.Docf(` + bar: | + %s + `, indentedCAPEM), + tlsYAML: func(secretOrConfigmapName string) string { + return here.Docf(` + tls: + certificateAuthorityDataSource: + kind: Secret + name: %s + key: bar + `, secretOrConfigmapName) + }, + wantErrorSnippets: nil, + wantTLSValidConditionMessage: `spec.tls is valid: using configured CA bundle`, }, { - name: "should create a custom resource passing all validations using a ConfigMap source", - tlsYAML: here.Doc(` - tls: - certificateAuthorityDataSource: - kind: ConfigMap - name: foo - key: bar - `), - expectedErrorSnippets: nil, + name: "should create a custom resource passing all validations using a ConfigMap source", + secretOrConfigmapKind: "ConfigMap", + secretOrConfigmapDataYAML: here.Docf(` + bar: | + %s + `, indentedCAPEM), + tlsYAML: func(secretOrConfigmapName string) string { + return here.Docf(` + tls: + certificateAuthorityDataSource: + kind: ConfigMap + name: %s + key: bar + `, secretOrConfigmapName) + }, + wantErrorSnippets: nil, + wantTLSValidConditionMessage: `spec.tls is valid: using configured CA bundle`, }, { - name: "should create a custom resource without any tls spec", - tlsYAML: "", - expectedErrorSnippets: nil, + name: "should create a custom resource without any tls spec", + tlsYAML: func(secretOrConfigmapName string) string { return "" }, + wantErrorSnippets: nil, + wantTLSValidConditionMessage: "spec.tls is valid: no TLS configuration provided: using default root CA bundle from container image", }, } @@ -157,44 +204,216 @@ func TestTLSSpecKubeBuilderValidationConcierge_Parallel(t *testing.T) { t.Run(tc.name, func(t *testing.T) { t.Parallel() - // Further indent every line except for the first line by four spaces. - // Use four spaces because that's what here.Doc uses. - // Do not indent the first line because the template already indents it. - indentedTLSYAML := strings.ReplaceAll(tc.tlsYAML, "\n", "\n ") - t.Run("apply webhook authenticator", func(t *testing.T) { resourceName := "test-webhook-authenticator-" + testlib.RandHex(t, 7) + + secretOrConfigmapResourceName := createSecretOrConfigMapFromData(t, + resourceName, + env.ConciergeNamespace, + tc.secretOrConfigmapKind, + tc.secretType, + tc.secretOrConfigmapDataYAML, + ) + yamlBytes := []byte(fmt.Sprintf(webhookAuthenticatorYamlTemplate, - env.APIGroupSuffix, resourceName, env.TestWebhook.Endpoint, indentedTLSYAML)) + env.APIGroupSuffix, resourceName, env.TestWebhook.Endpoint, + indentForHeredoc(tc.tlsYAML(secretOrConfigmapResourceName)))) stdOut, stdErr, err := performKubectlApply(t, resourceName, yamlBytes) requireKubectlApplyResult(t, stdOut, stdErr, err, fmt.Sprintf(`webhookauthenticator.authentication.concierge.%s`, env.APIGroupSuffix), - tc.expectedErrorSnippets, + tc.wantErrorSnippets, "WebhookAuthenticator", resourceName, ) + + if tc.wantErrorSnippets == nil { + requireTLSValidConditionMessageOnResource(t, + resourceName, + env.ConciergeNamespace, + "WebhookAuthenticator", + tc.wantTLSValidConditionMessage, + ) + } }) t.Run("apply jwt authenticator", func(t *testing.T) { _, supervisorIssuer := env.InferSupervisorIssuerURL(t) resourceName := "test-jwt-authenticator-" + testlib.RandHex(t, 7) + + secretOrConfigmapResourceName := createSecretOrConfigMapFromData(t, + resourceName, + env.ConciergeNamespace, + tc.secretOrConfigmapKind, + tc.secretType, + tc.secretOrConfigmapDataYAML, + ) + yamlBytes := []byte(fmt.Sprintf(jwtAuthenticatorYamlTemplate, - env.APIGroupSuffix, resourceName, supervisorIssuer, indentedTLSYAML)) + env.APIGroupSuffix, resourceName, supervisorIssuer, + indentForHeredoc(tc.tlsYAML(secretOrConfigmapResourceName)))) stdOut, stdErr, err := performKubectlApply(t, resourceName, yamlBytes) requireKubectlApplyResult(t, stdOut, stdErr, err, fmt.Sprintf(`jwtauthenticator.authentication.concierge.%s`, env.APIGroupSuffix), - tc.expectedErrorSnippets, + tc.wantErrorSnippets, "JWTAuthenticator", resourceName, ) + + if tc.wantErrorSnippets == nil { + requireTLSValidConditionMessageOnResource(t, + resourceName, + env.ConciergeNamespace, + "JWTAuthenticator", + tc.wantTLSValidConditionMessage, + ) + } }) }) } } +func indentForHeredoc(s string) string { + // Further indent every line except for the first line by four spaces. + // Use four spaces because that's what here.Doc uses. + // Do not indent the first line because the template already indents it. + return strings.ReplaceAll(s, "\n", "\n ") +} + +func requireTLSValidConditionMessageOnResource(t *testing.T, resourceName string, namespace string, resourceType string, wantMessage string) { + t.Helper() + + require.NotEmpty(t, resourceName, "bad test setup: empty resourceName") + require.NotEmpty(t, resourceType, "bad test setup: empty resourceType") + + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) + t.Cleanup(cancel) + + conciergeAuthClient := testlib.NewConciergeClientset(t).AuthenticationV1alpha1() + supervisorIDPClient := testlib.NewSupervisorClientset(t).IDPV1alpha1() + + switch resourceType { + case "JWTAuthenticator": + testlib.RequireEventuallyf(t, func(requireEventually *require.Assertions) { + got, err := conciergeAuthClient.JWTAuthenticators().Get(ctx, resourceName, metav1.GetOptions{}) + requireEventually.NoError(err) + requireConditionHasMessage(requireEventually, got.Status.Conditions, "TLSConfigurationValid", wantMessage) + }, 10*time.Second, 1*time.Second, "expected resource %s to have condition message %q", resourceName, wantMessage) + case "WebhookAuthenticator": + testlib.RequireEventuallyf(t, func(requireEventually *require.Assertions) { + got, err := conciergeAuthClient.WebhookAuthenticators().Get(ctx, resourceName, metav1.GetOptions{}) + requireEventually.NoError(err) + requireConditionHasMessage(requireEventually, got.Status.Conditions, "TLSConfigurationValid", wantMessage) + }, 10*time.Second, 1*time.Second, "expected resource %s to have condition message %q", resourceName, wantMessage) + case "OIDCIdentityProvider": + require.NotEmpty(t, namespace, "bad test setup: empty namespace") + testlib.RequireEventuallyf(t, func(requireEventually *require.Assertions) { + got, err := supervisorIDPClient.OIDCIdentityProviders(namespace).Get(ctx, resourceName, metav1.GetOptions{}) + requireEventually.NoError(err) + requireConditionHasMessage(requireEventually, got.Status.Conditions, "TLSConfigurationValid", wantMessage) + }, 10*time.Second, 1*time.Second, "expected resource %s to have condition message %q", resourceName, wantMessage) + case "LDAPIdentityProvider": + require.NotEmpty(t, namespace, "bad test setup: empty namespace") + testlib.RequireEventuallyf(t, func(requireEventually *require.Assertions) { + got, err := supervisorIDPClient.LDAPIdentityProviders(namespace).Get(ctx, resourceName, metav1.GetOptions{}) + requireEventually.NoError(err) + requireConditionHasMessage(requireEventually, got.Status.Conditions, "TLSConfigurationValid", wantMessage) + }, 10*time.Second, 1*time.Second, "expected resource %s to have condition message %q", resourceName, wantMessage) + case "ActiveDirectoryIdentityProvider": + require.NotEmpty(t, namespace, "bad test setup: empty namespace") + testlib.RequireEventuallyf(t, func(requireEventually *require.Assertions) { + got, err := supervisorIDPClient.ActiveDirectoryIdentityProviders(namespace).Get(ctx, resourceName, metav1.GetOptions{}) + requireEventually.NoError(err) + requireConditionHasMessage(requireEventually, got.Status.Conditions, "TLSConfigurationValid", wantMessage) + }, 10*time.Second, 1*time.Second, "expected resource %s to have condition message %q", resourceName, wantMessage) + case "GitHubIdentityProvider": + require.NotEmpty(t, namespace, "bad test setup: empty namespace") + testlib.RequireEventuallyf(t, func(requireEventually *require.Assertions) { + got, err := supervisorIDPClient.GitHubIdentityProviders(namespace).Get(ctx, resourceName, metav1.GetOptions{}) + requireEventually.NoError(err) + requireConditionHasMessage(requireEventually, got.Status.Conditions, "TLSConfigurationValid", wantMessage) + }, 10*time.Second, 1*time.Second, "expected resource %s to have condition message %q", resourceName, wantMessage) + default: + require.Failf(t, "unexpected resource type", "type %q", resourceType) + } +} + +func requireConditionHasMessage(assertions *require.Assertions, actualConditions []metav1.Condition, conditionType string, wantMessage string) { + assertions.NotEmpty(actualConditions, "wanted to have conditions but was empty") + for _, c := range actualConditions { + if c.Type == conditionType { + assertions.Equal(wantMessage, c.Message) + return + } + } + assertions.Failf("did not find condition with expected type", + "type %q, actual conditions: %#v", conditionType, actualConditions) +} + +func createSecretOrConfigMapFromData( + t *testing.T, + resourceNameSuffix string, + namespace string, + kind string, + secretType string, + dataYAML string, +) string { + t.Helper() + + if kind == "" { + // Nothing to create. + return "" + } + + require.NotEmpty(t, resourceNameSuffix, "bad test setup: empty resourceNameSuffix") + require.NotEmpty(t, namespace, "bad test setup: empty namespace") + + var resourceYAML string + lowerKind := strings.ToLower(kind) + resourceName := lowerKind + "-" + resourceNameSuffix + + // Further indent every line except for the first line by four spaces. + // Use four spaces because that's what here.Doc uses. + // Do not indent the first line because the template already indents it. + indentedDataYAML := strings.ReplaceAll(dataYAML, "\n", "\n ") + + switch lowerKind { + case "secret": + require.NotEmpty(t, secretType, "bad test setup: empty secret type") + resourceYAML = here.Docf(` + apiVersion: v1 + kind: Secret + metadata: + name: %s + namespace: %s + type: %s + stringData: + %s + `, resourceName, namespace, secretType, indentedDataYAML) + case "configmap": + resourceYAML = here.Docf(` + apiVersion: v1 + kind: ConfigMap + metadata: + name: %s + namespace: %s + data: + %s + `, resourceName, namespace, indentedDataYAML) + default: + require.Failf(t, "unexpected kind in test setup", "kind was %q", kind) + } + + stdOut, stdErr, err := performKubectlApply(t, resourceName, []byte(resourceYAML)) + require.NoErrorf(t, err, + "expected kubectl apply to succeed but got: %s\nstdout: %s\nstderr: %s\nyaml:\n%s", + err, stdOut, stdErr, resourceYAML) + + return resourceName +} + func performKubectlApply(t *testing.T, resourceName string, yamlBytes []byte) (string, string, error) { t.Helper() @@ -231,6 +450,8 @@ func requireKubectlApplyResult( wantResourceType string, wantResourceName string, ) { + t.Helper() + if len(wantErrorSnippets) > 0 { require.Error(t, kubectlErr) actualErrorString := strings.TrimSuffix(kubectlStdErr, "\n") diff --git a/test/integration/supervisor_tls_spec_test.go b/test/integration/supervisor_tls_spec_test.go index 6eb978b63..687f15d9b 100644 --- a/test/integration/supervisor_tls_spec_test.go +++ b/test/integration/supervisor_tls_spec_test.go @@ -6,21 +6,31 @@ import ( "fmt" "strings" "testing" + "time" + "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" + + "go.pinniped.dev/internal/certauthority" "go.pinniped.dev/internal/here" "go.pinniped.dev/test/testlib" ) // TestTLSSpecKubeBuilderValidationSupervisor_Parallel tests kubebuilder validation // on the TLSSpec in Pinniped supervisor CRDs using OIDCIdentityProvider as an example. -func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { +func TestTLSSpecValidationSupervisor_Parallel(t *testing.T) { env := testlib.IntegrationEnv(t) + ca, err := certauthority.New("pinniped-test", 24*time.Hour) + require.NoError(t, err) + indentedCAPEM := indentForHeredoc(string(ca.Bundle())) + oidcIDPTemplate := here.Doc(` apiVersion: idp.supervisor.%s/v1alpha1 kind: OIDCIdentityProvider metadata: name: %s + namespace: %s spec: issuer: %s authorizationConfig: @@ -36,6 +46,7 @@ func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { kind: LDAPIdentityProvider metadata: name: %s + namespace: %s spec: host: %s bind: @@ -53,6 +64,7 @@ func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { kind: ActiveDirectoryIdentityProvider metadata: name: %s + namespace: %s spec: host: %s bind: @@ -65,6 +77,7 @@ func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { kind: GitHubIdentityProvider metadata: name: %s + namespace: %s spec: allowAuthentication: organizations: @@ -76,136 +89,174 @@ func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { `) testCases := []struct { - name string - tlsYAML string - expectedErrorSnippets []string - expectedGitHubErrorSnippets []string + name string + + tlsYAML func(secretOrConfigmapName string) string + + secretOrConfigmapKind string + secretType string + secretOrConfigmapDataYAML string + + wantErrorSnippets []string + wantGitHubErrorSnippets []string + wantTLSValidConditionMessage string }{ { name: "should disallow certificate authority data source with missing name", - tlsYAML: here.Doc(` - tls: - certificateAuthorityDataSource: - kind: Secret - key: bar - `), - expectedErrorSnippets: []string{`The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.name: Required value`}, - expectedGitHubErrorSnippets: []string{ + tlsYAML: func(secretOrConfigmapName string) string { + return here.Doc(` + tls: + certificateAuthorityDataSource: + kind: Secret + key: bar + `) + }, + wantErrorSnippets: []string{`The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.name: Required value`}, + wantGitHubErrorSnippets: []string{ `The %s "%s" is invalid:`, "spec.githubAPI.tls.certificateAuthorityDataSource.name: Required value", }, }, { name: "should disallow certificate authority data source with empty value for name", - tlsYAML: here.Doc(` - tls: - certificateAuthorityDataSource: - kind: Secret - name: "" - key: bar - `), - expectedErrorSnippets: []string{`The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.name: Invalid value: "": spec.tls.certificateAuthorityDataSource.name in body should be at least 1 chars long`}, - expectedGitHubErrorSnippets: []string{`The %s "%s" is invalid: spec.githubAPI.tls.certificateAuthorityDataSource.name: Invalid value: "": spec.githubAPI.tls.certificateAuthorityDataSource.name in body should be at least 1 chars long`}, + tlsYAML: func(secretOrConfigmapName string) string { + return here.Doc(` + tls: + certificateAuthorityDataSource: + kind: Secret + name: "" + key: bar + `) + }, + wantErrorSnippets: []string{`The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.name: Invalid value: "": spec.tls.certificateAuthorityDataSource.name in body should be at least 1 chars long`}, + wantGitHubErrorSnippets: []string{`The %s "%s" is invalid: spec.githubAPI.tls.certificateAuthorityDataSource.name: Invalid value: "": spec.githubAPI.tls.certificateAuthorityDataSource.name in body should be at least 1 chars long`}, }, { name: "should disallow certificate authority data source with missing key", - tlsYAML: here.Doc(` - tls: - certificateAuthorityDataSource: - kind: Secret - name: foo - `), - expectedErrorSnippets: []string{`The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.key: Required value`}, - expectedGitHubErrorSnippets: []string{ + tlsYAML: func(secretOrConfigmapName string) string { + return here.Doc(` + tls: + certificateAuthorityDataSource: + kind: Secret + name: foo + `) + }, + wantErrorSnippets: []string{`The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.key: Required value`}, + wantGitHubErrorSnippets: []string{ `The %s "%s" is invalid:`, "spec.githubAPI.tls.certificateAuthorityDataSource.key: Required value", }, }, { name: "should disallow certificate authority data source with empty value for key", - tlsYAML: here.Doc(` - tls: - certificateAuthorityDataSource: - kind: Secret - name: foo - key: "" - `), - expectedErrorSnippets: []string{`The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.key: Invalid value: "": spec.tls.certificateAuthorityDataSource.key in body should be at least 1 chars long`}, - expectedGitHubErrorSnippets: []string{`The %s "%s" is invalid: spec.githubAPI.tls.certificateAuthorityDataSource.key: Invalid value: "": spec.githubAPI.tls.certificateAuthorityDataSource.key in body should be at least 1 chars long`}, + tlsYAML: func(secretOrConfigmapName string) string { + return here.Doc(` + tls: + certificateAuthorityDataSource: + kind: Secret + name: foo + key: "" + `) + }, + wantErrorSnippets: []string{`The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.key: Invalid value: "": spec.tls.certificateAuthorityDataSource.key in body should be at least 1 chars long`}, + wantGitHubErrorSnippets: []string{`The %s "%s" is invalid: spec.githubAPI.tls.certificateAuthorityDataSource.key: Invalid value: "": spec.githubAPI.tls.certificateAuthorityDataSource.key in body should be at least 1 chars long`}, }, { name: "should disallow certificate authority data source with missing kind", - tlsYAML: here.Doc(` - tls: - certificateAuthorityDataSource: - name: foo - key: bar - `), - expectedErrorSnippets: []string{`The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.kind: Required value`}, - expectedGitHubErrorSnippets: []string{ + tlsYAML: func(secretOrConfigmapName string) string { + return here.Doc(` + tls: + certificateAuthorityDataSource: + name: foo + key: bar + `) + }, + wantErrorSnippets: []string{`The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.kind: Required value`}, + wantGitHubErrorSnippets: []string{ `The %s "%s" is invalid:`, "spec.githubAPI.tls.certificateAuthorityDataSource.kind: Required value", }, }, { name: "should disallow certificate authority data source with empty value for kind", - tlsYAML: here.Doc(` - tls: - certificateAuthorityDataSource: - kind: "" - name: foo - key: bar - `), - expectedErrorSnippets: []string{`The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.kind: Unsupported value: "": supported values: "Secret", "ConfigMap"`}, - expectedGitHubErrorSnippets: []string{ + tlsYAML: func(secretOrConfigmapName string) string { + return here.Doc(` + tls: + certificateAuthorityDataSource: + kind: "" + name: foo + key: bar + `) + }, + wantErrorSnippets: []string{`The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.kind: Unsupported value: "": supported values: "Secret", "ConfigMap"`}, + wantGitHubErrorSnippets: []string{ `The %s "%s" is invalid:`, `spec.githubAPI.tls.certificateAuthorityDataSource.kind: Unsupported value: "": supported values: "Secret", "ConfigMap"`, }, }, { name: "should disallow certificate authority data source with invalid kind", - tlsYAML: here.Doc(` - tls: - certificateAuthorityDataSource: - kind: sorcery - name: foo - key: bar - `), - expectedErrorSnippets: []string{`The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.kind: Unsupported value: "sorcery": supported values: "Secret", "ConfigMap"`}, - expectedGitHubErrorSnippets: []string{ + tlsYAML: func(secretOrConfigmapName string) string { + return here.Doc(` + tls: + certificateAuthorityDataSource: + kind: sorcery + name: foo + key: bar + `) + }, + wantErrorSnippets: []string{`The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.kind: Unsupported value: "sorcery": supported values: "Secret", "ConfigMap"`}, + wantGitHubErrorSnippets: []string{ `The %s "%s" is invalid:`, `spec.githubAPI.tls.certificateAuthorityDataSource.kind: Unsupported value: "sorcery": supported values: "Secret", "ConfigMap"`, }, }, { - name: "should create a custom resource passing all validations using a Secret source", - tlsYAML: here.Doc(` - tls: - certificateAuthorityDataSource: - kind: Secret - name: foo - key: bar - `), - expectedErrorSnippets: nil, - expectedGitHubErrorSnippets: nil, + name: "should create a custom resource passing all validations using a Secret source of type Opaque", + secretOrConfigmapKind: "Secret", + secretType: string(corev1.SecretTypeOpaque), + secretOrConfigmapDataYAML: here.Docf(` + bar: | + %s + `, indentedCAPEM), + tlsYAML: func(secretOrConfigmapName string) string { + return here.Docf(` + tls: + certificateAuthorityDataSource: + kind: Secret + name: %s + key: bar + `, secretOrConfigmapName) + }, + wantErrorSnippets: nil, + wantGitHubErrorSnippets: nil, + wantTLSValidConditionMessage: "spec.tls is valid: using configured CA bundle", }, { - name: "should create a custom resource passing all validations using a ConfigMap source", - tlsYAML: here.Doc(` - tls: - certificateAuthorityDataSource: - kind: ConfigMap - name: foo - key: bar - `), - expectedErrorSnippets: nil, - expectedGitHubErrorSnippets: nil, + name: "should create a custom resource passing all validations using a ConfigMap source", + secretOrConfigmapKind: "ConfigMap", + secretOrConfigmapDataYAML: here.Docf(` + bar: | + %s + `, indentedCAPEM), + tlsYAML: func(secretOrConfigmapName string) string { + return here.Docf(` + tls: + certificateAuthorityDataSource: + kind: ConfigMap + name: %s + key: bar + `, secretOrConfigmapName) + }, + wantErrorSnippets: nil, + wantTLSValidConditionMessage: `spec.tls is valid: using configured CA bundle`, }, { - name: "should create a custom resource without any tls spec", - tlsYAML: "", - expectedErrorSnippets: nil, - expectedGitHubErrorSnippets: nil, + name: "should create a custom resource without any tls spec", + tlsYAML: func(secretOrConfigmapName string) string { return "" }, + wantErrorSnippets: nil, + wantGitHubErrorSnippets: nil, + wantTLSValidConditionMessage: "spec.tls is valid: no TLS configuration provided: using default root CA bundle from container image", }, } @@ -213,68 +264,139 @@ func TestTLSSpecKubeBuilderValidationSupervisor_Parallel(t *testing.T) { t.Run(tc.name, func(t *testing.T) { t.Parallel() - // Further indent every line except for the first line by four spaces. - // Use four spaces because that's what here.Doc uses. - // Do not indent the first line because the template already indents it. - indentedTLSYAML := strings.ReplaceAll(tc.tlsYAML, "\n", "\n ") - t.Run("apply OIDC IDP", func(t *testing.T) { resourceName := "test-oidc-idp-" + testlib.RandHex(t, 7) + + secretOrConfigmapResourceName := createSecretOrConfigMapFromData(t, + resourceName, + env.SupervisorNamespace, + tc.secretOrConfigmapKind, + tc.secretType, + tc.secretOrConfigmapDataYAML, + ) + yamlBytes := []byte(fmt.Sprintf(oidcIDPTemplate, - env.APIGroupSuffix, resourceName, env.SupervisorUpstreamOIDC.Issuer, indentedTLSYAML)) + env.APIGroupSuffix, resourceName, env.SupervisorNamespace, env.SupervisorUpstreamOIDC.Issuer, + indentForHeredoc(tc.tlsYAML(secretOrConfigmapResourceName)))) stdOut, stdErr, err := performKubectlApply(t, resourceName, yamlBytes) requireKubectlApplyResult(t, stdOut, stdErr, err, fmt.Sprintf(`oidcidentityprovider.idp.supervisor.%s`, env.APIGroupSuffix), - tc.expectedErrorSnippets, + tc.wantErrorSnippets, "OIDCIdentityProvider", resourceName, ) + + if tc.wantErrorSnippets == nil { + requireTLSValidConditionMessageOnResource(t, + resourceName, + env.SupervisorNamespace, + "OIDCIdentityProvider", + tc.wantTLSValidConditionMessage, + ) + } }) t.Run("apply LDAP IDP", func(t *testing.T) { resourceName := "test-ldap-idp-" + testlib.RandHex(t, 7) + + secretOrConfigmapResourceName := createSecretOrConfigMapFromData(t, + resourceName, + env.SupervisorNamespace, + tc.secretOrConfigmapKind, + tc.secretType, + tc.secretOrConfigmapDataYAML, + ) + yamlBytes := []byte(fmt.Sprintf(ldapIDPTemplate, - env.APIGroupSuffix, resourceName, env.SupervisorUpstreamLDAP.Host, indentedTLSYAML)) + env.APIGroupSuffix, resourceName, env.SupervisorNamespace, env.SupervisorUpstreamLDAP.Host, + indentForHeredoc(tc.tlsYAML(secretOrConfigmapResourceName)))) stdOut, stdErr, err := performKubectlApply(t, resourceName, yamlBytes) requireKubectlApplyResult(t, stdOut, stdErr, err, fmt.Sprintf(`ldapidentityprovider.idp.supervisor.%s`, env.APIGroupSuffix), - tc.expectedErrorSnippets, + tc.wantErrorSnippets, "LDAPIdentityProvider", resourceName, ) + + if tc.wantErrorSnippets == nil { + requireTLSValidConditionMessageOnResource(t, + resourceName, + env.SupervisorNamespace, + "LDAPIdentityProvider", + tc.wantTLSValidConditionMessage, + ) + } }) t.Run("apply ActiveDirectory IDP", func(t *testing.T) { resourceName := "test-ad-idp-" + testlib.RandHex(t, 7) + + secretOrConfigmapResourceName := createSecretOrConfigMapFromData(t, + resourceName, + env.SupervisorNamespace, + tc.secretOrConfigmapKind, + tc.secretType, + tc.secretOrConfigmapDataYAML, + ) + yamlBytes := []byte(fmt.Sprintf(activeDirectoryIDPTemplate, - env.APIGroupSuffix, resourceName, env.SupervisorUpstreamLDAP.Host, indentedTLSYAML)) + env.APIGroupSuffix, resourceName, env.SupervisorNamespace, env.SupervisorUpstreamLDAP.Host, + indentForHeredoc(tc.tlsYAML(secretOrConfigmapResourceName)))) stdOut, stdErr, err := performKubectlApply(t, resourceName, yamlBytes) requireKubectlApplyResult(t, stdOut, stdErr, err, fmt.Sprintf(`activedirectoryidentityprovider.idp.supervisor.%s`, env.APIGroupSuffix), - tc.expectedErrorSnippets, + tc.wantErrorSnippets, "ActiveDirectoryIdentityProvider", resourceName, ) + + if tc.wantErrorSnippets == nil { + requireTLSValidConditionMessageOnResource(t, + resourceName, + env.SupervisorNamespace, + "ActiveDirectoryIdentityProvider", + tc.wantTLSValidConditionMessage, + ) + } }) t.Run("apply GitHub IDP", func(t *testing.T) { - // GitHub is nested deeper - indentedTLSYAMLForGitHub := strings.ReplaceAll(indentedTLSYAML, "\n", "\n ") - resourceName := "test-github-idp-" + testlib.RandHex(t, 7) + + secretOrConfigmapResourceName := createSecretOrConfigMapFromData(t, + resourceName, + env.SupervisorNamespace, + tc.secretOrConfigmapKind, + tc.secretType, + tc.secretOrConfigmapDataYAML, + ) + + // GitHub is nested deeper. + indentedTLSYAMLForGitHub := indentForHeredoc(indentForHeredoc(tc.tlsYAML(secretOrConfigmapResourceName))) + yamlBytes := []byte(fmt.Sprintf(githubIDPTemplate, - env.APIGroupSuffix, resourceName, indentedTLSYAMLForGitHub)) + env.APIGroupSuffix, resourceName, env.SupervisorNamespace, indentedTLSYAMLForGitHub)) stdOut, stdErr, err := performKubectlApply(t, resourceName, yamlBytes) requireKubectlApplyResult(t, stdOut, stdErr, err, fmt.Sprintf(`githubidentityprovider.idp.supervisor.%s`, env.APIGroupSuffix), - tc.expectedGitHubErrorSnippets, + tc.wantGitHubErrorSnippets, "GitHubIdentityProvider", resourceName, ) + + if tc.wantGitHubErrorSnippets == nil { + requireTLSValidConditionMessageOnResource(t, + resourceName, + env.SupervisorNamespace, + "GitHubIdentityProvider", + // The tls spec location is different for GitHubIdentityProvider, so adjust the expectation. + strings.Replace(tc.wantTLSValidConditionMessage, "spec.tls is ", "spec.githubAPI.tls is ", 1), + ) + } }) }) } From 4eb9a09385303e45df2d04a752556e1d8c4bcacd Mon Sep 17 00:00:00 2001 From: Ryan Richard Date: Sat, 3 Aug 2024 17:26:42 -0700 Subject: [PATCH 92/99] test more condition message cases in concierge_tls_spec_test.go and supervisor_tls_spec_test.go --- test/integration/concierge_tls_spec_test.go | 290 +++++++++++++++++- test/integration/supervisor_tls_spec_test.go | 301 ++++++++++++++++++- 2 files changed, 565 insertions(+), 26 deletions(-) diff --git a/test/integration/concierge_tls_spec_test.go b/test/integration/concierge_tls_spec_test.go index 2aaefff1b..479ad9ba2 100644 --- a/test/integration/concierge_tls_spec_test.go +++ b/test/integration/concierge_tls_spec_test.go @@ -5,6 +5,7 @@ package integration import ( "bytes" "context" + "encoding/base64" "fmt" "os" "os/exec" @@ -63,7 +64,7 @@ func TestTLSSpecValidationConcierge_Parallel(t *testing.T) { secretOrConfigmapDataYAML string wantErrorSnippets []string - wantTLSValidConditionMessage string + wantTLSValidConditionMessage func(namespace string, secretOrConfigmapName string) string }{ { name: "should disallow certificate authority data source with missing name", @@ -153,6 +154,243 @@ func TestTLSSpecValidationConcierge_Parallel(t *testing.T) { }, wantErrorSnippets: []string{`The %s "%s" is invalid: spec.tls.certificateAuthorityDataSource.kind: Unsupported value: "sorcery": supported values: "Secret", "ConfigMap"`}, }, + { + name: "should get error condition when using both fields of the tls spec", + tlsYAML: func(secretOrConfigmapName string) string { + return here.Doc(` + tls: + certificateAuthorityData: "some CA data" + certificateAuthorityDataSource: + kind: ConfigMap + name: foo + key: bar + `) + }, + wantErrorSnippets: nil, + wantTLSValidConditionMessage: func(namespace string, secretOrConfigmapName string) string { + return "spec.tls is invalid: both tls.certificateAuthorityDataSource and tls.certificateAuthorityData provided" + }, + }, + { + name: "should get error condition when certificateAuthorityData is not base64 data", + tlsYAML: func(secretOrConfigmapName string) string { + return here.Doc(` + tls: + certificateAuthorityData: "this is not base64 encoded" + `) + }, + wantErrorSnippets: nil, + wantTLSValidConditionMessage: func(namespace string, secretOrConfigmapName string) string { + return `spec.tls.certificateAuthorityData is invalid: illegal base64 data at input byte 4` + }, + }, + { + name: "should get error condition when certificateAuthorityData does not contain PEM data", + tlsYAML: func(secretOrConfigmapName string) string { + return here.Docf(` + tls: + certificateAuthorityData: "%s" + `, base64.StdEncoding.EncodeToString([]byte("this is not PEM data"))) + }, + wantErrorSnippets: nil, + wantTLSValidConditionMessage: func(namespace string, secretOrConfigmapName string) string { + return `spec.tls.certificateAuthorityData is invalid: no base64-encoded PEM certificates found in 28 bytes of data (PEM certificates must begin with "-----BEGIN CERTIFICATE-----")` + }, + }, + { + name: "should get error condition when using a ConfigMap source and the ConfigMap does not exist", + tlsYAML: func(secretOrConfigmapName string) string { + return here.Doc(` + tls: + certificateAuthorityDataSource: + kind: ConfigMap + name: this-cm-does-not-exist + key: bar + `) + }, + wantErrorSnippets: nil, + wantTLSValidConditionMessage: func(namespace string, secretOrConfigmapName string) string { + return fmt.Sprintf( + `spec.tls.certificateAuthorityDataSource is invalid: failed to get configmap "%s/this-cm-does-not-exist": configmap "this-cm-does-not-exist" not found`, + namespace) + }, + }, + { + name: "should get error condition when using a Secret source and the Secret does not exist", + tlsYAML: func(secretOrConfigmapName string) string { + return here.Doc(` + tls: + certificateAuthorityDataSource: + kind: Secret + name: this-secret-does-not-exist + key: bar + `) + }, + wantErrorSnippets: nil, + wantTLSValidConditionMessage: func(namespace string, secretOrConfigmapName string) string { + return fmt.Sprintf( + `spec.tls.certificateAuthorityDataSource is invalid: failed to get secret "%s/this-secret-does-not-exist": secret "this-secret-does-not-exist" not found`, + namespace) + }, + }, + { + name: "should get error condition when using a Secret source and the Secret is the wrong type", + secretOrConfigmapKind: "Secret", + secretType: "wrong-type", + secretOrConfigmapDataYAML: here.Doc(` + bar: "does not matter for this test" + `), + tlsYAML: func(secretOrConfigmapName string) string { + return here.Docf(` + tls: + certificateAuthorityDataSource: + kind: Secret + name: %s + key: bar + `, secretOrConfigmapName) + }, + wantErrorSnippets: nil, + wantTLSValidConditionMessage: func(namespace string, secretOrConfigmapName string) string { + return fmt.Sprintf( + `spec.tls.certificateAuthorityDataSource is invalid: secret "%s/%s" of type "wrong-type" cannot be used as a certificate authority data source`, + namespace, secretOrConfigmapName) + }, + }, + { + name: "should get error condition when using a Secret source and the key does not exist", + secretOrConfigmapKind: "Secret", + secretType: string(corev1.SecretTypeOpaque), + secretOrConfigmapDataYAML: here.Doc(` + foo: "foo is the wrong key" + `), + tlsYAML: func(secretOrConfigmapName string) string { + return here.Docf(` + tls: + certificateAuthorityDataSource: + kind: Secret + name: %s + key: bar + `, secretOrConfigmapName) + }, + wantErrorSnippets: nil, + wantTLSValidConditionMessage: func(namespace string, secretOrConfigmapName string) string { + return fmt.Sprintf( + `spec.tls.certificateAuthorityDataSource is invalid: key "bar" not found in secret "%s/%s"`, + namespace, secretOrConfigmapName) + }, + }, + { + name: "should get error condition when using a ConfigMap source and the key does not exist", + secretOrConfigmapKind: "ConfigMap", + secretOrConfigmapDataYAML: here.Doc(` + foo: "foo is the wrong key" + `), + tlsYAML: func(secretOrConfigmapName string) string { + return here.Docf(` + tls: + certificateAuthorityDataSource: + kind: ConfigMap + name: %s + key: bar + `, secretOrConfigmapName) + }, + wantErrorSnippets: nil, + wantTLSValidConditionMessage: func(namespace string, secretOrConfigmapName string) string { + return fmt.Sprintf( + `spec.tls.certificateAuthorityDataSource is invalid: key "bar" not found in configmap "%s/%s"`, + namespace, secretOrConfigmapName) + }, + }, + { + name: "should get error condition when using a Secret source and the key has an empty value", + secretOrConfigmapKind: "Secret", + secretType: string(corev1.SecretTypeOpaque), + secretOrConfigmapDataYAML: here.Doc(` + bar: "" + `), + tlsYAML: func(secretOrConfigmapName string) string { + return here.Docf(` + tls: + certificateAuthorityDataSource: + kind: Secret + name: %s + key: bar + `, secretOrConfigmapName) + }, + wantErrorSnippets: nil, + wantTLSValidConditionMessage: func(namespace string, secretOrConfigmapName string) string { + return fmt.Sprintf( + `spec.tls.certificateAuthorityDataSource is invalid: key "bar" has empty value in secret "%s/%s"`, + namespace, secretOrConfigmapName) + }, + }, + { + name: "should get error condition when using a ConfigMap source and the key has an empty value", + secretOrConfigmapKind: "ConfigMap", + secretOrConfigmapDataYAML: here.Doc(` + bar: "" + `), + tlsYAML: func(secretOrConfigmapName string) string { + return here.Docf(` + tls: + certificateAuthorityDataSource: + kind: ConfigMap + name: %s + key: bar + `, secretOrConfigmapName) + }, + wantErrorSnippets: nil, + wantTLSValidConditionMessage: func(namespace string, secretOrConfigmapName string) string { + return fmt.Sprintf( + `spec.tls.certificateAuthorityDataSource is invalid: key "bar" has empty value in configmap "%s/%s"`, + namespace, secretOrConfigmapName) + }, + }, + { + name: "should get error condition when using a Secret source and the Secret contains data which is not in PEM format", + secretOrConfigmapKind: "Secret", + secretType: string(corev1.SecretTypeOpaque), + secretOrConfigmapDataYAML: here.Doc(` + bar: "this is not a PEM cert" + `), + tlsYAML: func(secretOrConfigmapName string) string { + return here.Docf(` + tls: + certificateAuthorityDataSource: + kind: Secret + name: %s + key: bar + `, secretOrConfigmapName) + }, + wantErrorSnippets: nil, + wantTLSValidConditionMessage: func(namespace string, secretOrConfigmapName string) string { + return fmt.Sprintf( + `spec.tls.certificateAuthorityDataSource is invalid: key "bar" with 22 bytes of data in secret "%s/%s" is not a PEM-encoded certificate (PEM certificates must begin with "-----BEGIN CERTIFICATE-----")`, + namespace, secretOrConfigmapName) + }, + }, + { + name: "should get error condition when using a ConfigMap source and the ConfigMap contains data which is not in PEM format", + secretOrConfigmapKind: "ConfigMap", + secretOrConfigmapDataYAML: here.Doc(` + bar: "this is not a PEM cert" + `), + tlsYAML: func(secretOrConfigmapName string) string { + return here.Docf(` + tls: + certificateAuthorityDataSource: + kind: ConfigMap + name: %s + key: bar + `, secretOrConfigmapName) + }, + wantErrorSnippets: nil, + wantTLSValidConditionMessage: func(namespace string, secretOrConfigmapName string) string { + return fmt.Sprintf( + `spec.tls.certificateAuthorityDataSource is invalid: key "bar" with 22 bytes of data in configmap "%s/%s" is not a PEM-encoded certificate (PEM certificates must begin with "-----BEGIN CERTIFICATE-----")`, + namespace, secretOrConfigmapName) + }, + }, { name: "should create a custom resource passing all validations using a Secret source of type Opaque", secretOrConfigmapKind: "Secret", @@ -170,8 +408,34 @@ func TestTLSSpecValidationConcierge_Parallel(t *testing.T) { key: bar `, secretOrConfigmapName) }, - wantErrorSnippets: nil, - wantTLSValidConditionMessage: `spec.tls is valid: using configured CA bundle`, + wantErrorSnippets: nil, + wantTLSValidConditionMessage: func(namespace string, secretOrConfigmapName string) string { + return `spec.tls is valid: using configured CA bundle` + }, + }, + { + name: "should create a custom resource passing all validations using a Secret source of type tls", + secretOrConfigmapKind: "Secret", + secretType: string(corev1.SecretTypeTLS), + secretOrConfigmapDataYAML: here.Docf(` + tls.crt: foo + tls.key: foo + bar: | + %s + `, indentedCAPEM), + tlsYAML: func(secretOrConfigmapName string) string { + return here.Docf(` + tls: + certificateAuthorityDataSource: + kind: Secret + name: %s + key: bar + `, secretOrConfigmapName) + }, + wantErrorSnippets: nil, + wantTLSValidConditionMessage: func(namespace string, secretOrConfigmapName string) string { + return `spec.tls is valid: using configured CA bundle` + }, }, { name: "should create a custom resource passing all validations using a ConfigMap source", @@ -189,14 +453,18 @@ func TestTLSSpecValidationConcierge_Parallel(t *testing.T) { key: bar `, secretOrConfigmapName) }, - wantErrorSnippets: nil, - wantTLSValidConditionMessage: `spec.tls is valid: using configured CA bundle`, + wantErrorSnippets: nil, + wantTLSValidConditionMessage: func(namespace string, secretOrConfigmapName string) string { + return `spec.tls is valid: using configured CA bundle` + }, }, { - name: "should create a custom resource without any tls spec", - tlsYAML: func(secretOrConfigmapName string) string { return "" }, - wantErrorSnippets: nil, - wantTLSValidConditionMessage: "spec.tls is valid: no TLS configuration provided: using default root CA bundle from container image", + name: "should create a custom resource without any tls spec", + tlsYAML: func(secretOrConfigmapName string) string { return "" }, + wantErrorSnippets: nil, + wantTLSValidConditionMessage: func(namespace string, secretOrConfigmapName string) string { + return "spec.tls is valid: no TLS configuration provided: using default root CA bundle from container image" + }, }, } @@ -232,7 +500,7 @@ func TestTLSSpecValidationConcierge_Parallel(t *testing.T) { resourceName, env.ConciergeNamespace, "WebhookAuthenticator", - tc.wantTLSValidConditionMessage, + tc.wantTLSValidConditionMessage(env.ConciergeNamespace, secretOrConfigmapResourceName), ) } }) @@ -267,7 +535,7 @@ func TestTLSSpecValidationConcierge_Parallel(t *testing.T) { resourceName, env.ConciergeNamespace, "JWTAuthenticator", - tc.wantTLSValidConditionMessage, + tc.wantTLSValidConditionMessage(env.ConciergeNamespace, secretOrConfigmapResourceName), ) } }) diff --git a/test/integration/supervisor_tls_spec_test.go b/test/integration/supervisor_tls_spec_test.go index 687f15d9b..89f76b505 100644 --- a/test/integration/supervisor_tls_spec_test.go +++ b/test/integration/supervisor_tls_spec_test.go @@ -3,6 +3,7 @@ package integration import ( + "encoding/base64" "fmt" "strings" "testing" @@ -99,7 +100,7 @@ func TestTLSSpecValidationSupervisor_Parallel(t *testing.T) { wantErrorSnippets []string wantGitHubErrorSnippets []string - wantTLSValidConditionMessage string + wantTLSValidConditionMessage func(namespace string, secretOrConfigmapName string) string }{ { name: "should disallow certificate authority data source with missing name", @@ -211,6 +212,243 @@ func TestTLSSpecValidationSupervisor_Parallel(t *testing.T) { `spec.githubAPI.tls.certificateAuthorityDataSource.kind: Unsupported value: "sorcery": supported values: "Secret", "ConfigMap"`, }, }, + { + name: "should get error condition when using both fields of the tls spec", + tlsYAML: func(secretOrConfigmapName string) string { + return here.Doc(` + tls: + certificateAuthorityData: "some CA data" + certificateAuthorityDataSource: + kind: ConfigMap + name: foo + key: bar + `) + }, + wantErrorSnippets: nil, + wantTLSValidConditionMessage: func(namespace string, secretOrConfigmapName string) string { + return "spec.tls is invalid: both tls.certificateAuthorityDataSource and tls.certificateAuthorityData provided" + }, + }, + { + name: "should get error condition when certificateAuthorityData is not base64 data", + tlsYAML: func(secretOrConfigmapName string) string { + return here.Doc(` + tls: + certificateAuthorityData: "this is not base64 encoded" + `) + }, + wantErrorSnippets: nil, + wantTLSValidConditionMessage: func(namespace string, secretOrConfigmapName string) string { + return `spec.tls.certificateAuthorityData is invalid: illegal base64 data at input byte 4` + }, + }, + { + name: "should get error condition when certificateAuthorityData does not contain PEM data", + tlsYAML: func(secretOrConfigmapName string) string { + return here.Docf(` + tls: + certificateAuthorityData: "%s" + `, base64.StdEncoding.EncodeToString([]byte("this is not PEM data"))) + }, + wantErrorSnippets: nil, + wantTLSValidConditionMessage: func(namespace string, secretOrConfigmapName string) string { + return `spec.tls.certificateAuthorityData is invalid: no base64-encoded PEM certificates found in 28 bytes of data (PEM certificates must begin with "-----BEGIN CERTIFICATE-----")` + }, + }, + { + name: "should get error condition when using a ConfigMap source and the ConfigMap does not exist", + tlsYAML: func(secretOrConfigmapName string) string { + return here.Doc(` + tls: + certificateAuthorityDataSource: + kind: ConfigMap + name: this-cm-does-not-exist + key: bar + `) + }, + wantErrorSnippets: nil, + wantTLSValidConditionMessage: func(namespace string, secretOrConfigmapName string) string { + return fmt.Sprintf( + `spec.tls.certificateAuthorityDataSource is invalid: failed to get configmap "%s/this-cm-does-not-exist": configmap "this-cm-does-not-exist" not found`, + namespace) + }, + }, + { + name: "should get error condition when using a Secret source and the Secret does not exist", + tlsYAML: func(secretOrConfigmapName string) string { + return here.Doc(` + tls: + certificateAuthorityDataSource: + kind: Secret + name: this-secret-does-not-exist + key: bar + `) + }, + wantErrorSnippets: nil, + wantTLSValidConditionMessage: func(namespace string, secretOrConfigmapName string) string { + return fmt.Sprintf( + `spec.tls.certificateAuthorityDataSource is invalid: failed to get secret "%s/this-secret-does-not-exist": secret "this-secret-does-not-exist" not found`, + namespace) + }, + }, + { + name: "should get error condition when using a Secret source and the Secret is the wrong type", + secretOrConfigmapKind: "Secret", + secretType: "wrong-type", + secretOrConfigmapDataYAML: here.Doc(` + bar: "does not matter for this test" + `), + tlsYAML: func(secretOrConfigmapName string) string { + return here.Docf(` + tls: + certificateAuthorityDataSource: + kind: Secret + name: %s + key: bar + `, secretOrConfigmapName) + }, + wantErrorSnippets: nil, + wantTLSValidConditionMessage: func(namespace string, secretOrConfigmapName string) string { + return fmt.Sprintf( + `spec.tls.certificateAuthorityDataSource is invalid: secret "%s/%s" of type "wrong-type" cannot be used as a certificate authority data source`, + namespace, secretOrConfigmapName) + }, + }, + { + name: "should get error condition when using a Secret source and the key does not exist", + secretOrConfigmapKind: "Secret", + secretType: string(corev1.SecretTypeOpaque), + secretOrConfigmapDataYAML: here.Doc(` + foo: "foo is the wrong key" + `), + tlsYAML: func(secretOrConfigmapName string) string { + return here.Docf(` + tls: + certificateAuthorityDataSource: + kind: Secret + name: %s + key: bar + `, secretOrConfigmapName) + }, + wantErrorSnippets: nil, + wantTLSValidConditionMessage: func(namespace string, secretOrConfigmapName string) string { + return fmt.Sprintf( + `spec.tls.certificateAuthorityDataSource is invalid: key "bar" not found in secret "%s/%s"`, + namespace, secretOrConfigmapName) + }, + }, + { + name: "should get error condition when using a ConfigMap source and the key does not exist", + secretOrConfigmapKind: "ConfigMap", + secretOrConfigmapDataYAML: here.Doc(` + foo: "foo is the wrong key" + `), + tlsYAML: func(secretOrConfigmapName string) string { + return here.Docf(` + tls: + certificateAuthorityDataSource: + kind: ConfigMap + name: %s + key: bar + `, secretOrConfigmapName) + }, + wantErrorSnippets: nil, + wantTLSValidConditionMessage: func(namespace string, secretOrConfigmapName string) string { + return fmt.Sprintf( + `spec.tls.certificateAuthorityDataSource is invalid: key "bar" not found in configmap "%s/%s"`, + namespace, secretOrConfigmapName) + }, + }, + { + name: "should get error condition when using a Secret source and the key has an empty value", + secretOrConfigmapKind: "Secret", + secretType: string(corev1.SecretTypeOpaque), + secretOrConfigmapDataYAML: here.Doc(` + bar: "" + `), + tlsYAML: func(secretOrConfigmapName string) string { + return here.Docf(` + tls: + certificateAuthorityDataSource: + kind: Secret + name: %s + key: bar + `, secretOrConfigmapName) + }, + wantErrorSnippets: nil, + wantTLSValidConditionMessage: func(namespace string, secretOrConfigmapName string) string { + return fmt.Sprintf( + `spec.tls.certificateAuthorityDataSource is invalid: key "bar" has empty value in secret "%s/%s"`, + namespace, secretOrConfigmapName) + }, + }, + { + name: "should get error condition when using a ConfigMap source and the key has an empty value", + secretOrConfigmapKind: "ConfigMap", + secretOrConfigmapDataYAML: here.Doc(` + bar: "" + `), + tlsYAML: func(secretOrConfigmapName string) string { + return here.Docf(` + tls: + certificateAuthorityDataSource: + kind: ConfigMap + name: %s + key: bar + `, secretOrConfigmapName) + }, + wantErrorSnippets: nil, + wantTLSValidConditionMessage: func(namespace string, secretOrConfigmapName string) string { + return fmt.Sprintf( + `spec.tls.certificateAuthorityDataSource is invalid: key "bar" has empty value in configmap "%s/%s"`, + namespace, secretOrConfigmapName) + }, + }, + { + name: "should get error condition when using a Secret source and the Secret contains data which is not in PEM format", + secretOrConfigmapKind: "Secret", + secretType: string(corev1.SecretTypeOpaque), + secretOrConfigmapDataYAML: here.Doc(` + bar: "this is not a PEM cert" + `), + tlsYAML: func(secretOrConfigmapName string) string { + return here.Docf(` + tls: + certificateAuthorityDataSource: + kind: Secret + name: %s + key: bar + `, secretOrConfigmapName) + }, + wantErrorSnippets: nil, + wantTLSValidConditionMessage: func(namespace string, secretOrConfigmapName string) string { + return fmt.Sprintf( + `spec.tls.certificateAuthorityDataSource is invalid: key "bar" with 22 bytes of data in secret "%s/%s" is not a PEM-encoded certificate (PEM certificates must begin with "-----BEGIN CERTIFICATE-----")`, + namespace, secretOrConfigmapName) + }, + }, + { + name: "should get error condition when using a ConfigMap source and the ConfigMap contains data which is not in PEM format", + secretOrConfigmapKind: "ConfigMap", + secretOrConfigmapDataYAML: here.Doc(` + bar: "this is not a PEM cert" + `), + tlsYAML: func(secretOrConfigmapName string) string { + return here.Docf(` + tls: + certificateAuthorityDataSource: + kind: ConfigMap + name: %s + key: bar + `, secretOrConfigmapName) + }, + wantErrorSnippets: nil, + wantTLSValidConditionMessage: func(namespace string, secretOrConfigmapName string) string { + return fmt.Sprintf( + `spec.tls.certificateAuthorityDataSource is invalid: key "bar" with 22 bytes of data in configmap "%s/%s" is not a PEM-encoded certificate (PEM certificates must begin with "-----BEGIN CERTIFICATE-----")`, + namespace, secretOrConfigmapName) + }, + }, { name: "should create a custom resource passing all validations using a Secret source of type Opaque", secretOrConfigmapKind: "Secret", @@ -228,9 +466,36 @@ func TestTLSSpecValidationSupervisor_Parallel(t *testing.T) { key: bar `, secretOrConfigmapName) }, - wantErrorSnippets: nil, - wantGitHubErrorSnippets: nil, - wantTLSValidConditionMessage: "spec.tls is valid: using configured CA bundle", + wantErrorSnippets: nil, + wantGitHubErrorSnippets: nil, + wantTLSValidConditionMessage: func(namespace string, secretOrConfigmapName string) string { + return "spec.tls is valid: using configured CA bundle" + }, + }, + { + name: "should create a custom resource passing all validations using a Secret source of type tls", + secretOrConfigmapKind: "Secret", + secretType: string(corev1.SecretTypeTLS), + secretOrConfigmapDataYAML: here.Docf(` + tls.crt: foo + tls.key: foo + bar: | + %s + `, indentedCAPEM), + tlsYAML: func(secretOrConfigmapName string) string { + return here.Docf(` + tls: + certificateAuthorityDataSource: + kind: Secret + name: %s + key: bar + `, secretOrConfigmapName) + }, + wantErrorSnippets: nil, + wantGitHubErrorSnippets: nil, + wantTLSValidConditionMessage: func(namespace string, secretOrConfigmapName string) string { + return "spec.tls is valid: using configured CA bundle" + }, }, { name: "should create a custom resource passing all validations using a ConfigMap source", @@ -248,15 +513,19 @@ func TestTLSSpecValidationSupervisor_Parallel(t *testing.T) { key: bar `, secretOrConfigmapName) }, - wantErrorSnippets: nil, - wantTLSValidConditionMessage: `spec.tls is valid: using configured CA bundle`, + wantErrorSnippets: nil, + wantTLSValidConditionMessage: func(namespace string, secretOrConfigmapName string) string { + return `spec.tls is valid: using configured CA bundle` + }, }, { - name: "should create a custom resource without any tls spec", - tlsYAML: func(secretOrConfigmapName string) string { return "" }, - wantErrorSnippets: nil, - wantGitHubErrorSnippets: nil, - wantTLSValidConditionMessage: "spec.tls is valid: no TLS configuration provided: using default root CA bundle from container image", + name: "should create a custom resource without any tls spec", + tlsYAML: func(secretOrConfigmapName string) string { return "" }, + wantErrorSnippets: nil, + wantGitHubErrorSnippets: nil, + wantTLSValidConditionMessage: func(namespace string, secretOrConfigmapName string) string { + return "spec.tls is valid: no TLS configuration provided: using default root CA bundle from container image" + }, }, } @@ -292,7 +561,7 @@ func TestTLSSpecValidationSupervisor_Parallel(t *testing.T) { resourceName, env.SupervisorNamespace, "OIDCIdentityProvider", - tc.wantTLSValidConditionMessage, + tc.wantTLSValidConditionMessage(env.SupervisorNamespace, secretOrConfigmapResourceName), ) } }) @@ -325,7 +594,7 @@ func TestTLSSpecValidationSupervisor_Parallel(t *testing.T) { resourceName, env.SupervisorNamespace, "LDAPIdentityProvider", - tc.wantTLSValidConditionMessage, + tc.wantTLSValidConditionMessage(env.SupervisorNamespace, secretOrConfigmapResourceName), ) } }) @@ -358,7 +627,7 @@ func TestTLSSpecValidationSupervisor_Parallel(t *testing.T) { resourceName, env.SupervisorNamespace, "ActiveDirectoryIdentityProvider", - tc.wantTLSValidConditionMessage, + tc.wantTLSValidConditionMessage(env.SupervisorNamespace, secretOrConfigmapResourceName), ) } }) @@ -394,7 +663,9 @@ func TestTLSSpecValidationSupervisor_Parallel(t *testing.T) { env.SupervisorNamespace, "GitHubIdentityProvider", // The tls spec location is different for GitHubIdentityProvider, so adjust the expectation. - strings.Replace(tc.wantTLSValidConditionMessage, "spec.tls is ", "spec.githubAPI.tls is ", 1), + strings.Replace( + tc.wantTLSValidConditionMessage(env.SupervisorNamespace, secretOrConfigmapResourceName), + "spec.tls", "spec.githubAPI.tls", 1), ) } }) From 59c2295dfd617ddb3882c0d1936352d50627f388 Mon Sep 17 00:00:00 2001 From: Ryan Richard Date: Sat, 3 Aug 2024 17:37:34 -0700 Subject: [PATCH 93/99] improve api docs for TLSSpec in authenticator and IDP specs Signed-off-by: Ashish Amarnath Co-authored-by: Ashish Amarnath --- .../authentication/v1alpha1/types_tls.go.tmpl | 18 ++++++++++++++++-- apis/supervisor/idp/v1alpha1/types_tls.go.tmpl | 18 ++++++++++++++++-- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/apis/concierge/authentication/v1alpha1/types_tls.go.tmpl b/apis/concierge/authentication/v1alpha1/types_tls.go.tmpl index db9f4ceff..85ea7a94c 100644 --- a/apis/concierge/authentication/v1alpha1/types_tls.go.tmpl +++ b/apis/concierge/authentication/v1alpha1/types_tls.go.tmpl @@ -3,12 +3,26 @@ package v1alpha1 +// CertificateAuthorityDataSourceKind enumerates the sources for CA Bundles. +// +// +kubebuilder:validation:Enum=Secret;ConfigMap +type CertificateAuthorityDataSourceKind string + +const ( + // CertificateAuthorityDataSourceKindConfigMap uses a Kubernetes configmap to source CA Bundles. + CertificateAuthorityDataSourceKindConfigMap = CertificateAuthorityDataSourceKind("ConfigMap") + + // CertificateAuthorityDataSourceKindSecret uses a Kubernetes secret to source CA Bundles. + // Secrets used to source CA Bundles must be of type kubernetes.io/tls or Opaque. + CertificateAuthorityDataSourceKindSecret = CertificateAuthorityDataSourceKind("Secret") +) + // CertificateAuthorityDataSourceSpec provides a source for CA bundle used for client-side TLS verification. type CertificateAuthorityDataSourceSpec struct { // Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - // Secrets must be of type kubernetes.io/tls or Opaque. // Allowed values are "Secret" or "ConfigMap". - // +kubebuilder:validation:Enum=Secret;ConfigMap + // "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + // "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. Kind string `json:"kind"` // Name is the resource name of the secret or configmap from which to read the CA bundle. // The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. diff --git a/apis/supervisor/idp/v1alpha1/types_tls.go.tmpl b/apis/supervisor/idp/v1alpha1/types_tls.go.tmpl index 70f1c71cc..8c28d7505 100644 --- a/apis/supervisor/idp/v1alpha1/types_tls.go.tmpl +++ b/apis/supervisor/idp/v1alpha1/types_tls.go.tmpl @@ -3,12 +3,26 @@ package v1alpha1 +// CertificateAuthorityDataSourceKind enumerates the sources for CA Bundles. +// +// +kubebuilder:validation:Enum=Secret;ConfigMap +type CertificateAuthorityDataSourceKind string + +const ( + // CertificateAuthorityDataSourceKindConfigMap uses a Kubernetes configmap to source CA Bundles. + CertificateAuthorityDataSourceKindConfigMap = CertificateAuthorityDataSourceKind("ConfigMap") + + // CertificateAuthorityDataSourceKindSecret uses a Kubernetes secret to source CA Bundles. + // Secrets used to source CA Bundles must be of type kubernetes.io/tls or Opaque. + CertificateAuthorityDataSourceKindSecret = CertificateAuthorityDataSourceKind("Secret") +) + // CertificateAuthorityDataSourceSpec provides a source for CA bundle used for client-side TLS verification. type CertificateAuthorityDataSourceSpec struct { // Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - // Secrets must be of type kubernetes.io/tls or Opaque. // Allowed values are "Secret" or "ConfigMap". - // +kubebuilder:validation:Enum=Secret;ConfigMap + // "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + // "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. Kind string `json:"kind"` // Name is the resource name of the secret or configmap from which to read the CA bundle. // The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. From d4ac69d88e648a0ebbbfc6e30221362db3601bbd Mon Sep 17 00:00:00 2001 From: Ryan Richard Date: Sat, 3 Aug 2024 17:41:08 -0700 Subject: [PATCH 94/99] run codegen for changes in previous commit --- ...ncierge.pinniped.dev_jwtauthenticators.yaml | 6 ++---- ...rge.pinniped.dev_webhookauthenticators.yaml | 6 ++---- ...d.dev_activedirectoryidentityproviders.yaml | 6 ++---- ...r.pinniped.dev_githubidentityproviders.yaml | 6 ++---- ...sor.pinniped.dev_ldapidentityproviders.yaml | 6 ++---- ...sor.pinniped.dev_oidcidentityproviders.yaml | 6 ++---- generated/1.24/README.adoc | 10 ++++++++-- .../authentication/v1alpha1/types_tls.go | 18 ++++++++++++++++-- .../apis/supervisor/idp/v1alpha1/types_tls.go | 18 ++++++++++++++++-- ...ncierge.pinniped.dev_jwtauthenticators.yaml | 6 ++---- ...rge.pinniped.dev_webhookauthenticators.yaml | 6 ++---- ...d.dev_activedirectoryidentityproviders.yaml | 6 ++---- ...r.pinniped.dev_githubidentityproviders.yaml | 6 ++---- ...sor.pinniped.dev_ldapidentityproviders.yaml | 6 ++---- ...sor.pinniped.dev_oidcidentityproviders.yaml | 6 ++---- generated/1.25/README.adoc | 10 ++++++++-- .../authentication/v1alpha1/types_tls.go | 18 ++++++++++++++++-- .../apis/supervisor/idp/v1alpha1/types_tls.go | 18 ++++++++++++++++-- ...ncierge.pinniped.dev_jwtauthenticators.yaml | 6 ++---- ...rge.pinniped.dev_webhookauthenticators.yaml | 6 ++---- ...d.dev_activedirectoryidentityproviders.yaml | 6 ++---- ...r.pinniped.dev_githubidentityproviders.yaml | 6 ++---- ...sor.pinniped.dev_ldapidentityproviders.yaml | 6 ++---- ...sor.pinniped.dev_oidcidentityproviders.yaml | 6 ++---- generated/1.26/README.adoc | 10 ++++++++-- .../authentication/v1alpha1/types_tls.go | 18 ++++++++++++++++-- .../apis/supervisor/idp/v1alpha1/types_tls.go | 18 ++++++++++++++++-- ...ncierge.pinniped.dev_jwtauthenticators.yaml | 6 ++---- ...rge.pinniped.dev_webhookauthenticators.yaml | 6 ++---- ...d.dev_activedirectoryidentityproviders.yaml | 6 ++---- ...r.pinniped.dev_githubidentityproviders.yaml | 6 ++---- ...sor.pinniped.dev_ldapidentityproviders.yaml | 6 ++---- ...sor.pinniped.dev_oidcidentityproviders.yaml | 6 ++---- generated/1.27/README.adoc | 10 ++++++++-- .../authentication/v1alpha1/types_tls.go | 18 ++++++++++++++++-- .../apis/supervisor/idp/v1alpha1/types_tls.go | 18 ++++++++++++++++-- ...ncierge.pinniped.dev_jwtauthenticators.yaml | 6 ++---- ...rge.pinniped.dev_webhookauthenticators.yaml | 6 ++---- ...d.dev_activedirectoryidentityproviders.yaml | 6 ++---- ...r.pinniped.dev_githubidentityproviders.yaml | 6 ++---- ...sor.pinniped.dev_ldapidentityproviders.yaml | 6 ++---- ...sor.pinniped.dev_oidcidentityproviders.yaml | 6 ++---- generated/1.28/README.adoc | 10 ++++++++-- .../authentication/v1alpha1/types_tls.go | 18 ++++++++++++++++-- .../apis/supervisor/idp/v1alpha1/types_tls.go | 18 ++++++++++++++++-- ...ncierge.pinniped.dev_jwtauthenticators.yaml | 6 ++---- ...rge.pinniped.dev_webhookauthenticators.yaml | 6 ++---- ...d.dev_activedirectoryidentityproviders.yaml | 6 ++---- ...r.pinniped.dev_githubidentityproviders.yaml | 6 ++---- ...sor.pinniped.dev_ldapidentityproviders.yaml | 6 ++---- ...sor.pinniped.dev_oidcidentityproviders.yaml | 6 ++---- generated/1.29/README.adoc | 10 ++++++++-- .../authentication/v1alpha1/types_tls.go | 18 ++++++++++++++++-- .../apis/supervisor/idp/v1alpha1/types_tls.go | 18 ++++++++++++++++-- ...ncierge.pinniped.dev_jwtauthenticators.yaml | 6 ++---- ...rge.pinniped.dev_webhookauthenticators.yaml | 6 ++---- ...d.dev_activedirectoryidentityproviders.yaml | 6 ++---- ...r.pinniped.dev_githubidentityproviders.yaml | 6 ++---- ...sor.pinniped.dev_ldapidentityproviders.yaml | 6 ++---- ...sor.pinniped.dev_oidcidentityproviders.yaml | 6 ++---- generated/1.30/README.adoc | 10 ++++++++-- .../authentication/v1alpha1/types_tls.go | 18 ++++++++++++++++-- .../apis/supervisor/idp/v1alpha1/types_tls.go | 18 ++++++++++++++++-- ...ncierge.pinniped.dev_jwtauthenticators.yaml | 6 ++---- ...rge.pinniped.dev_webhookauthenticators.yaml | 6 ++---- ...d.dev_activedirectoryidentityproviders.yaml | 6 ++---- ...r.pinniped.dev_githubidentityproviders.yaml | 6 ++---- ...sor.pinniped.dev_ldapidentityproviders.yaml | 6 ++---- ...sor.pinniped.dev_oidcidentityproviders.yaml | 6 ++---- generated/latest/README.adoc | 10 ++++++++-- .../authentication/v1alpha1/types_tls.go | 18 ++++++++++++++++-- .../apis/supervisor/idp/v1alpha1/types_tls.go | 18 ++++++++++++++++-- 72 files changed, 416 insertions(+), 240 deletions(-) diff --git a/deploy/concierge/authentication.concierge.pinniped.dev_jwtauthenticators.yaml b/deploy/concierge/authentication.concierge.pinniped.dev_jwtauthenticators.yaml index 5366768a8..60f872205 100644 --- a/deploy/concierge/authentication.concierge.pinniped.dev_jwtauthenticators.yaml +++ b/deploy/concierge/authentication.concierge.pinniped.dev_jwtauthenticators.yaml @@ -110,11 +110,9 @@ spec: kind: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - Secrets must be of type kubernetes.io/tls or Opaque. Allowed values are "Secret" or "ConfigMap". - enum: - - Secret - - ConfigMap + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. type: string name: description: |- diff --git a/deploy/concierge/authentication.concierge.pinniped.dev_webhookauthenticators.yaml b/deploy/concierge/authentication.concierge.pinniped.dev_webhookauthenticators.yaml index 3f4f32dc9..8e5b7d601 100644 --- a/deploy/concierge/authentication.concierge.pinniped.dev_webhookauthenticators.yaml +++ b/deploy/concierge/authentication.concierge.pinniped.dev_webhookauthenticators.yaml @@ -81,11 +81,9 @@ spec: kind: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - Secrets must be of type kubernetes.io/tls or Opaque. Allowed values are "Secret" or "ConfigMap". - enum: - - Secret - - ConfigMap + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. type: string name: description: |- diff --git a/deploy/supervisor/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml b/deploy/supervisor/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml index 9a5ec7c90..c608d26cb 100644 --- a/deploy/supervisor/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml +++ b/deploy/supervisor/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml @@ -185,11 +185,9 @@ spec: kind: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - Secrets must be of type kubernetes.io/tls or Opaque. Allowed values are "Secret" or "ConfigMap". - enum: - - Secret - - ConfigMap + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. type: string name: description: |- diff --git a/deploy/supervisor/idp.supervisor.pinniped.dev_githubidentityproviders.yaml b/deploy/supervisor/idp.supervisor.pinniped.dev_githubidentityproviders.yaml index 4cf2b9bca..56743cfce 100644 --- a/deploy/supervisor/idp.supervisor.pinniped.dev_githubidentityproviders.yaml +++ b/deploy/supervisor/idp.supervisor.pinniped.dev_githubidentityproviders.yaml @@ -244,11 +244,9 @@ spec: kind: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - Secrets must be of type kubernetes.io/tls or Opaque. Allowed values are "Secret" or "ConfigMap". - enum: - - Secret - - ConfigMap + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. type: string name: description: |- diff --git a/deploy/supervisor/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml b/deploy/supervisor/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml index 08b616aed..e3ccac69a 100644 --- a/deploy/supervisor/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml +++ b/deploy/supervisor/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml @@ -176,11 +176,9 @@ spec: kind: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - Secrets must be of type kubernetes.io/tls or Opaque. Allowed values are "Secret" or "ConfigMap". - enum: - - Secret - - ConfigMap + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. type: string name: description: |- diff --git a/deploy/supervisor/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml b/deploy/supervisor/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml index 75efc06fc..51f44856d 100644 --- a/deploy/supervisor/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml +++ b/deploy/supervisor/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml @@ -226,11 +226,9 @@ spec: kind: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - Secrets must be of type kubernetes.io/tls or Opaque. Allowed values are "Secret" or "ConfigMap". - enum: - - Secret - - ConfigMap + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. type: string name: description: |- diff --git a/generated/1.24/README.adoc b/generated/1.24/README.adoc index 2029b4436..df8f27733 100644 --- a/generated/1.24/README.adoc +++ b/generated/1.24/README.adoc @@ -23,6 +23,8 @@ Package v1alpha1 is the v1alpha1 version of the Pinniped concierge authenticatio + + [id="{anchor_prefix}-go-pinniped-dev-generated-1-24-apis-concierge-authentication-v1alpha1-certificateauthoritydatasourcespec"] ==== CertificateAuthorityDataSourceSpec @@ -37,8 +39,9 @@ CertificateAuthorityDataSourceSpec provides a source for CA bundle used for clie |=== | Field | Description | *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + -Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". + +"ConfigMap" uses a Kubernetes configmap to source CA Bundles. + +"Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + | *`name`* __string__ | Name is the resource name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. + | *`key`* __string__ | Key is the key name within the secret or configmap from which to read the CA bundle. + @@ -1675,6 +1678,8 @@ Optional, when empty this defaults to "objectGUID". + |=== + + [id="{anchor_prefix}-go-pinniped-dev-generated-1-24-apis-supervisor-idp-v1alpha1-certificateauthoritydatasourcespec"] ==== CertificateAuthorityDataSourceSpec @@ -1689,8 +1694,9 @@ CertificateAuthorityDataSourceSpec provides a source for CA bundle used for clie |=== | Field | Description | *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + -Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". + +"ConfigMap" uses a Kubernetes configmap to source CA Bundles. + +"Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + | *`name`* __string__ | Name is the resource name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. + | *`key`* __string__ | Key is the key name within the secret or configmap from which to read the CA bundle. + diff --git a/generated/1.24/apis/concierge/authentication/v1alpha1/types_tls.go b/generated/1.24/apis/concierge/authentication/v1alpha1/types_tls.go index db9f4ceff..85ea7a94c 100644 --- a/generated/1.24/apis/concierge/authentication/v1alpha1/types_tls.go +++ b/generated/1.24/apis/concierge/authentication/v1alpha1/types_tls.go @@ -3,12 +3,26 @@ package v1alpha1 +// CertificateAuthorityDataSourceKind enumerates the sources for CA Bundles. +// +// +kubebuilder:validation:Enum=Secret;ConfigMap +type CertificateAuthorityDataSourceKind string + +const ( + // CertificateAuthorityDataSourceKindConfigMap uses a Kubernetes configmap to source CA Bundles. + CertificateAuthorityDataSourceKindConfigMap = CertificateAuthorityDataSourceKind("ConfigMap") + + // CertificateAuthorityDataSourceKindSecret uses a Kubernetes secret to source CA Bundles. + // Secrets used to source CA Bundles must be of type kubernetes.io/tls or Opaque. + CertificateAuthorityDataSourceKindSecret = CertificateAuthorityDataSourceKind("Secret") +) + // CertificateAuthorityDataSourceSpec provides a source for CA bundle used for client-side TLS verification. type CertificateAuthorityDataSourceSpec struct { // Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - // Secrets must be of type kubernetes.io/tls or Opaque. // Allowed values are "Secret" or "ConfigMap". - // +kubebuilder:validation:Enum=Secret;ConfigMap + // "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + // "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. Kind string `json:"kind"` // Name is the resource name of the secret or configmap from which to read the CA bundle. // The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. diff --git a/generated/1.24/apis/supervisor/idp/v1alpha1/types_tls.go b/generated/1.24/apis/supervisor/idp/v1alpha1/types_tls.go index 70f1c71cc..8c28d7505 100644 --- a/generated/1.24/apis/supervisor/idp/v1alpha1/types_tls.go +++ b/generated/1.24/apis/supervisor/idp/v1alpha1/types_tls.go @@ -3,12 +3,26 @@ package v1alpha1 +// CertificateAuthorityDataSourceKind enumerates the sources for CA Bundles. +// +// +kubebuilder:validation:Enum=Secret;ConfigMap +type CertificateAuthorityDataSourceKind string + +const ( + // CertificateAuthorityDataSourceKindConfigMap uses a Kubernetes configmap to source CA Bundles. + CertificateAuthorityDataSourceKindConfigMap = CertificateAuthorityDataSourceKind("ConfigMap") + + // CertificateAuthorityDataSourceKindSecret uses a Kubernetes secret to source CA Bundles. + // Secrets used to source CA Bundles must be of type kubernetes.io/tls or Opaque. + CertificateAuthorityDataSourceKindSecret = CertificateAuthorityDataSourceKind("Secret") +) + // CertificateAuthorityDataSourceSpec provides a source for CA bundle used for client-side TLS verification. type CertificateAuthorityDataSourceSpec struct { // Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - // Secrets must be of type kubernetes.io/tls or Opaque. // Allowed values are "Secret" or "ConfigMap". - // +kubebuilder:validation:Enum=Secret;ConfigMap + // "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + // "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. Kind string `json:"kind"` // Name is the resource name of the secret or configmap from which to read the CA bundle. // The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. diff --git a/generated/1.24/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml b/generated/1.24/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml index 5366768a8..60f872205 100644 --- a/generated/1.24/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml +++ b/generated/1.24/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml @@ -110,11 +110,9 @@ spec: kind: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - Secrets must be of type kubernetes.io/tls or Opaque. Allowed values are "Secret" or "ConfigMap". - enum: - - Secret - - ConfigMap + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. type: string name: description: |- diff --git a/generated/1.24/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml b/generated/1.24/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml index 3f4f32dc9..8e5b7d601 100644 --- a/generated/1.24/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml +++ b/generated/1.24/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml @@ -81,11 +81,9 @@ spec: kind: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - Secrets must be of type kubernetes.io/tls or Opaque. Allowed values are "Secret" or "ConfigMap". - enum: - - Secret - - ConfigMap + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. type: string name: description: |- diff --git a/generated/1.24/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml b/generated/1.24/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml index 9a5ec7c90..c608d26cb 100644 --- a/generated/1.24/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml +++ b/generated/1.24/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml @@ -185,11 +185,9 @@ spec: kind: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - Secrets must be of type kubernetes.io/tls or Opaque. Allowed values are "Secret" or "ConfigMap". - enum: - - Secret - - ConfigMap + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. type: string name: description: |- diff --git a/generated/1.24/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml b/generated/1.24/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml index 4cf2b9bca..56743cfce 100644 --- a/generated/1.24/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml +++ b/generated/1.24/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml @@ -244,11 +244,9 @@ spec: kind: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - Secrets must be of type kubernetes.io/tls or Opaque. Allowed values are "Secret" or "ConfigMap". - enum: - - Secret - - ConfigMap + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. type: string name: description: |- diff --git a/generated/1.24/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml b/generated/1.24/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml index 08b616aed..e3ccac69a 100644 --- a/generated/1.24/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml +++ b/generated/1.24/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml @@ -176,11 +176,9 @@ spec: kind: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - Secrets must be of type kubernetes.io/tls or Opaque. Allowed values are "Secret" or "ConfigMap". - enum: - - Secret - - ConfigMap + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. type: string name: description: |- diff --git a/generated/1.24/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml b/generated/1.24/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml index 75efc06fc..51f44856d 100644 --- a/generated/1.24/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml +++ b/generated/1.24/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml @@ -226,11 +226,9 @@ spec: kind: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - Secrets must be of type kubernetes.io/tls or Opaque. Allowed values are "Secret" or "ConfigMap". - enum: - - Secret - - ConfigMap + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. type: string name: description: |- diff --git a/generated/1.25/README.adoc b/generated/1.25/README.adoc index 9797af1d9..22c907b91 100644 --- a/generated/1.25/README.adoc +++ b/generated/1.25/README.adoc @@ -23,6 +23,8 @@ Package v1alpha1 is the v1alpha1 version of the Pinniped concierge authenticatio + + [id="{anchor_prefix}-go-pinniped-dev-generated-1-25-apis-concierge-authentication-v1alpha1-certificateauthoritydatasourcespec"] ==== CertificateAuthorityDataSourceSpec @@ -37,8 +39,9 @@ CertificateAuthorityDataSourceSpec provides a source for CA bundle used for clie |=== | Field | Description | *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + -Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". + +"ConfigMap" uses a Kubernetes configmap to source CA Bundles. + +"Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + | *`name`* __string__ | Name is the resource name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. + | *`key`* __string__ | Key is the key name within the secret or configmap from which to read the CA bundle. + @@ -1675,6 +1678,8 @@ Optional, when empty this defaults to "objectGUID". + |=== + + [id="{anchor_prefix}-go-pinniped-dev-generated-1-25-apis-supervisor-idp-v1alpha1-certificateauthoritydatasourcespec"] ==== CertificateAuthorityDataSourceSpec @@ -1689,8 +1694,9 @@ CertificateAuthorityDataSourceSpec provides a source for CA bundle used for clie |=== | Field | Description | *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + -Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". + +"ConfigMap" uses a Kubernetes configmap to source CA Bundles. + +"Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + | *`name`* __string__ | Name is the resource name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. + | *`key`* __string__ | Key is the key name within the secret or configmap from which to read the CA bundle. + diff --git a/generated/1.25/apis/concierge/authentication/v1alpha1/types_tls.go b/generated/1.25/apis/concierge/authentication/v1alpha1/types_tls.go index db9f4ceff..85ea7a94c 100644 --- a/generated/1.25/apis/concierge/authentication/v1alpha1/types_tls.go +++ b/generated/1.25/apis/concierge/authentication/v1alpha1/types_tls.go @@ -3,12 +3,26 @@ package v1alpha1 +// CertificateAuthorityDataSourceKind enumerates the sources for CA Bundles. +// +// +kubebuilder:validation:Enum=Secret;ConfigMap +type CertificateAuthorityDataSourceKind string + +const ( + // CertificateAuthorityDataSourceKindConfigMap uses a Kubernetes configmap to source CA Bundles. + CertificateAuthorityDataSourceKindConfigMap = CertificateAuthorityDataSourceKind("ConfigMap") + + // CertificateAuthorityDataSourceKindSecret uses a Kubernetes secret to source CA Bundles. + // Secrets used to source CA Bundles must be of type kubernetes.io/tls or Opaque. + CertificateAuthorityDataSourceKindSecret = CertificateAuthorityDataSourceKind("Secret") +) + // CertificateAuthorityDataSourceSpec provides a source for CA bundle used for client-side TLS verification. type CertificateAuthorityDataSourceSpec struct { // Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - // Secrets must be of type kubernetes.io/tls or Opaque. // Allowed values are "Secret" or "ConfigMap". - // +kubebuilder:validation:Enum=Secret;ConfigMap + // "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + // "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. Kind string `json:"kind"` // Name is the resource name of the secret or configmap from which to read the CA bundle. // The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. diff --git a/generated/1.25/apis/supervisor/idp/v1alpha1/types_tls.go b/generated/1.25/apis/supervisor/idp/v1alpha1/types_tls.go index 70f1c71cc..8c28d7505 100644 --- a/generated/1.25/apis/supervisor/idp/v1alpha1/types_tls.go +++ b/generated/1.25/apis/supervisor/idp/v1alpha1/types_tls.go @@ -3,12 +3,26 @@ package v1alpha1 +// CertificateAuthorityDataSourceKind enumerates the sources for CA Bundles. +// +// +kubebuilder:validation:Enum=Secret;ConfigMap +type CertificateAuthorityDataSourceKind string + +const ( + // CertificateAuthorityDataSourceKindConfigMap uses a Kubernetes configmap to source CA Bundles. + CertificateAuthorityDataSourceKindConfigMap = CertificateAuthorityDataSourceKind("ConfigMap") + + // CertificateAuthorityDataSourceKindSecret uses a Kubernetes secret to source CA Bundles. + // Secrets used to source CA Bundles must be of type kubernetes.io/tls or Opaque. + CertificateAuthorityDataSourceKindSecret = CertificateAuthorityDataSourceKind("Secret") +) + // CertificateAuthorityDataSourceSpec provides a source for CA bundle used for client-side TLS verification. type CertificateAuthorityDataSourceSpec struct { // Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - // Secrets must be of type kubernetes.io/tls or Opaque. // Allowed values are "Secret" or "ConfigMap". - // +kubebuilder:validation:Enum=Secret;ConfigMap + // "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + // "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. Kind string `json:"kind"` // Name is the resource name of the secret or configmap from which to read the CA bundle. // The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. diff --git a/generated/1.25/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml b/generated/1.25/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml index 5366768a8..60f872205 100644 --- a/generated/1.25/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml +++ b/generated/1.25/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml @@ -110,11 +110,9 @@ spec: kind: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - Secrets must be of type kubernetes.io/tls or Opaque. Allowed values are "Secret" or "ConfigMap". - enum: - - Secret - - ConfigMap + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. type: string name: description: |- diff --git a/generated/1.25/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml b/generated/1.25/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml index 3f4f32dc9..8e5b7d601 100644 --- a/generated/1.25/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml +++ b/generated/1.25/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml @@ -81,11 +81,9 @@ spec: kind: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - Secrets must be of type kubernetes.io/tls or Opaque. Allowed values are "Secret" or "ConfigMap". - enum: - - Secret - - ConfigMap + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. type: string name: description: |- diff --git a/generated/1.25/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml b/generated/1.25/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml index 9a5ec7c90..c608d26cb 100644 --- a/generated/1.25/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml +++ b/generated/1.25/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml @@ -185,11 +185,9 @@ spec: kind: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - Secrets must be of type kubernetes.io/tls or Opaque. Allowed values are "Secret" or "ConfigMap". - enum: - - Secret - - ConfigMap + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. type: string name: description: |- diff --git a/generated/1.25/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml b/generated/1.25/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml index 4cf2b9bca..56743cfce 100644 --- a/generated/1.25/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml +++ b/generated/1.25/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml @@ -244,11 +244,9 @@ spec: kind: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - Secrets must be of type kubernetes.io/tls or Opaque. Allowed values are "Secret" or "ConfigMap". - enum: - - Secret - - ConfigMap + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. type: string name: description: |- diff --git a/generated/1.25/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml b/generated/1.25/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml index 08b616aed..e3ccac69a 100644 --- a/generated/1.25/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml +++ b/generated/1.25/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml @@ -176,11 +176,9 @@ spec: kind: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - Secrets must be of type kubernetes.io/tls or Opaque. Allowed values are "Secret" or "ConfigMap". - enum: - - Secret - - ConfigMap + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. type: string name: description: |- diff --git a/generated/1.25/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml b/generated/1.25/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml index 75efc06fc..51f44856d 100644 --- a/generated/1.25/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml +++ b/generated/1.25/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml @@ -226,11 +226,9 @@ spec: kind: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - Secrets must be of type kubernetes.io/tls or Opaque. Allowed values are "Secret" or "ConfigMap". - enum: - - Secret - - ConfigMap + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. type: string name: description: |- diff --git a/generated/1.26/README.adoc b/generated/1.26/README.adoc index 312c1f4b0..8eb3082bc 100644 --- a/generated/1.26/README.adoc +++ b/generated/1.26/README.adoc @@ -23,6 +23,8 @@ Package v1alpha1 is the v1alpha1 version of the Pinniped concierge authenticatio + + [id="{anchor_prefix}-go-pinniped-dev-generated-1-26-apis-concierge-authentication-v1alpha1-certificateauthoritydatasourcespec"] ==== CertificateAuthorityDataSourceSpec @@ -37,8 +39,9 @@ CertificateAuthorityDataSourceSpec provides a source for CA bundle used for clie |=== | Field | Description | *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + -Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". + +"ConfigMap" uses a Kubernetes configmap to source CA Bundles. + +"Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + | *`name`* __string__ | Name is the resource name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. + | *`key`* __string__ | Key is the key name within the secret or configmap from which to read the CA bundle. + @@ -1675,6 +1678,8 @@ Optional, when empty this defaults to "objectGUID". + |=== + + [id="{anchor_prefix}-go-pinniped-dev-generated-1-26-apis-supervisor-idp-v1alpha1-certificateauthoritydatasourcespec"] ==== CertificateAuthorityDataSourceSpec @@ -1689,8 +1694,9 @@ CertificateAuthorityDataSourceSpec provides a source for CA bundle used for clie |=== | Field | Description | *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + -Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". + +"ConfigMap" uses a Kubernetes configmap to source CA Bundles. + +"Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + | *`name`* __string__ | Name is the resource name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. + | *`key`* __string__ | Key is the key name within the secret or configmap from which to read the CA bundle. + diff --git a/generated/1.26/apis/concierge/authentication/v1alpha1/types_tls.go b/generated/1.26/apis/concierge/authentication/v1alpha1/types_tls.go index db9f4ceff..85ea7a94c 100644 --- a/generated/1.26/apis/concierge/authentication/v1alpha1/types_tls.go +++ b/generated/1.26/apis/concierge/authentication/v1alpha1/types_tls.go @@ -3,12 +3,26 @@ package v1alpha1 +// CertificateAuthorityDataSourceKind enumerates the sources for CA Bundles. +// +// +kubebuilder:validation:Enum=Secret;ConfigMap +type CertificateAuthorityDataSourceKind string + +const ( + // CertificateAuthorityDataSourceKindConfigMap uses a Kubernetes configmap to source CA Bundles. + CertificateAuthorityDataSourceKindConfigMap = CertificateAuthorityDataSourceKind("ConfigMap") + + // CertificateAuthorityDataSourceKindSecret uses a Kubernetes secret to source CA Bundles. + // Secrets used to source CA Bundles must be of type kubernetes.io/tls or Opaque. + CertificateAuthorityDataSourceKindSecret = CertificateAuthorityDataSourceKind("Secret") +) + // CertificateAuthorityDataSourceSpec provides a source for CA bundle used for client-side TLS verification. type CertificateAuthorityDataSourceSpec struct { // Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - // Secrets must be of type kubernetes.io/tls or Opaque. // Allowed values are "Secret" or "ConfigMap". - // +kubebuilder:validation:Enum=Secret;ConfigMap + // "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + // "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. Kind string `json:"kind"` // Name is the resource name of the secret or configmap from which to read the CA bundle. // The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. diff --git a/generated/1.26/apis/supervisor/idp/v1alpha1/types_tls.go b/generated/1.26/apis/supervisor/idp/v1alpha1/types_tls.go index 70f1c71cc..8c28d7505 100644 --- a/generated/1.26/apis/supervisor/idp/v1alpha1/types_tls.go +++ b/generated/1.26/apis/supervisor/idp/v1alpha1/types_tls.go @@ -3,12 +3,26 @@ package v1alpha1 +// CertificateAuthorityDataSourceKind enumerates the sources for CA Bundles. +// +// +kubebuilder:validation:Enum=Secret;ConfigMap +type CertificateAuthorityDataSourceKind string + +const ( + // CertificateAuthorityDataSourceKindConfigMap uses a Kubernetes configmap to source CA Bundles. + CertificateAuthorityDataSourceKindConfigMap = CertificateAuthorityDataSourceKind("ConfigMap") + + // CertificateAuthorityDataSourceKindSecret uses a Kubernetes secret to source CA Bundles. + // Secrets used to source CA Bundles must be of type kubernetes.io/tls or Opaque. + CertificateAuthorityDataSourceKindSecret = CertificateAuthorityDataSourceKind("Secret") +) + // CertificateAuthorityDataSourceSpec provides a source for CA bundle used for client-side TLS verification. type CertificateAuthorityDataSourceSpec struct { // Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - // Secrets must be of type kubernetes.io/tls or Opaque. // Allowed values are "Secret" or "ConfigMap". - // +kubebuilder:validation:Enum=Secret;ConfigMap + // "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + // "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. Kind string `json:"kind"` // Name is the resource name of the secret or configmap from which to read the CA bundle. // The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. diff --git a/generated/1.26/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml b/generated/1.26/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml index 5366768a8..60f872205 100644 --- a/generated/1.26/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml +++ b/generated/1.26/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml @@ -110,11 +110,9 @@ spec: kind: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - Secrets must be of type kubernetes.io/tls or Opaque. Allowed values are "Secret" or "ConfigMap". - enum: - - Secret - - ConfigMap + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. type: string name: description: |- diff --git a/generated/1.26/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml b/generated/1.26/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml index 3f4f32dc9..8e5b7d601 100644 --- a/generated/1.26/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml +++ b/generated/1.26/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml @@ -81,11 +81,9 @@ spec: kind: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - Secrets must be of type kubernetes.io/tls or Opaque. Allowed values are "Secret" or "ConfigMap". - enum: - - Secret - - ConfigMap + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. type: string name: description: |- diff --git a/generated/1.26/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml b/generated/1.26/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml index 9a5ec7c90..c608d26cb 100644 --- a/generated/1.26/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml +++ b/generated/1.26/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml @@ -185,11 +185,9 @@ spec: kind: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - Secrets must be of type kubernetes.io/tls or Opaque. Allowed values are "Secret" or "ConfigMap". - enum: - - Secret - - ConfigMap + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. type: string name: description: |- diff --git a/generated/1.26/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml b/generated/1.26/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml index 4cf2b9bca..56743cfce 100644 --- a/generated/1.26/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml +++ b/generated/1.26/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml @@ -244,11 +244,9 @@ spec: kind: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - Secrets must be of type kubernetes.io/tls or Opaque. Allowed values are "Secret" or "ConfigMap". - enum: - - Secret - - ConfigMap + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. type: string name: description: |- diff --git a/generated/1.26/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml b/generated/1.26/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml index 08b616aed..e3ccac69a 100644 --- a/generated/1.26/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml +++ b/generated/1.26/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml @@ -176,11 +176,9 @@ spec: kind: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - Secrets must be of type kubernetes.io/tls or Opaque. Allowed values are "Secret" or "ConfigMap". - enum: - - Secret - - ConfigMap + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. type: string name: description: |- diff --git a/generated/1.26/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml b/generated/1.26/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml index 75efc06fc..51f44856d 100644 --- a/generated/1.26/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml +++ b/generated/1.26/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml @@ -226,11 +226,9 @@ spec: kind: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - Secrets must be of type kubernetes.io/tls or Opaque. Allowed values are "Secret" or "ConfigMap". - enum: - - Secret - - ConfigMap + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. type: string name: description: |- diff --git a/generated/1.27/README.adoc b/generated/1.27/README.adoc index f973f3223..90d7ab20c 100644 --- a/generated/1.27/README.adoc +++ b/generated/1.27/README.adoc @@ -23,6 +23,8 @@ Package v1alpha1 is the v1alpha1 version of the Pinniped concierge authenticatio + + [id="{anchor_prefix}-go-pinniped-dev-generated-1-27-apis-concierge-authentication-v1alpha1-certificateauthoritydatasourcespec"] ==== CertificateAuthorityDataSourceSpec @@ -37,8 +39,9 @@ CertificateAuthorityDataSourceSpec provides a source for CA bundle used for clie |=== | Field | Description | *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + -Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". + +"ConfigMap" uses a Kubernetes configmap to source CA Bundles. + +"Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + | *`name`* __string__ | Name is the resource name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. + | *`key`* __string__ | Key is the key name within the secret or configmap from which to read the CA bundle. + @@ -1675,6 +1678,8 @@ Optional, when empty this defaults to "objectGUID". + |=== + + [id="{anchor_prefix}-go-pinniped-dev-generated-1-27-apis-supervisor-idp-v1alpha1-certificateauthoritydatasourcespec"] ==== CertificateAuthorityDataSourceSpec @@ -1689,8 +1694,9 @@ CertificateAuthorityDataSourceSpec provides a source for CA bundle used for clie |=== | Field | Description | *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + -Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". + +"ConfigMap" uses a Kubernetes configmap to source CA Bundles. + +"Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + | *`name`* __string__ | Name is the resource name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. + | *`key`* __string__ | Key is the key name within the secret or configmap from which to read the CA bundle. + diff --git a/generated/1.27/apis/concierge/authentication/v1alpha1/types_tls.go b/generated/1.27/apis/concierge/authentication/v1alpha1/types_tls.go index db9f4ceff..85ea7a94c 100644 --- a/generated/1.27/apis/concierge/authentication/v1alpha1/types_tls.go +++ b/generated/1.27/apis/concierge/authentication/v1alpha1/types_tls.go @@ -3,12 +3,26 @@ package v1alpha1 +// CertificateAuthorityDataSourceKind enumerates the sources for CA Bundles. +// +// +kubebuilder:validation:Enum=Secret;ConfigMap +type CertificateAuthorityDataSourceKind string + +const ( + // CertificateAuthorityDataSourceKindConfigMap uses a Kubernetes configmap to source CA Bundles. + CertificateAuthorityDataSourceKindConfigMap = CertificateAuthorityDataSourceKind("ConfigMap") + + // CertificateAuthorityDataSourceKindSecret uses a Kubernetes secret to source CA Bundles. + // Secrets used to source CA Bundles must be of type kubernetes.io/tls or Opaque. + CertificateAuthorityDataSourceKindSecret = CertificateAuthorityDataSourceKind("Secret") +) + // CertificateAuthorityDataSourceSpec provides a source for CA bundle used for client-side TLS verification. type CertificateAuthorityDataSourceSpec struct { // Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - // Secrets must be of type kubernetes.io/tls or Opaque. // Allowed values are "Secret" or "ConfigMap". - // +kubebuilder:validation:Enum=Secret;ConfigMap + // "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + // "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. Kind string `json:"kind"` // Name is the resource name of the secret or configmap from which to read the CA bundle. // The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. diff --git a/generated/1.27/apis/supervisor/idp/v1alpha1/types_tls.go b/generated/1.27/apis/supervisor/idp/v1alpha1/types_tls.go index 70f1c71cc..8c28d7505 100644 --- a/generated/1.27/apis/supervisor/idp/v1alpha1/types_tls.go +++ b/generated/1.27/apis/supervisor/idp/v1alpha1/types_tls.go @@ -3,12 +3,26 @@ package v1alpha1 +// CertificateAuthorityDataSourceKind enumerates the sources for CA Bundles. +// +// +kubebuilder:validation:Enum=Secret;ConfigMap +type CertificateAuthorityDataSourceKind string + +const ( + // CertificateAuthorityDataSourceKindConfigMap uses a Kubernetes configmap to source CA Bundles. + CertificateAuthorityDataSourceKindConfigMap = CertificateAuthorityDataSourceKind("ConfigMap") + + // CertificateAuthorityDataSourceKindSecret uses a Kubernetes secret to source CA Bundles. + // Secrets used to source CA Bundles must be of type kubernetes.io/tls or Opaque. + CertificateAuthorityDataSourceKindSecret = CertificateAuthorityDataSourceKind("Secret") +) + // CertificateAuthorityDataSourceSpec provides a source for CA bundle used for client-side TLS verification. type CertificateAuthorityDataSourceSpec struct { // Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - // Secrets must be of type kubernetes.io/tls or Opaque. // Allowed values are "Secret" or "ConfigMap". - // +kubebuilder:validation:Enum=Secret;ConfigMap + // "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + // "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. Kind string `json:"kind"` // Name is the resource name of the secret or configmap from which to read the CA bundle. // The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. diff --git a/generated/1.27/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml b/generated/1.27/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml index 5366768a8..60f872205 100644 --- a/generated/1.27/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml +++ b/generated/1.27/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml @@ -110,11 +110,9 @@ spec: kind: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - Secrets must be of type kubernetes.io/tls or Opaque. Allowed values are "Secret" or "ConfigMap". - enum: - - Secret - - ConfigMap + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. type: string name: description: |- diff --git a/generated/1.27/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml b/generated/1.27/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml index 3f4f32dc9..8e5b7d601 100644 --- a/generated/1.27/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml +++ b/generated/1.27/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml @@ -81,11 +81,9 @@ spec: kind: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - Secrets must be of type kubernetes.io/tls or Opaque. Allowed values are "Secret" or "ConfigMap". - enum: - - Secret - - ConfigMap + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. type: string name: description: |- diff --git a/generated/1.27/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml b/generated/1.27/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml index 9a5ec7c90..c608d26cb 100644 --- a/generated/1.27/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml +++ b/generated/1.27/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml @@ -185,11 +185,9 @@ spec: kind: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - Secrets must be of type kubernetes.io/tls or Opaque. Allowed values are "Secret" or "ConfigMap". - enum: - - Secret - - ConfigMap + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. type: string name: description: |- diff --git a/generated/1.27/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml b/generated/1.27/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml index 4cf2b9bca..56743cfce 100644 --- a/generated/1.27/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml +++ b/generated/1.27/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml @@ -244,11 +244,9 @@ spec: kind: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - Secrets must be of type kubernetes.io/tls or Opaque. Allowed values are "Secret" or "ConfigMap". - enum: - - Secret - - ConfigMap + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. type: string name: description: |- diff --git a/generated/1.27/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml b/generated/1.27/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml index 08b616aed..e3ccac69a 100644 --- a/generated/1.27/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml +++ b/generated/1.27/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml @@ -176,11 +176,9 @@ spec: kind: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - Secrets must be of type kubernetes.io/tls or Opaque. Allowed values are "Secret" or "ConfigMap". - enum: - - Secret - - ConfigMap + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. type: string name: description: |- diff --git a/generated/1.27/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml b/generated/1.27/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml index 75efc06fc..51f44856d 100644 --- a/generated/1.27/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml +++ b/generated/1.27/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml @@ -226,11 +226,9 @@ spec: kind: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - Secrets must be of type kubernetes.io/tls or Opaque. Allowed values are "Secret" or "ConfigMap". - enum: - - Secret - - ConfigMap + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. type: string name: description: |- diff --git a/generated/1.28/README.adoc b/generated/1.28/README.adoc index 52bf0d702..f47b4fa8d 100644 --- a/generated/1.28/README.adoc +++ b/generated/1.28/README.adoc @@ -23,6 +23,8 @@ Package v1alpha1 is the v1alpha1 version of the Pinniped concierge authenticatio + + [id="{anchor_prefix}-go-pinniped-dev-generated-1-28-apis-concierge-authentication-v1alpha1-certificateauthoritydatasourcespec"] ==== CertificateAuthorityDataSourceSpec @@ -37,8 +39,9 @@ CertificateAuthorityDataSourceSpec provides a source for CA bundle used for clie |=== | Field | Description | *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + -Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". + +"ConfigMap" uses a Kubernetes configmap to source CA Bundles. + +"Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + | *`name`* __string__ | Name is the resource name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. + | *`key`* __string__ | Key is the key name within the secret or configmap from which to read the CA bundle. + @@ -1675,6 +1678,8 @@ Optional, when empty this defaults to "objectGUID". + |=== + + [id="{anchor_prefix}-go-pinniped-dev-generated-1-28-apis-supervisor-idp-v1alpha1-certificateauthoritydatasourcespec"] ==== CertificateAuthorityDataSourceSpec @@ -1689,8 +1694,9 @@ CertificateAuthorityDataSourceSpec provides a source for CA bundle used for clie |=== | Field | Description | *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + -Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". + +"ConfigMap" uses a Kubernetes configmap to source CA Bundles. + +"Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + | *`name`* __string__ | Name is the resource name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. + | *`key`* __string__ | Key is the key name within the secret or configmap from which to read the CA bundle. + diff --git a/generated/1.28/apis/concierge/authentication/v1alpha1/types_tls.go b/generated/1.28/apis/concierge/authentication/v1alpha1/types_tls.go index db9f4ceff..85ea7a94c 100644 --- a/generated/1.28/apis/concierge/authentication/v1alpha1/types_tls.go +++ b/generated/1.28/apis/concierge/authentication/v1alpha1/types_tls.go @@ -3,12 +3,26 @@ package v1alpha1 +// CertificateAuthorityDataSourceKind enumerates the sources for CA Bundles. +// +// +kubebuilder:validation:Enum=Secret;ConfigMap +type CertificateAuthorityDataSourceKind string + +const ( + // CertificateAuthorityDataSourceKindConfigMap uses a Kubernetes configmap to source CA Bundles. + CertificateAuthorityDataSourceKindConfigMap = CertificateAuthorityDataSourceKind("ConfigMap") + + // CertificateAuthorityDataSourceKindSecret uses a Kubernetes secret to source CA Bundles. + // Secrets used to source CA Bundles must be of type kubernetes.io/tls or Opaque. + CertificateAuthorityDataSourceKindSecret = CertificateAuthorityDataSourceKind("Secret") +) + // CertificateAuthorityDataSourceSpec provides a source for CA bundle used for client-side TLS verification. type CertificateAuthorityDataSourceSpec struct { // Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - // Secrets must be of type kubernetes.io/tls or Opaque. // Allowed values are "Secret" or "ConfigMap". - // +kubebuilder:validation:Enum=Secret;ConfigMap + // "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + // "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. Kind string `json:"kind"` // Name is the resource name of the secret or configmap from which to read the CA bundle. // The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. diff --git a/generated/1.28/apis/supervisor/idp/v1alpha1/types_tls.go b/generated/1.28/apis/supervisor/idp/v1alpha1/types_tls.go index 70f1c71cc..8c28d7505 100644 --- a/generated/1.28/apis/supervisor/idp/v1alpha1/types_tls.go +++ b/generated/1.28/apis/supervisor/idp/v1alpha1/types_tls.go @@ -3,12 +3,26 @@ package v1alpha1 +// CertificateAuthorityDataSourceKind enumerates the sources for CA Bundles. +// +// +kubebuilder:validation:Enum=Secret;ConfigMap +type CertificateAuthorityDataSourceKind string + +const ( + // CertificateAuthorityDataSourceKindConfigMap uses a Kubernetes configmap to source CA Bundles. + CertificateAuthorityDataSourceKindConfigMap = CertificateAuthorityDataSourceKind("ConfigMap") + + // CertificateAuthorityDataSourceKindSecret uses a Kubernetes secret to source CA Bundles. + // Secrets used to source CA Bundles must be of type kubernetes.io/tls or Opaque. + CertificateAuthorityDataSourceKindSecret = CertificateAuthorityDataSourceKind("Secret") +) + // CertificateAuthorityDataSourceSpec provides a source for CA bundle used for client-side TLS verification. type CertificateAuthorityDataSourceSpec struct { // Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - // Secrets must be of type kubernetes.io/tls or Opaque. // Allowed values are "Secret" or "ConfigMap". - // +kubebuilder:validation:Enum=Secret;ConfigMap + // "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + // "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. Kind string `json:"kind"` // Name is the resource name of the secret or configmap from which to read the CA bundle. // The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. diff --git a/generated/1.28/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml b/generated/1.28/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml index 5366768a8..60f872205 100644 --- a/generated/1.28/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml +++ b/generated/1.28/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml @@ -110,11 +110,9 @@ spec: kind: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - Secrets must be of type kubernetes.io/tls or Opaque. Allowed values are "Secret" or "ConfigMap". - enum: - - Secret - - ConfigMap + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. type: string name: description: |- diff --git a/generated/1.28/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml b/generated/1.28/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml index 3f4f32dc9..8e5b7d601 100644 --- a/generated/1.28/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml +++ b/generated/1.28/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml @@ -81,11 +81,9 @@ spec: kind: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - Secrets must be of type kubernetes.io/tls or Opaque. Allowed values are "Secret" or "ConfigMap". - enum: - - Secret - - ConfigMap + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. type: string name: description: |- diff --git a/generated/1.28/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml b/generated/1.28/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml index 9a5ec7c90..c608d26cb 100644 --- a/generated/1.28/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml +++ b/generated/1.28/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml @@ -185,11 +185,9 @@ spec: kind: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - Secrets must be of type kubernetes.io/tls or Opaque. Allowed values are "Secret" or "ConfigMap". - enum: - - Secret - - ConfigMap + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. type: string name: description: |- diff --git a/generated/1.28/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml b/generated/1.28/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml index 4cf2b9bca..56743cfce 100644 --- a/generated/1.28/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml +++ b/generated/1.28/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml @@ -244,11 +244,9 @@ spec: kind: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - Secrets must be of type kubernetes.io/tls or Opaque. Allowed values are "Secret" or "ConfigMap". - enum: - - Secret - - ConfigMap + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. type: string name: description: |- diff --git a/generated/1.28/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml b/generated/1.28/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml index 08b616aed..e3ccac69a 100644 --- a/generated/1.28/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml +++ b/generated/1.28/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml @@ -176,11 +176,9 @@ spec: kind: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - Secrets must be of type kubernetes.io/tls or Opaque. Allowed values are "Secret" or "ConfigMap". - enum: - - Secret - - ConfigMap + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. type: string name: description: |- diff --git a/generated/1.28/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml b/generated/1.28/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml index 75efc06fc..51f44856d 100644 --- a/generated/1.28/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml +++ b/generated/1.28/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml @@ -226,11 +226,9 @@ spec: kind: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - Secrets must be of type kubernetes.io/tls or Opaque. Allowed values are "Secret" or "ConfigMap". - enum: - - Secret - - ConfigMap + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. type: string name: description: |- diff --git a/generated/1.29/README.adoc b/generated/1.29/README.adoc index 5350ab237..d60a6642e 100644 --- a/generated/1.29/README.adoc +++ b/generated/1.29/README.adoc @@ -23,6 +23,8 @@ Package v1alpha1 is the v1alpha1 version of the Pinniped concierge authenticatio + + [id="{anchor_prefix}-go-pinniped-dev-generated-1-29-apis-concierge-authentication-v1alpha1-certificateauthoritydatasourcespec"] ==== CertificateAuthorityDataSourceSpec @@ -37,8 +39,9 @@ CertificateAuthorityDataSourceSpec provides a source for CA bundle used for clie |=== | Field | Description | *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + -Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". + +"ConfigMap" uses a Kubernetes configmap to source CA Bundles. + +"Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + | *`name`* __string__ | Name is the resource name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. + | *`key`* __string__ | Key is the key name within the secret or configmap from which to read the CA bundle. + @@ -1675,6 +1678,8 @@ Optional, when empty this defaults to "objectGUID". + |=== + + [id="{anchor_prefix}-go-pinniped-dev-generated-1-29-apis-supervisor-idp-v1alpha1-certificateauthoritydatasourcespec"] ==== CertificateAuthorityDataSourceSpec @@ -1689,8 +1694,9 @@ CertificateAuthorityDataSourceSpec provides a source for CA bundle used for clie |=== | Field | Description | *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + -Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". + +"ConfigMap" uses a Kubernetes configmap to source CA Bundles. + +"Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + | *`name`* __string__ | Name is the resource name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. + | *`key`* __string__ | Key is the key name within the secret or configmap from which to read the CA bundle. + diff --git a/generated/1.29/apis/concierge/authentication/v1alpha1/types_tls.go b/generated/1.29/apis/concierge/authentication/v1alpha1/types_tls.go index db9f4ceff..85ea7a94c 100644 --- a/generated/1.29/apis/concierge/authentication/v1alpha1/types_tls.go +++ b/generated/1.29/apis/concierge/authentication/v1alpha1/types_tls.go @@ -3,12 +3,26 @@ package v1alpha1 +// CertificateAuthorityDataSourceKind enumerates the sources for CA Bundles. +// +// +kubebuilder:validation:Enum=Secret;ConfigMap +type CertificateAuthorityDataSourceKind string + +const ( + // CertificateAuthorityDataSourceKindConfigMap uses a Kubernetes configmap to source CA Bundles. + CertificateAuthorityDataSourceKindConfigMap = CertificateAuthorityDataSourceKind("ConfigMap") + + // CertificateAuthorityDataSourceKindSecret uses a Kubernetes secret to source CA Bundles. + // Secrets used to source CA Bundles must be of type kubernetes.io/tls or Opaque. + CertificateAuthorityDataSourceKindSecret = CertificateAuthorityDataSourceKind("Secret") +) + // CertificateAuthorityDataSourceSpec provides a source for CA bundle used for client-side TLS verification. type CertificateAuthorityDataSourceSpec struct { // Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - // Secrets must be of type kubernetes.io/tls or Opaque. // Allowed values are "Secret" or "ConfigMap". - // +kubebuilder:validation:Enum=Secret;ConfigMap + // "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + // "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. Kind string `json:"kind"` // Name is the resource name of the secret or configmap from which to read the CA bundle. // The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. diff --git a/generated/1.29/apis/supervisor/idp/v1alpha1/types_tls.go b/generated/1.29/apis/supervisor/idp/v1alpha1/types_tls.go index 70f1c71cc..8c28d7505 100644 --- a/generated/1.29/apis/supervisor/idp/v1alpha1/types_tls.go +++ b/generated/1.29/apis/supervisor/idp/v1alpha1/types_tls.go @@ -3,12 +3,26 @@ package v1alpha1 +// CertificateAuthorityDataSourceKind enumerates the sources for CA Bundles. +// +// +kubebuilder:validation:Enum=Secret;ConfigMap +type CertificateAuthorityDataSourceKind string + +const ( + // CertificateAuthorityDataSourceKindConfigMap uses a Kubernetes configmap to source CA Bundles. + CertificateAuthorityDataSourceKindConfigMap = CertificateAuthorityDataSourceKind("ConfigMap") + + // CertificateAuthorityDataSourceKindSecret uses a Kubernetes secret to source CA Bundles. + // Secrets used to source CA Bundles must be of type kubernetes.io/tls or Opaque. + CertificateAuthorityDataSourceKindSecret = CertificateAuthorityDataSourceKind("Secret") +) + // CertificateAuthorityDataSourceSpec provides a source for CA bundle used for client-side TLS verification. type CertificateAuthorityDataSourceSpec struct { // Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - // Secrets must be of type kubernetes.io/tls or Opaque. // Allowed values are "Secret" or "ConfigMap". - // +kubebuilder:validation:Enum=Secret;ConfigMap + // "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + // "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. Kind string `json:"kind"` // Name is the resource name of the secret or configmap from which to read the CA bundle. // The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. diff --git a/generated/1.29/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml b/generated/1.29/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml index 5366768a8..60f872205 100644 --- a/generated/1.29/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml +++ b/generated/1.29/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml @@ -110,11 +110,9 @@ spec: kind: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - Secrets must be of type kubernetes.io/tls or Opaque. Allowed values are "Secret" or "ConfigMap". - enum: - - Secret - - ConfigMap + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. type: string name: description: |- diff --git a/generated/1.29/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml b/generated/1.29/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml index 3f4f32dc9..8e5b7d601 100644 --- a/generated/1.29/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml +++ b/generated/1.29/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml @@ -81,11 +81,9 @@ spec: kind: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - Secrets must be of type kubernetes.io/tls or Opaque. Allowed values are "Secret" or "ConfigMap". - enum: - - Secret - - ConfigMap + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. type: string name: description: |- diff --git a/generated/1.29/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml b/generated/1.29/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml index 9a5ec7c90..c608d26cb 100644 --- a/generated/1.29/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml +++ b/generated/1.29/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml @@ -185,11 +185,9 @@ spec: kind: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - Secrets must be of type kubernetes.io/tls or Opaque. Allowed values are "Secret" or "ConfigMap". - enum: - - Secret - - ConfigMap + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. type: string name: description: |- diff --git a/generated/1.29/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml b/generated/1.29/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml index 4cf2b9bca..56743cfce 100644 --- a/generated/1.29/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml +++ b/generated/1.29/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml @@ -244,11 +244,9 @@ spec: kind: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - Secrets must be of type kubernetes.io/tls or Opaque. Allowed values are "Secret" or "ConfigMap". - enum: - - Secret - - ConfigMap + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. type: string name: description: |- diff --git a/generated/1.29/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml b/generated/1.29/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml index 08b616aed..e3ccac69a 100644 --- a/generated/1.29/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml +++ b/generated/1.29/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml @@ -176,11 +176,9 @@ spec: kind: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - Secrets must be of type kubernetes.io/tls or Opaque. Allowed values are "Secret" or "ConfigMap". - enum: - - Secret - - ConfigMap + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. type: string name: description: |- diff --git a/generated/1.29/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml b/generated/1.29/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml index 75efc06fc..51f44856d 100644 --- a/generated/1.29/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml +++ b/generated/1.29/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml @@ -226,11 +226,9 @@ spec: kind: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - Secrets must be of type kubernetes.io/tls or Opaque. Allowed values are "Secret" or "ConfigMap". - enum: - - Secret - - ConfigMap + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. type: string name: description: |- diff --git a/generated/1.30/README.adoc b/generated/1.30/README.adoc index 23ad2e25b..3d317f0d3 100644 --- a/generated/1.30/README.adoc +++ b/generated/1.30/README.adoc @@ -23,6 +23,8 @@ Package v1alpha1 is the v1alpha1 version of the Pinniped concierge authenticatio + + [id="{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-concierge-authentication-v1alpha1-certificateauthoritydatasourcespec"] ==== CertificateAuthorityDataSourceSpec @@ -37,8 +39,9 @@ CertificateAuthorityDataSourceSpec provides a source for CA bundle used for clie |=== | Field | Description | *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + -Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". + +"ConfigMap" uses a Kubernetes configmap to source CA Bundles. + +"Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + | *`name`* __string__ | Name is the resource name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. + | *`key`* __string__ | Key is the key name within the secret or configmap from which to read the CA bundle. + @@ -1675,6 +1678,8 @@ Optional, when empty this defaults to "objectGUID". + |=== + + [id="{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-supervisor-idp-v1alpha1-certificateauthoritydatasourcespec"] ==== CertificateAuthorityDataSourceSpec @@ -1689,8 +1694,9 @@ CertificateAuthorityDataSourceSpec provides a source for CA bundle used for clie |=== | Field | Description | *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + -Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". + +"ConfigMap" uses a Kubernetes configmap to source CA Bundles. + +"Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + | *`name`* __string__ | Name is the resource name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. + | *`key`* __string__ | Key is the key name within the secret or configmap from which to read the CA bundle. + diff --git a/generated/1.30/apis/concierge/authentication/v1alpha1/types_tls.go b/generated/1.30/apis/concierge/authentication/v1alpha1/types_tls.go index db9f4ceff..85ea7a94c 100644 --- a/generated/1.30/apis/concierge/authentication/v1alpha1/types_tls.go +++ b/generated/1.30/apis/concierge/authentication/v1alpha1/types_tls.go @@ -3,12 +3,26 @@ package v1alpha1 +// CertificateAuthorityDataSourceKind enumerates the sources for CA Bundles. +// +// +kubebuilder:validation:Enum=Secret;ConfigMap +type CertificateAuthorityDataSourceKind string + +const ( + // CertificateAuthorityDataSourceKindConfigMap uses a Kubernetes configmap to source CA Bundles. + CertificateAuthorityDataSourceKindConfigMap = CertificateAuthorityDataSourceKind("ConfigMap") + + // CertificateAuthorityDataSourceKindSecret uses a Kubernetes secret to source CA Bundles. + // Secrets used to source CA Bundles must be of type kubernetes.io/tls or Opaque. + CertificateAuthorityDataSourceKindSecret = CertificateAuthorityDataSourceKind("Secret") +) + // CertificateAuthorityDataSourceSpec provides a source for CA bundle used for client-side TLS verification. type CertificateAuthorityDataSourceSpec struct { // Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - // Secrets must be of type kubernetes.io/tls or Opaque. // Allowed values are "Secret" or "ConfigMap". - // +kubebuilder:validation:Enum=Secret;ConfigMap + // "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + // "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. Kind string `json:"kind"` // Name is the resource name of the secret or configmap from which to read the CA bundle. // The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. diff --git a/generated/1.30/apis/supervisor/idp/v1alpha1/types_tls.go b/generated/1.30/apis/supervisor/idp/v1alpha1/types_tls.go index 70f1c71cc..8c28d7505 100644 --- a/generated/1.30/apis/supervisor/idp/v1alpha1/types_tls.go +++ b/generated/1.30/apis/supervisor/idp/v1alpha1/types_tls.go @@ -3,12 +3,26 @@ package v1alpha1 +// CertificateAuthorityDataSourceKind enumerates the sources for CA Bundles. +// +// +kubebuilder:validation:Enum=Secret;ConfigMap +type CertificateAuthorityDataSourceKind string + +const ( + // CertificateAuthorityDataSourceKindConfigMap uses a Kubernetes configmap to source CA Bundles. + CertificateAuthorityDataSourceKindConfigMap = CertificateAuthorityDataSourceKind("ConfigMap") + + // CertificateAuthorityDataSourceKindSecret uses a Kubernetes secret to source CA Bundles. + // Secrets used to source CA Bundles must be of type kubernetes.io/tls or Opaque. + CertificateAuthorityDataSourceKindSecret = CertificateAuthorityDataSourceKind("Secret") +) + // CertificateAuthorityDataSourceSpec provides a source for CA bundle used for client-side TLS verification. type CertificateAuthorityDataSourceSpec struct { // Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - // Secrets must be of type kubernetes.io/tls or Opaque. // Allowed values are "Secret" or "ConfigMap". - // +kubebuilder:validation:Enum=Secret;ConfigMap + // "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + // "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. Kind string `json:"kind"` // Name is the resource name of the secret or configmap from which to read the CA bundle. // The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. diff --git a/generated/1.30/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml b/generated/1.30/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml index 5366768a8..60f872205 100644 --- a/generated/1.30/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml +++ b/generated/1.30/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml @@ -110,11 +110,9 @@ spec: kind: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - Secrets must be of type kubernetes.io/tls or Opaque. Allowed values are "Secret" or "ConfigMap". - enum: - - Secret - - ConfigMap + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. type: string name: description: |- diff --git a/generated/1.30/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml b/generated/1.30/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml index 3f4f32dc9..8e5b7d601 100644 --- a/generated/1.30/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml +++ b/generated/1.30/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml @@ -81,11 +81,9 @@ spec: kind: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - Secrets must be of type kubernetes.io/tls or Opaque. Allowed values are "Secret" or "ConfigMap". - enum: - - Secret - - ConfigMap + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. type: string name: description: |- diff --git a/generated/1.30/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml b/generated/1.30/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml index 9a5ec7c90..c608d26cb 100644 --- a/generated/1.30/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml +++ b/generated/1.30/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml @@ -185,11 +185,9 @@ spec: kind: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - Secrets must be of type kubernetes.io/tls or Opaque. Allowed values are "Secret" or "ConfigMap". - enum: - - Secret - - ConfigMap + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. type: string name: description: |- diff --git a/generated/1.30/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml b/generated/1.30/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml index 4cf2b9bca..56743cfce 100644 --- a/generated/1.30/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml +++ b/generated/1.30/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml @@ -244,11 +244,9 @@ spec: kind: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - Secrets must be of type kubernetes.io/tls or Opaque. Allowed values are "Secret" or "ConfigMap". - enum: - - Secret - - ConfigMap + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. type: string name: description: |- diff --git a/generated/1.30/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml b/generated/1.30/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml index 08b616aed..e3ccac69a 100644 --- a/generated/1.30/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml +++ b/generated/1.30/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml @@ -176,11 +176,9 @@ spec: kind: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - Secrets must be of type kubernetes.io/tls or Opaque. Allowed values are "Secret" or "ConfigMap". - enum: - - Secret - - ConfigMap + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. type: string name: description: |- diff --git a/generated/1.30/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml b/generated/1.30/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml index 75efc06fc..51f44856d 100644 --- a/generated/1.30/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml +++ b/generated/1.30/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml @@ -226,11 +226,9 @@ spec: kind: description: |- Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - Secrets must be of type kubernetes.io/tls or Opaque. Allowed values are "Secret" or "ConfigMap". - enum: - - Secret - - ConfigMap + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. type: string name: description: |- diff --git a/generated/latest/README.adoc b/generated/latest/README.adoc index 23ad2e25b..3d317f0d3 100644 --- a/generated/latest/README.adoc +++ b/generated/latest/README.adoc @@ -23,6 +23,8 @@ Package v1alpha1 is the v1alpha1 version of the Pinniped concierge authenticatio + + [id="{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-concierge-authentication-v1alpha1-certificateauthoritydatasourcespec"] ==== CertificateAuthorityDataSourceSpec @@ -37,8 +39,9 @@ CertificateAuthorityDataSourceSpec provides a source for CA bundle used for clie |=== | Field | Description | *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + -Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". + +"ConfigMap" uses a Kubernetes configmap to source CA Bundles. + +"Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + | *`name`* __string__ | Name is the resource name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. + | *`key`* __string__ | Key is the key name within the secret or configmap from which to read the CA bundle. + @@ -1675,6 +1678,8 @@ Optional, when empty this defaults to "objectGUID". + |=== + + [id="{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-supervisor-idp-v1alpha1-certificateauthoritydatasourcespec"] ==== CertificateAuthorityDataSourceSpec @@ -1689,8 +1694,9 @@ CertificateAuthorityDataSourceSpec provides a source for CA bundle used for clie |=== | Field | Description | *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + -Secrets must be of type kubernetes.io/tls or Opaque. + Allowed values are "Secret" or "ConfigMap". + +"ConfigMap" uses a Kubernetes configmap to source CA Bundles. + +"Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + | *`name`* __string__ | Name is the resource name of the secret or configmap from which to read the CA bundle. + The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. + | *`key`* __string__ | Key is the key name within the secret or configmap from which to read the CA bundle. + diff --git a/generated/latest/apis/concierge/authentication/v1alpha1/types_tls.go b/generated/latest/apis/concierge/authentication/v1alpha1/types_tls.go index db9f4ceff..85ea7a94c 100644 --- a/generated/latest/apis/concierge/authentication/v1alpha1/types_tls.go +++ b/generated/latest/apis/concierge/authentication/v1alpha1/types_tls.go @@ -3,12 +3,26 @@ package v1alpha1 +// CertificateAuthorityDataSourceKind enumerates the sources for CA Bundles. +// +// +kubebuilder:validation:Enum=Secret;ConfigMap +type CertificateAuthorityDataSourceKind string + +const ( + // CertificateAuthorityDataSourceKindConfigMap uses a Kubernetes configmap to source CA Bundles. + CertificateAuthorityDataSourceKindConfigMap = CertificateAuthorityDataSourceKind("ConfigMap") + + // CertificateAuthorityDataSourceKindSecret uses a Kubernetes secret to source CA Bundles. + // Secrets used to source CA Bundles must be of type kubernetes.io/tls or Opaque. + CertificateAuthorityDataSourceKindSecret = CertificateAuthorityDataSourceKind("Secret") +) + // CertificateAuthorityDataSourceSpec provides a source for CA bundle used for client-side TLS verification. type CertificateAuthorityDataSourceSpec struct { // Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - // Secrets must be of type kubernetes.io/tls or Opaque. // Allowed values are "Secret" or "ConfigMap". - // +kubebuilder:validation:Enum=Secret;ConfigMap + // "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + // "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. Kind string `json:"kind"` // Name is the resource name of the secret or configmap from which to read the CA bundle. // The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. diff --git a/generated/latest/apis/supervisor/idp/v1alpha1/types_tls.go b/generated/latest/apis/supervisor/idp/v1alpha1/types_tls.go index 70f1c71cc..8c28d7505 100644 --- a/generated/latest/apis/supervisor/idp/v1alpha1/types_tls.go +++ b/generated/latest/apis/supervisor/idp/v1alpha1/types_tls.go @@ -3,12 +3,26 @@ package v1alpha1 +// CertificateAuthorityDataSourceKind enumerates the sources for CA Bundles. +// +// +kubebuilder:validation:Enum=Secret;ConfigMap +type CertificateAuthorityDataSourceKind string + +const ( + // CertificateAuthorityDataSourceKindConfigMap uses a Kubernetes configmap to source CA Bundles. + CertificateAuthorityDataSourceKindConfigMap = CertificateAuthorityDataSourceKind("ConfigMap") + + // CertificateAuthorityDataSourceKindSecret uses a Kubernetes secret to source CA Bundles. + // Secrets used to source CA Bundles must be of type kubernetes.io/tls or Opaque. + CertificateAuthorityDataSourceKindSecret = CertificateAuthorityDataSourceKind("Secret") +) + // CertificateAuthorityDataSourceSpec provides a source for CA bundle used for client-side TLS verification. type CertificateAuthorityDataSourceSpec struct { // Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. - // Secrets must be of type kubernetes.io/tls or Opaque. // Allowed values are "Secret" or "ConfigMap". - // +kubebuilder:validation:Enum=Secret;ConfigMap + // "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + // "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. Kind string `json:"kind"` // Name is the resource name of the secret or configmap from which to read the CA bundle. // The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. From b70db9dc0325c9b71dedf622b3f0fb5ab1ceacc1 Mon Sep 17 00:00:00 2001 From: Ashish Amarnath Date: Sat, 3 Aug 2024 16:59:18 -0700 Subject: [PATCH 95/99] refactor to use new certificateAuthorityDataSourceKind enum Signed-off-by: Ashish Amarnath --- internal/controller/tlsconfigutil/tls_config_util.go | 4 ++-- test/integration/concierge_jwtauthenticator_status_test.go | 6 +++--- .../concierge_webhookauthenticator_status_test.go | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/internal/controller/tlsconfigutil/tls_config_util.go b/internal/controller/tlsconfigutil/tls_config_util.go index 69376e3dc..2c783db6d 100644 --- a/internal/controller/tlsconfigutil/tls_config_util.go +++ b/internal/controller/tlsconfigutil/tls_config_util.go @@ -52,7 +52,7 @@ func TLSSpecForSupervisor(source *idpv1alpha1.TLSSpec) *TLSSpec { if source.CertificateAuthorityDataSource != nil { dest.CertificateAuthorityDataSource = &caBundleSource{ - Kind: source.CertificateAuthorityDataSource.Kind, + Kind: string(source.CertificateAuthorityDataSource.Kind), Name: source.CertificateAuthorityDataSource.Name, Key: source.CertificateAuthorityDataSource.Key, } @@ -71,7 +71,7 @@ func TLSSpecForConcierge(source *authenticationv1alpha1.TLSSpec) *TLSSpec { } if source.CertificateAuthorityDataSource != nil { dest.CertificateAuthorityDataSource = &caBundleSource{ - Kind: source.CertificateAuthorityDataSource.Kind, + Kind: string(source.CertificateAuthorityDataSource.Kind), Name: source.CertificateAuthorityDataSource.Name, Key: source.CertificateAuthorityDataSource.Key, } diff --git a/test/integration/concierge_jwtauthenticator_status_test.go b/test/integration/concierge_jwtauthenticator_status_test.go index 4c992c193..327e85e87 100644 --- a/test/integration/concierge_jwtauthenticator_status_test.go +++ b/test/integration/concierge_jwtauthenticator_status_test.go @@ -33,13 +33,13 @@ func TestConciergeJWTAuthenticatorWithExternalCABundleStatusIsUpdatedWhenExterna tests := []struct { name string - caBundleSourceSpecKind string + caBundleSourceSpecKind authenticationv1alpha1.CertificateAuthorityDataSourceKind createResourceForCABundle func(t *testing.T, caBundle string) string updateCABundle func(t *testing.T, resourceName, caBundle string) }{ { name: "for a CA bundle from a ConfigMap", - caBundleSourceSpecKind: "ConfigMap", + caBundleSourceSpecKind: authenticationv1alpha1.CertificateAuthorityDataSourceKindConfigMap, createResourceForCABundle: func(t *testing.T, caBundle string) string { createdResource := testlib.CreateTestConfigMap(t, env.ConciergeNamespace, "ca-bundle", map[string]string{ "ca.crt": caBundle, @@ -58,7 +58,7 @@ func TestConciergeJWTAuthenticatorWithExternalCABundleStatusIsUpdatedWhenExterna }, { name: "for a CA bundle from a Secret", - caBundleSourceSpecKind: "Secret", + caBundleSourceSpecKind: authenticationv1alpha1.CertificateAuthorityDataSourceKindSecret, createResourceForCABundle: func(t *testing.T, caBundle string) string { createdResource := testlib.CreateTestSecret(t, env.ConciergeNamespace, "ca-bundle", corev1.SecretTypeOpaque, map[string]string{ "ca.crt": caBundle, diff --git a/test/integration/concierge_webhookauthenticator_status_test.go b/test/integration/concierge_webhookauthenticator_status_test.go index 910bcd57d..8c8b3b515 100644 --- a/test/integration/concierge_webhookauthenticator_status_test.go +++ b/test/integration/concierge_webhookauthenticator_status_test.go @@ -28,13 +28,13 @@ func TestConciergeWebhookAuthenticatorWithExternalCABundleStatusIsUpdatedWhenExt tests := []struct { name string - caBundleSourceSpecKind string + caBundleSourceSpecKind authenticationv1alpha1.CertificateAuthorityDataSourceKind createResourceForCABundle func(t *testing.T, caBundle string) string updateCABundle func(t *testing.T, resourceName, caBundle string) }{ { name: "for a CA bundle from a ConfigMap", - caBundleSourceSpecKind: "ConfigMap", + caBundleSourceSpecKind: authenticationv1alpha1.CertificateAuthorityDataSourceKindConfigMap, createResourceForCABundle: func(t *testing.T, caBundle string) string { createdResource := testlib.CreateTestConfigMap(t, env.ConciergeNamespace, "ca-bundle", map[string]string{ "ca.crt": caBundle, @@ -53,7 +53,7 @@ func TestConciergeWebhookAuthenticatorWithExternalCABundleStatusIsUpdatedWhenExt }, { name: "for a CA bundle from a Secret", - caBundleSourceSpecKind: "Secret", + caBundleSourceSpecKind: authenticationv1alpha1.CertificateAuthorityDataSourceKindSecret, createResourceForCABundle: func(t *testing.T, caBundle string) string { createdResource := testlib.CreateTestSecret(t, env.ConciergeNamespace, "ca-bundle", corev1.SecretTypeOpaque, map[string]string{ "ca.crt": caBundle, From 06b7d302a2e6321d266213edea0eb1a961ce757f Mon Sep 17 00:00:00 2001 From: Ryan Richard Date: Mon, 5 Aug 2024 10:44:04 -0700 Subject: [PATCH 96/99] fix typo in tmpl and run codegen --- .../authentication/v1alpha1/types_tls.go.tmpl | 2 +- .../supervisor/idp/v1alpha1/types_tls.go.tmpl | 2 +- ...cierge.pinniped.dev_jwtauthenticators.yaml | 3 +++ ...ge.pinniped.dev_webhookauthenticators.yaml | 3 +++ ....dev_activedirectoryidentityproviders.yaml | 3 +++ ....pinniped.dev_githubidentityproviders.yaml | 3 +++ ...or.pinniped.dev_ldapidentityproviders.yaml | 3 +++ ...or.pinniped.dev_oidcidentityproviders.yaml | 3 +++ generated/1.24/README.adoc | 24 +++++++++++++++++-- .../authentication/v1alpha1/types_tls.go | 2 +- .../apis/supervisor/idp/v1alpha1/types_tls.go | 2 +- ...cierge.pinniped.dev_jwtauthenticators.yaml | 3 +++ ...ge.pinniped.dev_webhookauthenticators.yaml | 3 +++ ....dev_activedirectoryidentityproviders.yaml | 3 +++ ....pinniped.dev_githubidentityproviders.yaml | 3 +++ ...or.pinniped.dev_ldapidentityproviders.yaml | 3 +++ ...or.pinniped.dev_oidcidentityproviders.yaml | 3 +++ generated/1.25/README.adoc | 24 +++++++++++++++++-- .../authentication/v1alpha1/types_tls.go | 2 +- .../apis/supervisor/idp/v1alpha1/types_tls.go | 2 +- ...cierge.pinniped.dev_jwtauthenticators.yaml | 3 +++ ...ge.pinniped.dev_webhookauthenticators.yaml | 3 +++ ....dev_activedirectoryidentityproviders.yaml | 3 +++ ....pinniped.dev_githubidentityproviders.yaml | 3 +++ ...or.pinniped.dev_ldapidentityproviders.yaml | 3 +++ ...or.pinniped.dev_oidcidentityproviders.yaml | 3 +++ generated/1.26/README.adoc | 24 +++++++++++++++++-- .../authentication/v1alpha1/types_tls.go | 2 +- .../apis/supervisor/idp/v1alpha1/types_tls.go | 2 +- ...cierge.pinniped.dev_jwtauthenticators.yaml | 3 +++ ...ge.pinniped.dev_webhookauthenticators.yaml | 3 +++ ....dev_activedirectoryidentityproviders.yaml | 3 +++ ....pinniped.dev_githubidentityproviders.yaml | 3 +++ ...or.pinniped.dev_ldapidentityproviders.yaml | 3 +++ ...or.pinniped.dev_oidcidentityproviders.yaml | 3 +++ generated/1.27/README.adoc | 24 +++++++++++++++++-- .../authentication/v1alpha1/types_tls.go | 2 +- .../apis/supervisor/idp/v1alpha1/types_tls.go | 2 +- ...cierge.pinniped.dev_jwtauthenticators.yaml | 3 +++ ...ge.pinniped.dev_webhookauthenticators.yaml | 3 +++ ....dev_activedirectoryidentityproviders.yaml | 3 +++ ....pinniped.dev_githubidentityproviders.yaml | 3 +++ ...or.pinniped.dev_ldapidentityproviders.yaml | 3 +++ ...or.pinniped.dev_oidcidentityproviders.yaml | 3 +++ generated/1.28/README.adoc | 24 +++++++++++++++++-- .../authentication/v1alpha1/types_tls.go | 2 +- .../apis/supervisor/idp/v1alpha1/types_tls.go | 2 +- ...cierge.pinniped.dev_jwtauthenticators.yaml | 3 +++ ...ge.pinniped.dev_webhookauthenticators.yaml | 3 +++ ....dev_activedirectoryidentityproviders.yaml | 3 +++ ....pinniped.dev_githubidentityproviders.yaml | 3 +++ ...or.pinniped.dev_ldapidentityproviders.yaml | 3 +++ ...or.pinniped.dev_oidcidentityproviders.yaml | 3 +++ generated/1.29/README.adoc | 24 +++++++++++++++++-- .../authentication/v1alpha1/types_tls.go | 2 +- .../apis/supervisor/idp/v1alpha1/types_tls.go | 2 +- ...cierge.pinniped.dev_jwtauthenticators.yaml | 3 +++ ...ge.pinniped.dev_webhookauthenticators.yaml | 3 +++ ....dev_activedirectoryidentityproviders.yaml | 3 +++ ....pinniped.dev_githubidentityproviders.yaml | 3 +++ ...or.pinniped.dev_ldapidentityproviders.yaml | 3 +++ ...or.pinniped.dev_oidcidentityproviders.yaml | 3 +++ generated/1.30/README.adoc | 24 +++++++++++++++++-- .../authentication/v1alpha1/types_tls.go | 2 +- .../apis/supervisor/idp/v1alpha1/types_tls.go | 2 +- ...cierge.pinniped.dev_jwtauthenticators.yaml | 3 +++ ...ge.pinniped.dev_webhookauthenticators.yaml | 3 +++ ....dev_activedirectoryidentityproviders.yaml | 3 +++ ....pinniped.dev_githubidentityproviders.yaml | 3 +++ ...or.pinniped.dev_ldapidentityproviders.yaml | 3 +++ ...or.pinniped.dev_oidcidentityproviders.yaml | 3 +++ generated/latest/README.adoc | 24 +++++++++++++++++-- .../authentication/v1alpha1/types_tls.go | 2 +- .../apis/supervisor/idp/v1alpha1/types_tls.go | 2 +- 74 files changed, 338 insertions(+), 34 deletions(-) diff --git a/apis/concierge/authentication/v1alpha1/types_tls.go.tmpl b/apis/concierge/authentication/v1alpha1/types_tls.go.tmpl index 85ea7a94c..3be891eda 100644 --- a/apis/concierge/authentication/v1alpha1/types_tls.go.tmpl +++ b/apis/concierge/authentication/v1alpha1/types_tls.go.tmpl @@ -23,7 +23,7 @@ type CertificateAuthorityDataSourceSpec struct { // Allowed values are "Secret" or "ConfigMap". // "ConfigMap" uses a Kubernetes configmap to source CA Bundles. // "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. - Kind string `json:"kind"` + Kind CertificateAuthorityDataSourceKind `json:"kind"` // Name is the resource name of the secret or configmap from which to read the CA bundle. // The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. // +kubebuilder:validation:MinLength=1 diff --git a/apis/supervisor/idp/v1alpha1/types_tls.go.tmpl b/apis/supervisor/idp/v1alpha1/types_tls.go.tmpl index 8c28d7505..831cd308c 100644 --- a/apis/supervisor/idp/v1alpha1/types_tls.go.tmpl +++ b/apis/supervisor/idp/v1alpha1/types_tls.go.tmpl @@ -23,7 +23,7 @@ type CertificateAuthorityDataSourceSpec struct { // Allowed values are "Secret" or "ConfigMap". // "ConfigMap" uses a Kubernetes configmap to source CA Bundles. // "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. - Kind string `json:"kind"` + Kind CertificateAuthorityDataSourceKind `json:"kind"` // Name is the resource name of the secret or configmap from which to read the CA bundle. // The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. // +kubebuilder:validation:MinLength=1 diff --git a/deploy/concierge/authentication.concierge.pinniped.dev_jwtauthenticators.yaml b/deploy/concierge/authentication.concierge.pinniped.dev_jwtauthenticators.yaml index 60f872205..f707ed4a8 100644 --- a/deploy/concierge/authentication.concierge.pinniped.dev_jwtauthenticators.yaml +++ b/deploy/concierge/authentication.concierge.pinniped.dev_jwtauthenticators.yaml @@ -113,6 +113,9 @@ spec: Allowed values are "Secret" or "ConfigMap". "ConfigMap" uses a Kubernetes configmap to source CA Bundles. "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + enum: + - Secret + - ConfigMap type: string name: description: |- diff --git a/deploy/concierge/authentication.concierge.pinniped.dev_webhookauthenticators.yaml b/deploy/concierge/authentication.concierge.pinniped.dev_webhookauthenticators.yaml index 8e5b7d601..7285fee36 100644 --- a/deploy/concierge/authentication.concierge.pinniped.dev_webhookauthenticators.yaml +++ b/deploy/concierge/authentication.concierge.pinniped.dev_webhookauthenticators.yaml @@ -84,6 +84,9 @@ spec: Allowed values are "Secret" or "ConfigMap". "ConfigMap" uses a Kubernetes configmap to source CA Bundles. "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + enum: + - Secret + - ConfigMap type: string name: description: |- diff --git a/deploy/supervisor/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml b/deploy/supervisor/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml index c608d26cb..a9f4ee414 100644 --- a/deploy/supervisor/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml +++ b/deploy/supervisor/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml @@ -188,6 +188,9 @@ spec: Allowed values are "Secret" or "ConfigMap". "ConfigMap" uses a Kubernetes configmap to source CA Bundles. "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + enum: + - Secret + - ConfigMap type: string name: description: |- diff --git a/deploy/supervisor/idp.supervisor.pinniped.dev_githubidentityproviders.yaml b/deploy/supervisor/idp.supervisor.pinniped.dev_githubidentityproviders.yaml index 56743cfce..d14c6773d 100644 --- a/deploy/supervisor/idp.supervisor.pinniped.dev_githubidentityproviders.yaml +++ b/deploy/supervisor/idp.supervisor.pinniped.dev_githubidentityproviders.yaml @@ -247,6 +247,9 @@ spec: Allowed values are "Secret" or "ConfigMap". "ConfigMap" uses a Kubernetes configmap to source CA Bundles. "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + enum: + - Secret + - ConfigMap type: string name: description: |- diff --git a/deploy/supervisor/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml b/deploy/supervisor/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml index e3ccac69a..463351de6 100644 --- a/deploy/supervisor/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml +++ b/deploy/supervisor/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml @@ -179,6 +179,9 @@ spec: Allowed values are "Secret" or "ConfigMap". "ConfigMap" uses a Kubernetes configmap to source CA Bundles. "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + enum: + - Secret + - ConfigMap type: string name: description: |- diff --git a/deploy/supervisor/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml b/deploy/supervisor/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml index 51f44856d..33e15403c 100644 --- a/deploy/supervisor/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml +++ b/deploy/supervisor/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml @@ -229,6 +229,9 @@ spec: Allowed values are "Secret" or "ConfigMap". "ConfigMap" uses a Kubernetes configmap to source CA Bundles. "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + enum: + - Secret + - ConfigMap type: string name: description: |- diff --git a/generated/1.24/README.adoc b/generated/1.24/README.adoc index df8f27733..55d1e9a75 100644 --- a/generated/1.24/README.adoc +++ b/generated/1.24/README.adoc @@ -23,6 +23,16 @@ Package v1alpha1 is the v1alpha1 version of the Pinniped concierge authenticatio +[id="{anchor_prefix}-go-pinniped-dev-generated-1-24-apis-concierge-authentication-v1alpha1-certificateauthoritydatasourcekind"] +==== CertificateAuthorityDataSourceKind (string) + +CertificateAuthorityDataSourceKind enumerates the sources for CA Bundles. + +.Appears In: +**** +- xref:{anchor_prefix}-go-pinniped-dev-generated-1-24-apis-concierge-authentication-v1alpha1-certificateauthoritydatasourcespec[$$CertificateAuthorityDataSourceSpec$$] +**** + [id="{anchor_prefix}-go-pinniped-dev-generated-1-24-apis-concierge-authentication-v1alpha1-certificateauthoritydatasourcespec"] @@ -38,7 +48,7 @@ CertificateAuthorityDataSourceSpec provides a source for CA bundle used for clie [cols="25a,75a", options="header"] |=== | Field | Description -| *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + +| *`kind`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-24-apis-concierge-authentication-v1alpha1-certificateauthoritydatasourcekind[$$CertificateAuthorityDataSourceKind$$]__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + Allowed values are "Secret" or "ConfigMap". + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + @@ -1678,6 +1688,16 @@ Optional, when empty this defaults to "objectGUID". + |=== +[id="{anchor_prefix}-go-pinniped-dev-generated-1-24-apis-supervisor-idp-v1alpha1-certificateauthoritydatasourcekind"] +==== CertificateAuthorityDataSourceKind (string) + +CertificateAuthorityDataSourceKind enumerates the sources for CA Bundles. + +.Appears In: +**** +- xref:{anchor_prefix}-go-pinniped-dev-generated-1-24-apis-supervisor-idp-v1alpha1-certificateauthoritydatasourcespec[$$CertificateAuthorityDataSourceSpec$$] +**** + [id="{anchor_prefix}-go-pinniped-dev-generated-1-24-apis-supervisor-idp-v1alpha1-certificateauthoritydatasourcespec"] @@ -1693,7 +1713,7 @@ CertificateAuthorityDataSourceSpec provides a source for CA bundle used for clie [cols="25a,75a", options="header"] |=== | Field | Description -| *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + +| *`kind`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-24-apis-supervisor-idp-v1alpha1-certificateauthoritydatasourcekind[$$CertificateAuthorityDataSourceKind$$]__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + Allowed values are "Secret" or "ConfigMap". + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + diff --git a/generated/1.24/apis/concierge/authentication/v1alpha1/types_tls.go b/generated/1.24/apis/concierge/authentication/v1alpha1/types_tls.go index 85ea7a94c..3be891eda 100644 --- a/generated/1.24/apis/concierge/authentication/v1alpha1/types_tls.go +++ b/generated/1.24/apis/concierge/authentication/v1alpha1/types_tls.go @@ -23,7 +23,7 @@ type CertificateAuthorityDataSourceSpec struct { // Allowed values are "Secret" or "ConfigMap". // "ConfigMap" uses a Kubernetes configmap to source CA Bundles. // "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. - Kind string `json:"kind"` + Kind CertificateAuthorityDataSourceKind `json:"kind"` // Name is the resource name of the secret or configmap from which to read the CA bundle. // The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. // +kubebuilder:validation:MinLength=1 diff --git a/generated/1.24/apis/supervisor/idp/v1alpha1/types_tls.go b/generated/1.24/apis/supervisor/idp/v1alpha1/types_tls.go index 8c28d7505..831cd308c 100644 --- a/generated/1.24/apis/supervisor/idp/v1alpha1/types_tls.go +++ b/generated/1.24/apis/supervisor/idp/v1alpha1/types_tls.go @@ -23,7 +23,7 @@ type CertificateAuthorityDataSourceSpec struct { // Allowed values are "Secret" or "ConfigMap". // "ConfigMap" uses a Kubernetes configmap to source CA Bundles. // "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. - Kind string `json:"kind"` + Kind CertificateAuthorityDataSourceKind `json:"kind"` // Name is the resource name of the secret or configmap from which to read the CA bundle. // The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. // +kubebuilder:validation:MinLength=1 diff --git a/generated/1.24/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml b/generated/1.24/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml index 60f872205..f707ed4a8 100644 --- a/generated/1.24/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml +++ b/generated/1.24/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml @@ -113,6 +113,9 @@ spec: Allowed values are "Secret" or "ConfigMap". "ConfigMap" uses a Kubernetes configmap to source CA Bundles. "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + enum: + - Secret + - ConfigMap type: string name: description: |- diff --git a/generated/1.24/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml b/generated/1.24/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml index 8e5b7d601..7285fee36 100644 --- a/generated/1.24/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml +++ b/generated/1.24/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml @@ -84,6 +84,9 @@ spec: Allowed values are "Secret" or "ConfigMap". "ConfigMap" uses a Kubernetes configmap to source CA Bundles. "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + enum: + - Secret + - ConfigMap type: string name: description: |- diff --git a/generated/1.24/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml b/generated/1.24/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml index c608d26cb..a9f4ee414 100644 --- a/generated/1.24/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml +++ b/generated/1.24/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml @@ -188,6 +188,9 @@ spec: Allowed values are "Secret" or "ConfigMap". "ConfigMap" uses a Kubernetes configmap to source CA Bundles. "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + enum: + - Secret + - ConfigMap type: string name: description: |- diff --git a/generated/1.24/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml b/generated/1.24/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml index 56743cfce..d14c6773d 100644 --- a/generated/1.24/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml +++ b/generated/1.24/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml @@ -247,6 +247,9 @@ spec: Allowed values are "Secret" or "ConfigMap". "ConfigMap" uses a Kubernetes configmap to source CA Bundles. "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + enum: + - Secret + - ConfigMap type: string name: description: |- diff --git a/generated/1.24/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml b/generated/1.24/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml index e3ccac69a..463351de6 100644 --- a/generated/1.24/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml +++ b/generated/1.24/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml @@ -179,6 +179,9 @@ spec: Allowed values are "Secret" or "ConfigMap". "ConfigMap" uses a Kubernetes configmap to source CA Bundles. "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + enum: + - Secret + - ConfigMap type: string name: description: |- diff --git a/generated/1.24/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml b/generated/1.24/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml index 51f44856d..33e15403c 100644 --- a/generated/1.24/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml +++ b/generated/1.24/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml @@ -229,6 +229,9 @@ spec: Allowed values are "Secret" or "ConfigMap". "ConfigMap" uses a Kubernetes configmap to source CA Bundles. "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + enum: + - Secret + - ConfigMap type: string name: description: |- diff --git a/generated/1.25/README.adoc b/generated/1.25/README.adoc index 22c907b91..6c20c0159 100644 --- a/generated/1.25/README.adoc +++ b/generated/1.25/README.adoc @@ -23,6 +23,16 @@ Package v1alpha1 is the v1alpha1 version of the Pinniped concierge authenticatio +[id="{anchor_prefix}-go-pinniped-dev-generated-1-25-apis-concierge-authentication-v1alpha1-certificateauthoritydatasourcekind"] +==== CertificateAuthorityDataSourceKind (string) + +CertificateAuthorityDataSourceKind enumerates the sources for CA Bundles. + +.Appears In: +**** +- xref:{anchor_prefix}-go-pinniped-dev-generated-1-25-apis-concierge-authentication-v1alpha1-certificateauthoritydatasourcespec[$$CertificateAuthorityDataSourceSpec$$] +**** + [id="{anchor_prefix}-go-pinniped-dev-generated-1-25-apis-concierge-authentication-v1alpha1-certificateauthoritydatasourcespec"] @@ -38,7 +48,7 @@ CertificateAuthorityDataSourceSpec provides a source for CA bundle used for clie [cols="25a,75a", options="header"] |=== | Field | Description -| *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + +| *`kind`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-25-apis-concierge-authentication-v1alpha1-certificateauthoritydatasourcekind[$$CertificateAuthorityDataSourceKind$$]__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + Allowed values are "Secret" or "ConfigMap". + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + @@ -1678,6 +1688,16 @@ Optional, when empty this defaults to "objectGUID". + |=== +[id="{anchor_prefix}-go-pinniped-dev-generated-1-25-apis-supervisor-idp-v1alpha1-certificateauthoritydatasourcekind"] +==== CertificateAuthorityDataSourceKind (string) + +CertificateAuthorityDataSourceKind enumerates the sources for CA Bundles. + +.Appears In: +**** +- xref:{anchor_prefix}-go-pinniped-dev-generated-1-25-apis-supervisor-idp-v1alpha1-certificateauthoritydatasourcespec[$$CertificateAuthorityDataSourceSpec$$] +**** + [id="{anchor_prefix}-go-pinniped-dev-generated-1-25-apis-supervisor-idp-v1alpha1-certificateauthoritydatasourcespec"] @@ -1693,7 +1713,7 @@ CertificateAuthorityDataSourceSpec provides a source for CA bundle used for clie [cols="25a,75a", options="header"] |=== | Field | Description -| *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + +| *`kind`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-25-apis-supervisor-idp-v1alpha1-certificateauthoritydatasourcekind[$$CertificateAuthorityDataSourceKind$$]__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + Allowed values are "Secret" or "ConfigMap". + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + diff --git a/generated/1.25/apis/concierge/authentication/v1alpha1/types_tls.go b/generated/1.25/apis/concierge/authentication/v1alpha1/types_tls.go index 85ea7a94c..3be891eda 100644 --- a/generated/1.25/apis/concierge/authentication/v1alpha1/types_tls.go +++ b/generated/1.25/apis/concierge/authentication/v1alpha1/types_tls.go @@ -23,7 +23,7 @@ type CertificateAuthorityDataSourceSpec struct { // Allowed values are "Secret" or "ConfigMap". // "ConfigMap" uses a Kubernetes configmap to source CA Bundles. // "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. - Kind string `json:"kind"` + Kind CertificateAuthorityDataSourceKind `json:"kind"` // Name is the resource name of the secret or configmap from which to read the CA bundle. // The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. // +kubebuilder:validation:MinLength=1 diff --git a/generated/1.25/apis/supervisor/idp/v1alpha1/types_tls.go b/generated/1.25/apis/supervisor/idp/v1alpha1/types_tls.go index 8c28d7505..831cd308c 100644 --- a/generated/1.25/apis/supervisor/idp/v1alpha1/types_tls.go +++ b/generated/1.25/apis/supervisor/idp/v1alpha1/types_tls.go @@ -23,7 +23,7 @@ type CertificateAuthorityDataSourceSpec struct { // Allowed values are "Secret" or "ConfigMap". // "ConfigMap" uses a Kubernetes configmap to source CA Bundles. // "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. - Kind string `json:"kind"` + Kind CertificateAuthorityDataSourceKind `json:"kind"` // Name is the resource name of the secret or configmap from which to read the CA bundle. // The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. // +kubebuilder:validation:MinLength=1 diff --git a/generated/1.25/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml b/generated/1.25/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml index 60f872205..f707ed4a8 100644 --- a/generated/1.25/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml +++ b/generated/1.25/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml @@ -113,6 +113,9 @@ spec: Allowed values are "Secret" or "ConfigMap". "ConfigMap" uses a Kubernetes configmap to source CA Bundles. "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + enum: + - Secret + - ConfigMap type: string name: description: |- diff --git a/generated/1.25/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml b/generated/1.25/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml index 8e5b7d601..7285fee36 100644 --- a/generated/1.25/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml +++ b/generated/1.25/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml @@ -84,6 +84,9 @@ spec: Allowed values are "Secret" or "ConfigMap". "ConfigMap" uses a Kubernetes configmap to source CA Bundles. "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + enum: + - Secret + - ConfigMap type: string name: description: |- diff --git a/generated/1.25/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml b/generated/1.25/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml index c608d26cb..a9f4ee414 100644 --- a/generated/1.25/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml +++ b/generated/1.25/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml @@ -188,6 +188,9 @@ spec: Allowed values are "Secret" or "ConfigMap". "ConfigMap" uses a Kubernetes configmap to source CA Bundles. "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + enum: + - Secret + - ConfigMap type: string name: description: |- diff --git a/generated/1.25/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml b/generated/1.25/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml index 56743cfce..d14c6773d 100644 --- a/generated/1.25/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml +++ b/generated/1.25/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml @@ -247,6 +247,9 @@ spec: Allowed values are "Secret" or "ConfigMap". "ConfigMap" uses a Kubernetes configmap to source CA Bundles. "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + enum: + - Secret + - ConfigMap type: string name: description: |- diff --git a/generated/1.25/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml b/generated/1.25/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml index e3ccac69a..463351de6 100644 --- a/generated/1.25/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml +++ b/generated/1.25/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml @@ -179,6 +179,9 @@ spec: Allowed values are "Secret" or "ConfigMap". "ConfigMap" uses a Kubernetes configmap to source CA Bundles. "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + enum: + - Secret + - ConfigMap type: string name: description: |- diff --git a/generated/1.25/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml b/generated/1.25/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml index 51f44856d..33e15403c 100644 --- a/generated/1.25/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml +++ b/generated/1.25/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml @@ -229,6 +229,9 @@ spec: Allowed values are "Secret" or "ConfigMap". "ConfigMap" uses a Kubernetes configmap to source CA Bundles. "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + enum: + - Secret + - ConfigMap type: string name: description: |- diff --git a/generated/1.26/README.adoc b/generated/1.26/README.adoc index 8eb3082bc..d38453a85 100644 --- a/generated/1.26/README.adoc +++ b/generated/1.26/README.adoc @@ -23,6 +23,16 @@ Package v1alpha1 is the v1alpha1 version of the Pinniped concierge authenticatio +[id="{anchor_prefix}-go-pinniped-dev-generated-1-26-apis-concierge-authentication-v1alpha1-certificateauthoritydatasourcekind"] +==== CertificateAuthorityDataSourceKind (string) + +CertificateAuthorityDataSourceKind enumerates the sources for CA Bundles. + +.Appears In: +**** +- xref:{anchor_prefix}-go-pinniped-dev-generated-1-26-apis-concierge-authentication-v1alpha1-certificateauthoritydatasourcespec[$$CertificateAuthorityDataSourceSpec$$] +**** + [id="{anchor_prefix}-go-pinniped-dev-generated-1-26-apis-concierge-authentication-v1alpha1-certificateauthoritydatasourcespec"] @@ -38,7 +48,7 @@ CertificateAuthorityDataSourceSpec provides a source for CA bundle used for clie [cols="25a,75a", options="header"] |=== | Field | Description -| *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + +| *`kind`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-26-apis-concierge-authentication-v1alpha1-certificateauthoritydatasourcekind[$$CertificateAuthorityDataSourceKind$$]__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + Allowed values are "Secret" or "ConfigMap". + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + @@ -1678,6 +1688,16 @@ Optional, when empty this defaults to "objectGUID". + |=== +[id="{anchor_prefix}-go-pinniped-dev-generated-1-26-apis-supervisor-idp-v1alpha1-certificateauthoritydatasourcekind"] +==== CertificateAuthorityDataSourceKind (string) + +CertificateAuthorityDataSourceKind enumerates the sources for CA Bundles. + +.Appears In: +**** +- xref:{anchor_prefix}-go-pinniped-dev-generated-1-26-apis-supervisor-idp-v1alpha1-certificateauthoritydatasourcespec[$$CertificateAuthorityDataSourceSpec$$] +**** + [id="{anchor_prefix}-go-pinniped-dev-generated-1-26-apis-supervisor-idp-v1alpha1-certificateauthoritydatasourcespec"] @@ -1693,7 +1713,7 @@ CertificateAuthorityDataSourceSpec provides a source for CA bundle used for clie [cols="25a,75a", options="header"] |=== | Field | Description -| *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + +| *`kind`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-26-apis-supervisor-idp-v1alpha1-certificateauthoritydatasourcekind[$$CertificateAuthorityDataSourceKind$$]__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + Allowed values are "Secret" or "ConfigMap". + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + diff --git a/generated/1.26/apis/concierge/authentication/v1alpha1/types_tls.go b/generated/1.26/apis/concierge/authentication/v1alpha1/types_tls.go index 85ea7a94c..3be891eda 100644 --- a/generated/1.26/apis/concierge/authentication/v1alpha1/types_tls.go +++ b/generated/1.26/apis/concierge/authentication/v1alpha1/types_tls.go @@ -23,7 +23,7 @@ type CertificateAuthorityDataSourceSpec struct { // Allowed values are "Secret" or "ConfigMap". // "ConfigMap" uses a Kubernetes configmap to source CA Bundles. // "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. - Kind string `json:"kind"` + Kind CertificateAuthorityDataSourceKind `json:"kind"` // Name is the resource name of the secret or configmap from which to read the CA bundle. // The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. // +kubebuilder:validation:MinLength=1 diff --git a/generated/1.26/apis/supervisor/idp/v1alpha1/types_tls.go b/generated/1.26/apis/supervisor/idp/v1alpha1/types_tls.go index 8c28d7505..831cd308c 100644 --- a/generated/1.26/apis/supervisor/idp/v1alpha1/types_tls.go +++ b/generated/1.26/apis/supervisor/idp/v1alpha1/types_tls.go @@ -23,7 +23,7 @@ type CertificateAuthorityDataSourceSpec struct { // Allowed values are "Secret" or "ConfigMap". // "ConfigMap" uses a Kubernetes configmap to source CA Bundles. // "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. - Kind string `json:"kind"` + Kind CertificateAuthorityDataSourceKind `json:"kind"` // Name is the resource name of the secret or configmap from which to read the CA bundle. // The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. // +kubebuilder:validation:MinLength=1 diff --git a/generated/1.26/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml b/generated/1.26/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml index 60f872205..f707ed4a8 100644 --- a/generated/1.26/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml +++ b/generated/1.26/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml @@ -113,6 +113,9 @@ spec: Allowed values are "Secret" or "ConfigMap". "ConfigMap" uses a Kubernetes configmap to source CA Bundles. "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + enum: + - Secret + - ConfigMap type: string name: description: |- diff --git a/generated/1.26/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml b/generated/1.26/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml index 8e5b7d601..7285fee36 100644 --- a/generated/1.26/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml +++ b/generated/1.26/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml @@ -84,6 +84,9 @@ spec: Allowed values are "Secret" or "ConfigMap". "ConfigMap" uses a Kubernetes configmap to source CA Bundles. "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + enum: + - Secret + - ConfigMap type: string name: description: |- diff --git a/generated/1.26/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml b/generated/1.26/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml index c608d26cb..a9f4ee414 100644 --- a/generated/1.26/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml +++ b/generated/1.26/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml @@ -188,6 +188,9 @@ spec: Allowed values are "Secret" or "ConfigMap". "ConfigMap" uses a Kubernetes configmap to source CA Bundles. "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + enum: + - Secret + - ConfigMap type: string name: description: |- diff --git a/generated/1.26/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml b/generated/1.26/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml index 56743cfce..d14c6773d 100644 --- a/generated/1.26/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml +++ b/generated/1.26/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml @@ -247,6 +247,9 @@ spec: Allowed values are "Secret" or "ConfigMap". "ConfigMap" uses a Kubernetes configmap to source CA Bundles. "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + enum: + - Secret + - ConfigMap type: string name: description: |- diff --git a/generated/1.26/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml b/generated/1.26/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml index e3ccac69a..463351de6 100644 --- a/generated/1.26/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml +++ b/generated/1.26/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml @@ -179,6 +179,9 @@ spec: Allowed values are "Secret" or "ConfigMap". "ConfigMap" uses a Kubernetes configmap to source CA Bundles. "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + enum: + - Secret + - ConfigMap type: string name: description: |- diff --git a/generated/1.26/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml b/generated/1.26/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml index 51f44856d..33e15403c 100644 --- a/generated/1.26/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml +++ b/generated/1.26/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml @@ -229,6 +229,9 @@ spec: Allowed values are "Secret" or "ConfigMap". "ConfigMap" uses a Kubernetes configmap to source CA Bundles. "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + enum: + - Secret + - ConfigMap type: string name: description: |- diff --git a/generated/1.27/README.adoc b/generated/1.27/README.adoc index 90d7ab20c..8f76bded1 100644 --- a/generated/1.27/README.adoc +++ b/generated/1.27/README.adoc @@ -23,6 +23,16 @@ Package v1alpha1 is the v1alpha1 version of the Pinniped concierge authenticatio +[id="{anchor_prefix}-go-pinniped-dev-generated-1-27-apis-concierge-authentication-v1alpha1-certificateauthoritydatasourcekind"] +==== CertificateAuthorityDataSourceKind (string) + +CertificateAuthorityDataSourceKind enumerates the sources for CA Bundles. + +.Appears In: +**** +- xref:{anchor_prefix}-go-pinniped-dev-generated-1-27-apis-concierge-authentication-v1alpha1-certificateauthoritydatasourcespec[$$CertificateAuthorityDataSourceSpec$$] +**** + [id="{anchor_prefix}-go-pinniped-dev-generated-1-27-apis-concierge-authentication-v1alpha1-certificateauthoritydatasourcespec"] @@ -38,7 +48,7 @@ CertificateAuthorityDataSourceSpec provides a source for CA bundle used for clie [cols="25a,75a", options="header"] |=== | Field | Description -| *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + +| *`kind`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-27-apis-concierge-authentication-v1alpha1-certificateauthoritydatasourcekind[$$CertificateAuthorityDataSourceKind$$]__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + Allowed values are "Secret" or "ConfigMap". + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + @@ -1678,6 +1688,16 @@ Optional, when empty this defaults to "objectGUID". + |=== +[id="{anchor_prefix}-go-pinniped-dev-generated-1-27-apis-supervisor-idp-v1alpha1-certificateauthoritydatasourcekind"] +==== CertificateAuthorityDataSourceKind (string) + +CertificateAuthorityDataSourceKind enumerates the sources for CA Bundles. + +.Appears In: +**** +- xref:{anchor_prefix}-go-pinniped-dev-generated-1-27-apis-supervisor-idp-v1alpha1-certificateauthoritydatasourcespec[$$CertificateAuthorityDataSourceSpec$$] +**** + [id="{anchor_prefix}-go-pinniped-dev-generated-1-27-apis-supervisor-idp-v1alpha1-certificateauthoritydatasourcespec"] @@ -1693,7 +1713,7 @@ CertificateAuthorityDataSourceSpec provides a source for CA bundle used for clie [cols="25a,75a", options="header"] |=== | Field | Description -| *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + +| *`kind`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-27-apis-supervisor-idp-v1alpha1-certificateauthoritydatasourcekind[$$CertificateAuthorityDataSourceKind$$]__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + Allowed values are "Secret" or "ConfigMap". + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + diff --git a/generated/1.27/apis/concierge/authentication/v1alpha1/types_tls.go b/generated/1.27/apis/concierge/authentication/v1alpha1/types_tls.go index 85ea7a94c..3be891eda 100644 --- a/generated/1.27/apis/concierge/authentication/v1alpha1/types_tls.go +++ b/generated/1.27/apis/concierge/authentication/v1alpha1/types_tls.go @@ -23,7 +23,7 @@ type CertificateAuthorityDataSourceSpec struct { // Allowed values are "Secret" or "ConfigMap". // "ConfigMap" uses a Kubernetes configmap to source CA Bundles. // "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. - Kind string `json:"kind"` + Kind CertificateAuthorityDataSourceKind `json:"kind"` // Name is the resource name of the secret or configmap from which to read the CA bundle. // The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. // +kubebuilder:validation:MinLength=1 diff --git a/generated/1.27/apis/supervisor/idp/v1alpha1/types_tls.go b/generated/1.27/apis/supervisor/idp/v1alpha1/types_tls.go index 8c28d7505..831cd308c 100644 --- a/generated/1.27/apis/supervisor/idp/v1alpha1/types_tls.go +++ b/generated/1.27/apis/supervisor/idp/v1alpha1/types_tls.go @@ -23,7 +23,7 @@ type CertificateAuthorityDataSourceSpec struct { // Allowed values are "Secret" or "ConfigMap". // "ConfigMap" uses a Kubernetes configmap to source CA Bundles. // "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. - Kind string `json:"kind"` + Kind CertificateAuthorityDataSourceKind `json:"kind"` // Name is the resource name of the secret or configmap from which to read the CA bundle. // The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. // +kubebuilder:validation:MinLength=1 diff --git a/generated/1.27/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml b/generated/1.27/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml index 60f872205..f707ed4a8 100644 --- a/generated/1.27/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml +++ b/generated/1.27/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml @@ -113,6 +113,9 @@ spec: Allowed values are "Secret" or "ConfigMap". "ConfigMap" uses a Kubernetes configmap to source CA Bundles. "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + enum: + - Secret + - ConfigMap type: string name: description: |- diff --git a/generated/1.27/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml b/generated/1.27/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml index 8e5b7d601..7285fee36 100644 --- a/generated/1.27/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml +++ b/generated/1.27/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml @@ -84,6 +84,9 @@ spec: Allowed values are "Secret" or "ConfigMap". "ConfigMap" uses a Kubernetes configmap to source CA Bundles. "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + enum: + - Secret + - ConfigMap type: string name: description: |- diff --git a/generated/1.27/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml b/generated/1.27/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml index c608d26cb..a9f4ee414 100644 --- a/generated/1.27/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml +++ b/generated/1.27/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml @@ -188,6 +188,9 @@ spec: Allowed values are "Secret" or "ConfigMap". "ConfigMap" uses a Kubernetes configmap to source CA Bundles. "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + enum: + - Secret + - ConfigMap type: string name: description: |- diff --git a/generated/1.27/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml b/generated/1.27/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml index 56743cfce..d14c6773d 100644 --- a/generated/1.27/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml +++ b/generated/1.27/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml @@ -247,6 +247,9 @@ spec: Allowed values are "Secret" or "ConfigMap". "ConfigMap" uses a Kubernetes configmap to source CA Bundles. "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + enum: + - Secret + - ConfigMap type: string name: description: |- diff --git a/generated/1.27/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml b/generated/1.27/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml index e3ccac69a..463351de6 100644 --- a/generated/1.27/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml +++ b/generated/1.27/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml @@ -179,6 +179,9 @@ spec: Allowed values are "Secret" or "ConfigMap". "ConfigMap" uses a Kubernetes configmap to source CA Bundles. "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + enum: + - Secret + - ConfigMap type: string name: description: |- diff --git a/generated/1.27/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml b/generated/1.27/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml index 51f44856d..33e15403c 100644 --- a/generated/1.27/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml +++ b/generated/1.27/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml @@ -229,6 +229,9 @@ spec: Allowed values are "Secret" or "ConfigMap". "ConfigMap" uses a Kubernetes configmap to source CA Bundles. "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + enum: + - Secret + - ConfigMap type: string name: description: |- diff --git a/generated/1.28/README.adoc b/generated/1.28/README.adoc index f47b4fa8d..de0de6e43 100644 --- a/generated/1.28/README.adoc +++ b/generated/1.28/README.adoc @@ -23,6 +23,16 @@ Package v1alpha1 is the v1alpha1 version of the Pinniped concierge authenticatio +[id="{anchor_prefix}-go-pinniped-dev-generated-1-28-apis-concierge-authentication-v1alpha1-certificateauthoritydatasourcekind"] +==== CertificateAuthorityDataSourceKind (string) + +CertificateAuthorityDataSourceKind enumerates the sources for CA Bundles. + +.Appears In: +**** +- xref:{anchor_prefix}-go-pinniped-dev-generated-1-28-apis-concierge-authentication-v1alpha1-certificateauthoritydatasourcespec[$$CertificateAuthorityDataSourceSpec$$] +**** + [id="{anchor_prefix}-go-pinniped-dev-generated-1-28-apis-concierge-authentication-v1alpha1-certificateauthoritydatasourcespec"] @@ -38,7 +48,7 @@ CertificateAuthorityDataSourceSpec provides a source for CA bundle used for clie [cols="25a,75a", options="header"] |=== | Field | Description -| *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + +| *`kind`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-28-apis-concierge-authentication-v1alpha1-certificateauthoritydatasourcekind[$$CertificateAuthorityDataSourceKind$$]__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + Allowed values are "Secret" or "ConfigMap". + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + @@ -1678,6 +1688,16 @@ Optional, when empty this defaults to "objectGUID". + |=== +[id="{anchor_prefix}-go-pinniped-dev-generated-1-28-apis-supervisor-idp-v1alpha1-certificateauthoritydatasourcekind"] +==== CertificateAuthorityDataSourceKind (string) + +CertificateAuthorityDataSourceKind enumerates the sources for CA Bundles. + +.Appears In: +**** +- xref:{anchor_prefix}-go-pinniped-dev-generated-1-28-apis-supervisor-idp-v1alpha1-certificateauthoritydatasourcespec[$$CertificateAuthorityDataSourceSpec$$] +**** + [id="{anchor_prefix}-go-pinniped-dev-generated-1-28-apis-supervisor-idp-v1alpha1-certificateauthoritydatasourcespec"] @@ -1693,7 +1713,7 @@ CertificateAuthorityDataSourceSpec provides a source for CA bundle used for clie [cols="25a,75a", options="header"] |=== | Field | Description -| *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + +| *`kind`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-28-apis-supervisor-idp-v1alpha1-certificateauthoritydatasourcekind[$$CertificateAuthorityDataSourceKind$$]__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + Allowed values are "Secret" or "ConfigMap". + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + diff --git a/generated/1.28/apis/concierge/authentication/v1alpha1/types_tls.go b/generated/1.28/apis/concierge/authentication/v1alpha1/types_tls.go index 85ea7a94c..3be891eda 100644 --- a/generated/1.28/apis/concierge/authentication/v1alpha1/types_tls.go +++ b/generated/1.28/apis/concierge/authentication/v1alpha1/types_tls.go @@ -23,7 +23,7 @@ type CertificateAuthorityDataSourceSpec struct { // Allowed values are "Secret" or "ConfigMap". // "ConfigMap" uses a Kubernetes configmap to source CA Bundles. // "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. - Kind string `json:"kind"` + Kind CertificateAuthorityDataSourceKind `json:"kind"` // Name is the resource name of the secret or configmap from which to read the CA bundle. // The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. // +kubebuilder:validation:MinLength=1 diff --git a/generated/1.28/apis/supervisor/idp/v1alpha1/types_tls.go b/generated/1.28/apis/supervisor/idp/v1alpha1/types_tls.go index 8c28d7505..831cd308c 100644 --- a/generated/1.28/apis/supervisor/idp/v1alpha1/types_tls.go +++ b/generated/1.28/apis/supervisor/idp/v1alpha1/types_tls.go @@ -23,7 +23,7 @@ type CertificateAuthorityDataSourceSpec struct { // Allowed values are "Secret" or "ConfigMap". // "ConfigMap" uses a Kubernetes configmap to source CA Bundles. // "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. - Kind string `json:"kind"` + Kind CertificateAuthorityDataSourceKind `json:"kind"` // Name is the resource name of the secret or configmap from which to read the CA bundle. // The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. // +kubebuilder:validation:MinLength=1 diff --git a/generated/1.28/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml b/generated/1.28/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml index 60f872205..f707ed4a8 100644 --- a/generated/1.28/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml +++ b/generated/1.28/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml @@ -113,6 +113,9 @@ spec: Allowed values are "Secret" or "ConfigMap". "ConfigMap" uses a Kubernetes configmap to source CA Bundles. "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + enum: + - Secret + - ConfigMap type: string name: description: |- diff --git a/generated/1.28/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml b/generated/1.28/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml index 8e5b7d601..7285fee36 100644 --- a/generated/1.28/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml +++ b/generated/1.28/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml @@ -84,6 +84,9 @@ spec: Allowed values are "Secret" or "ConfigMap". "ConfigMap" uses a Kubernetes configmap to source CA Bundles. "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + enum: + - Secret + - ConfigMap type: string name: description: |- diff --git a/generated/1.28/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml b/generated/1.28/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml index c608d26cb..a9f4ee414 100644 --- a/generated/1.28/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml +++ b/generated/1.28/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml @@ -188,6 +188,9 @@ spec: Allowed values are "Secret" or "ConfigMap". "ConfigMap" uses a Kubernetes configmap to source CA Bundles. "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + enum: + - Secret + - ConfigMap type: string name: description: |- diff --git a/generated/1.28/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml b/generated/1.28/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml index 56743cfce..d14c6773d 100644 --- a/generated/1.28/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml +++ b/generated/1.28/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml @@ -247,6 +247,9 @@ spec: Allowed values are "Secret" or "ConfigMap". "ConfigMap" uses a Kubernetes configmap to source CA Bundles. "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + enum: + - Secret + - ConfigMap type: string name: description: |- diff --git a/generated/1.28/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml b/generated/1.28/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml index e3ccac69a..463351de6 100644 --- a/generated/1.28/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml +++ b/generated/1.28/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml @@ -179,6 +179,9 @@ spec: Allowed values are "Secret" or "ConfigMap". "ConfigMap" uses a Kubernetes configmap to source CA Bundles. "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + enum: + - Secret + - ConfigMap type: string name: description: |- diff --git a/generated/1.28/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml b/generated/1.28/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml index 51f44856d..33e15403c 100644 --- a/generated/1.28/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml +++ b/generated/1.28/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml @@ -229,6 +229,9 @@ spec: Allowed values are "Secret" or "ConfigMap". "ConfigMap" uses a Kubernetes configmap to source CA Bundles. "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + enum: + - Secret + - ConfigMap type: string name: description: |- diff --git a/generated/1.29/README.adoc b/generated/1.29/README.adoc index d60a6642e..4dc6f2bf7 100644 --- a/generated/1.29/README.adoc +++ b/generated/1.29/README.adoc @@ -23,6 +23,16 @@ Package v1alpha1 is the v1alpha1 version of the Pinniped concierge authenticatio +[id="{anchor_prefix}-go-pinniped-dev-generated-1-29-apis-concierge-authentication-v1alpha1-certificateauthoritydatasourcekind"] +==== CertificateAuthorityDataSourceKind (string) + +CertificateAuthorityDataSourceKind enumerates the sources for CA Bundles. + +.Appears In: +**** +- xref:{anchor_prefix}-go-pinniped-dev-generated-1-29-apis-concierge-authentication-v1alpha1-certificateauthoritydatasourcespec[$$CertificateAuthorityDataSourceSpec$$] +**** + [id="{anchor_prefix}-go-pinniped-dev-generated-1-29-apis-concierge-authentication-v1alpha1-certificateauthoritydatasourcespec"] @@ -38,7 +48,7 @@ CertificateAuthorityDataSourceSpec provides a source for CA bundle used for clie [cols="25a,75a", options="header"] |=== | Field | Description -| *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + +| *`kind`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-29-apis-concierge-authentication-v1alpha1-certificateauthoritydatasourcekind[$$CertificateAuthorityDataSourceKind$$]__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + Allowed values are "Secret" or "ConfigMap". + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + @@ -1678,6 +1688,16 @@ Optional, when empty this defaults to "objectGUID". + |=== +[id="{anchor_prefix}-go-pinniped-dev-generated-1-29-apis-supervisor-idp-v1alpha1-certificateauthoritydatasourcekind"] +==== CertificateAuthorityDataSourceKind (string) + +CertificateAuthorityDataSourceKind enumerates the sources for CA Bundles. + +.Appears In: +**** +- xref:{anchor_prefix}-go-pinniped-dev-generated-1-29-apis-supervisor-idp-v1alpha1-certificateauthoritydatasourcespec[$$CertificateAuthorityDataSourceSpec$$] +**** + [id="{anchor_prefix}-go-pinniped-dev-generated-1-29-apis-supervisor-idp-v1alpha1-certificateauthoritydatasourcespec"] @@ -1693,7 +1713,7 @@ CertificateAuthorityDataSourceSpec provides a source for CA bundle used for clie [cols="25a,75a", options="header"] |=== | Field | Description -| *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + +| *`kind`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-29-apis-supervisor-idp-v1alpha1-certificateauthoritydatasourcekind[$$CertificateAuthorityDataSourceKind$$]__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + Allowed values are "Secret" or "ConfigMap". + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + diff --git a/generated/1.29/apis/concierge/authentication/v1alpha1/types_tls.go b/generated/1.29/apis/concierge/authentication/v1alpha1/types_tls.go index 85ea7a94c..3be891eda 100644 --- a/generated/1.29/apis/concierge/authentication/v1alpha1/types_tls.go +++ b/generated/1.29/apis/concierge/authentication/v1alpha1/types_tls.go @@ -23,7 +23,7 @@ type CertificateAuthorityDataSourceSpec struct { // Allowed values are "Secret" or "ConfigMap". // "ConfigMap" uses a Kubernetes configmap to source CA Bundles. // "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. - Kind string `json:"kind"` + Kind CertificateAuthorityDataSourceKind `json:"kind"` // Name is the resource name of the secret or configmap from which to read the CA bundle. // The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. // +kubebuilder:validation:MinLength=1 diff --git a/generated/1.29/apis/supervisor/idp/v1alpha1/types_tls.go b/generated/1.29/apis/supervisor/idp/v1alpha1/types_tls.go index 8c28d7505..831cd308c 100644 --- a/generated/1.29/apis/supervisor/idp/v1alpha1/types_tls.go +++ b/generated/1.29/apis/supervisor/idp/v1alpha1/types_tls.go @@ -23,7 +23,7 @@ type CertificateAuthorityDataSourceSpec struct { // Allowed values are "Secret" or "ConfigMap". // "ConfigMap" uses a Kubernetes configmap to source CA Bundles. // "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. - Kind string `json:"kind"` + Kind CertificateAuthorityDataSourceKind `json:"kind"` // Name is the resource name of the secret or configmap from which to read the CA bundle. // The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. // +kubebuilder:validation:MinLength=1 diff --git a/generated/1.29/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml b/generated/1.29/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml index 60f872205..f707ed4a8 100644 --- a/generated/1.29/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml +++ b/generated/1.29/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml @@ -113,6 +113,9 @@ spec: Allowed values are "Secret" or "ConfigMap". "ConfigMap" uses a Kubernetes configmap to source CA Bundles. "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + enum: + - Secret + - ConfigMap type: string name: description: |- diff --git a/generated/1.29/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml b/generated/1.29/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml index 8e5b7d601..7285fee36 100644 --- a/generated/1.29/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml +++ b/generated/1.29/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml @@ -84,6 +84,9 @@ spec: Allowed values are "Secret" or "ConfigMap". "ConfigMap" uses a Kubernetes configmap to source CA Bundles. "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + enum: + - Secret + - ConfigMap type: string name: description: |- diff --git a/generated/1.29/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml b/generated/1.29/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml index c608d26cb..a9f4ee414 100644 --- a/generated/1.29/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml +++ b/generated/1.29/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml @@ -188,6 +188,9 @@ spec: Allowed values are "Secret" or "ConfigMap". "ConfigMap" uses a Kubernetes configmap to source CA Bundles. "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + enum: + - Secret + - ConfigMap type: string name: description: |- diff --git a/generated/1.29/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml b/generated/1.29/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml index 56743cfce..d14c6773d 100644 --- a/generated/1.29/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml +++ b/generated/1.29/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml @@ -247,6 +247,9 @@ spec: Allowed values are "Secret" or "ConfigMap". "ConfigMap" uses a Kubernetes configmap to source CA Bundles. "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + enum: + - Secret + - ConfigMap type: string name: description: |- diff --git a/generated/1.29/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml b/generated/1.29/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml index e3ccac69a..463351de6 100644 --- a/generated/1.29/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml +++ b/generated/1.29/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml @@ -179,6 +179,9 @@ spec: Allowed values are "Secret" or "ConfigMap". "ConfigMap" uses a Kubernetes configmap to source CA Bundles. "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + enum: + - Secret + - ConfigMap type: string name: description: |- diff --git a/generated/1.29/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml b/generated/1.29/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml index 51f44856d..33e15403c 100644 --- a/generated/1.29/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml +++ b/generated/1.29/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml @@ -229,6 +229,9 @@ spec: Allowed values are "Secret" or "ConfigMap". "ConfigMap" uses a Kubernetes configmap to source CA Bundles. "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + enum: + - Secret + - ConfigMap type: string name: description: |- diff --git a/generated/1.30/README.adoc b/generated/1.30/README.adoc index 3d317f0d3..81cc0a298 100644 --- a/generated/1.30/README.adoc +++ b/generated/1.30/README.adoc @@ -23,6 +23,16 @@ Package v1alpha1 is the v1alpha1 version of the Pinniped concierge authenticatio +[id="{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-concierge-authentication-v1alpha1-certificateauthoritydatasourcekind"] +==== CertificateAuthorityDataSourceKind (string) + +CertificateAuthorityDataSourceKind enumerates the sources for CA Bundles. + +.Appears In: +**** +- xref:{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-concierge-authentication-v1alpha1-certificateauthoritydatasourcespec[$$CertificateAuthorityDataSourceSpec$$] +**** + [id="{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-concierge-authentication-v1alpha1-certificateauthoritydatasourcespec"] @@ -38,7 +48,7 @@ CertificateAuthorityDataSourceSpec provides a source for CA bundle used for clie [cols="25a,75a", options="header"] |=== | Field | Description -| *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + +| *`kind`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-concierge-authentication-v1alpha1-certificateauthoritydatasourcekind[$$CertificateAuthorityDataSourceKind$$]__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + Allowed values are "Secret" or "ConfigMap". + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + @@ -1678,6 +1688,16 @@ Optional, when empty this defaults to "objectGUID". + |=== +[id="{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-supervisor-idp-v1alpha1-certificateauthoritydatasourcekind"] +==== CertificateAuthorityDataSourceKind (string) + +CertificateAuthorityDataSourceKind enumerates the sources for CA Bundles. + +.Appears In: +**** +- xref:{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-supervisor-idp-v1alpha1-certificateauthoritydatasourcespec[$$CertificateAuthorityDataSourceSpec$$] +**** + [id="{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-supervisor-idp-v1alpha1-certificateauthoritydatasourcespec"] @@ -1693,7 +1713,7 @@ CertificateAuthorityDataSourceSpec provides a source for CA bundle used for clie [cols="25a,75a", options="header"] |=== | Field | Description -| *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + +| *`kind`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-supervisor-idp-v1alpha1-certificateauthoritydatasourcekind[$$CertificateAuthorityDataSourceKind$$]__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + Allowed values are "Secret" or "ConfigMap". + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + diff --git a/generated/1.30/apis/concierge/authentication/v1alpha1/types_tls.go b/generated/1.30/apis/concierge/authentication/v1alpha1/types_tls.go index 85ea7a94c..3be891eda 100644 --- a/generated/1.30/apis/concierge/authentication/v1alpha1/types_tls.go +++ b/generated/1.30/apis/concierge/authentication/v1alpha1/types_tls.go @@ -23,7 +23,7 @@ type CertificateAuthorityDataSourceSpec struct { // Allowed values are "Secret" or "ConfigMap". // "ConfigMap" uses a Kubernetes configmap to source CA Bundles. // "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. - Kind string `json:"kind"` + Kind CertificateAuthorityDataSourceKind `json:"kind"` // Name is the resource name of the secret or configmap from which to read the CA bundle. // The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. // +kubebuilder:validation:MinLength=1 diff --git a/generated/1.30/apis/supervisor/idp/v1alpha1/types_tls.go b/generated/1.30/apis/supervisor/idp/v1alpha1/types_tls.go index 8c28d7505..831cd308c 100644 --- a/generated/1.30/apis/supervisor/idp/v1alpha1/types_tls.go +++ b/generated/1.30/apis/supervisor/idp/v1alpha1/types_tls.go @@ -23,7 +23,7 @@ type CertificateAuthorityDataSourceSpec struct { // Allowed values are "Secret" or "ConfigMap". // "ConfigMap" uses a Kubernetes configmap to source CA Bundles. // "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. - Kind string `json:"kind"` + Kind CertificateAuthorityDataSourceKind `json:"kind"` // Name is the resource name of the secret or configmap from which to read the CA bundle. // The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. // +kubebuilder:validation:MinLength=1 diff --git a/generated/1.30/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml b/generated/1.30/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml index 60f872205..f707ed4a8 100644 --- a/generated/1.30/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml +++ b/generated/1.30/crds/authentication.concierge.pinniped.dev_jwtauthenticators.yaml @@ -113,6 +113,9 @@ spec: Allowed values are "Secret" or "ConfigMap". "ConfigMap" uses a Kubernetes configmap to source CA Bundles. "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + enum: + - Secret + - ConfigMap type: string name: description: |- diff --git a/generated/1.30/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml b/generated/1.30/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml index 8e5b7d601..7285fee36 100644 --- a/generated/1.30/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml +++ b/generated/1.30/crds/authentication.concierge.pinniped.dev_webhookauthenticators.yaml @@ -84,6 +84,9 @@ spec: Allowed values are "Secret" or "ConfigMap". "ConfigMap" uses a Kubernetes configmap to source CA Bundles. "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + enum: + - Secret + - ConfigMap type: string name: description: |- diff --git a/generated/1.30/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml b/generated/1.30/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml index c608d26cb..a9f4ee414 100644 --- a/generated/1.30/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml +++ b/generated/1.30/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml @@ -188,6 +188,9 @@ spec: Allowed values are "Secret" or "ConfigMap". "ConfigMap" uses a Kubernetes configmap to source CA Bundles. "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + enum: + - Secret + - ConfigMap type: string name: description: |- diff --git a/generated/1.30/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml b/generated/1.30/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml index 56743cfce..d14c6773d 100644 --- a/generated/1.30/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml +++ b/generated/1.30/crds/idp.supervisor.pinniped.dev_githubidentityproviders.yaml @@ -247,6 +247,9 @@ spec: Allowed values are "Secret" or "ConfigMap". "ConfigMap" uses a Kubernetes configmap to source CA Bundles. "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + enum: + - Secret + - ConfigMap type: string name: description: |- diff --git a/generated/1.30/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml b/generated/1.30/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml index e3ccac69a..463351de6 100644 --- a/generated/1.30/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml +++ b/generated/1.30/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml @@ -179,6 +179,9 @@ spec: Allowed values are "Secret" or "ConfigMap". "ConfigMap" uses a Kubernetes configmap to source CA Bundles. "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + enum: + - Secret + - ConfigMap type: string name: description: |- diff --git a/generated/1.30/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml b/generated/1.30/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml index 51f44856d..33e15403c 100644 --- a/generated/1.30/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml +++ b/generated/1.30/crds/idp.supervisor.pinniped.dev_oidcidentityproviders.yaml @@ -229,6 +229,9 @@ spec: Allowed values are "Secret" or "ConfigMap". "ConfigMap" uses a Kubernetes configmap to source CA Bundles. "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + enum: + - Secret + - ConfigMap type: string name: description: |- diff --git a/generated/latest/README.adoc b/generated/latest/README.adoc index 3d317f0d3..81cc0a298 100644 --- a/generated/latest/README.adoc +++ b/generated/latest/README.adoc @@ -23,6 +23,16 @@ Package v1alpha1 is the v1alpha1 version of the Pinniped concierge authenticatio +[id="{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-concierge-authentication-v1alpha1-certificateauthoritydatasourcekind"] +==== CertificateAuthorityDataSourceKind (string) + +CertificateAuthorityDataSourceKind enumerates the sources for CA Bundles. + +.Appears In: +**** +- xref:{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-concierge-authentication-v1alpha1-certificateauthoritydatasourcespec[$$CertificateAuthorityDataSourceSpec$$] +**** + [id="{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-concierge-authentication-v1alpha1-certificateauthoritydatasourcespec"] @@ -38,7 +48,7 @@ CertificateAuthorityDataSourceSpec provides a source for CA bundle used for clie [cols="25a,75a", options="header"] |=== | Field | Description -| *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + +| *`kind`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-concierge-authentication-v1alpha1-certificateauthoritydatasourcekind[$$CertificateAuthorityDataSourceKind$$]__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + Allowed values are "Secret" or "ConfigMap". + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + @@ -1678,6 +1688,16 @@ Optional, when empty this defaults to "objectGUID". + |=== +[id="{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-supervisor-idp-v1alpha1-certificateauthoritydatasourcekind"] +==== CertificateAuthorityDataSourceKind (string) + +CertificateAuthorityDataSourceKind enumerates the sources for CA Bundles. + +.Appears In: +**** +- xref:{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-supervisor-idp-v1alpha1-certificateauthoritydatasourcespec[$$CertificateAuthorityDataSourceSpec$$] +**** + [id="{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-supervisor-idp-v1alpha1-certificateauthoritydatasourcespec"] @@ -1693,7 +1713,7 @@ CertificateAuthorityDataSourceSpec provides a source for CA bundle used for clie [cols="25a,75a", options="header"] |=== | Field | Description -| *`kind`* __string__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + +| *`kind`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-supervisor-idp-v1alpha1-certificateauthoritydatasourcekind[$$CertificateAuthorityDataSourceKind$$]__ | Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap. + Allowed values are "Secret" or "ConfigMap". + "ConfigMap" uses a Kubernetes configmap to source CA Bundles. + "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. + diff --git a/generated/latest/apis/concierge/authentication/v1alpha1/types_tls.go b/generated/latest/apis/concierge/authentication/v1alpha1/types_tls.go index 85ea7a94c..3be891eda 100644 --- a/generated/latest/apis/concierge/authentication/v1alpha1/types_tls.go +++ b/generated/latest/apis/concierge/authentication/v1alpha1/types_tls.go @@ -23,7 +23,7 @@ type CertificateAuthorityDataSourceSpec struct { // Allowed values are "Secret" or "ConfigMap". // "ConfigMap" uses a Kubernetes configmap to source CA Bundles. // "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. - Kind string `json:"kind"` + Kind CertificateAuthorityDataSourceKind `json:"kind"` // Name is the resource name of the secret or configmap from which to read the CA bundle. // The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed. // +kubebuilder:validation:MinLength=1 diff --git a/generated/latest/apis/supervisor/idp/v1alpha1/types_tls.go b/generated/latest/apis/supervisor/idp/v1alpha1/types_tls.go index 8c28d7505..831cd308c 100644 --- a/generated/latest/apis/supervisor/idp/v1alpha1/types_tls.go +++ b/generated/latest/apis/supervisor/idp/v1alpha1/types_tls.go @@ -23,7 +23,7 @@ type CertificateAuthorityDataSourceSpec struct { // Allowed values are "Secret" or "ConfigMap". // "ConfigMap" uses a Kubernetes configmap to source CA Bundles. // "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles. - Kind string `json:"kind"` + Kind CertificateAuthorityDataSourceKind `json:"kind"` // Name is the resource name of the secret or configmap from which to read the CA bundle. // The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed. // +kubebuilder:validation:MinLength=1 From 23fd15f840007712aa6d72b6ece260bea689f4a4 Mon Sep 17 00:00:00 2001 From: Ryan Richard Date: Mon, 5 Aug 2024 12:52:21 -0700 Subject: [PATCH 97/99] Revert "Add integration tests for tls spec validation in JWTAuthenticator and WebhookAuthenticator" This reverts commit c3405095b23db9aa4e64eb42829c48814954bab9. --- .../concierge_jwtauthenticator_status_test.go | 295 ------------------ ...cierge_webhookauthenticator_status_test.go | 220 ------------- 2 files changed, 515 deletions(-) diff --git a/test/integration/concierge_jwtauthenticator_status_test.go b/test/integration/concierge_jwtauthenticator_status_test.go index 327e85e87..bc2d502b9 100644 --- a/test/integration/concierge_jwtauthenticator_status_test.go +++ b/test/integration/concierge_jwtauthenticator_status_test.go @@ -164,10 +164,6 @@ func TestConciergeJWTAuthenticatorStatus_Parallel(t *testing.T) { unusedLocalhostPort := findRecentlyUnusedLocalhostPorts(t, 1)[0] - badCABundleConfigMap := testlib.CreateTestConfigMap(t, env.ConciergeNamespace, "ca-bundle", map[string]string{ - "ca.crt": "This is not a real CA bundle", - }) - tests := []struct { name string spec authenticationv1alpha1.JWTAuthenticatorSpec @@ -326,297 +322,6 @@ func TestConciergeJWTAuthenticatorStatus_Parallel(t *testing.T) { }, ), }, - { - name: "invalid when spec.tls supplies both certificateAuthorityData and certificateAuthorityDataSource", - spec: authenticationv1alpha1.JWTAuthenticatorSpec{ - Issuer: env.CLIUpstreamOIDC.Issuer, - Audience: "foo", - Claims: authenticationv1alpha1.JWTTokenClaims{ - Groups: "", - Username: "", - }, - TLS: &authenticationv1alpha1.TLSSpec{ - CertificateAuthorityData: "pretend-this-is-a-certificate", - CertificateAuthorityDataSource: &authenticationv1alpha1.CertificateAuthorityDataSourceSpec{ - Kind: "ConfigMap", - Name: "does-not-matter", - Key: "also-does-not-matter", - }, - }, - }, - wantConditions: []metav1.Condition{ - { - Type: "AuthenticatorValid", - Status: "Unknown", - Reason: "UnableToValidate", - Message: "unable to validate; see other conditions for details", - }, { - Type: "DiscoveryURLValid", - Status: "Unknown", - Reason: "UnableToValidate", - Message: "unable to validate; see other conditions for details", - }, { - Type: "IssuerURLValid", - Status: "True", - Reason: "Success", - Message: "issuer is a valid URL", - }, { - Type: "JWKSFetchValid", - Status: "Unknown", - Reason: "UnableToValidate", - Message: "unable to validate; see other conditions for details", - }, { - Type: "JWKSURLValid", - Status: "Unknown", - Reason: "UnableToValidate", - Message: "unable to validate; see other conditions for details", - }, { - Type: "Ready", - Status: "False", - Reason: "NotReady", - Message: "the JWTAuthenticator is not ready: see other conditions for details", - }, - { - Type: "TLSConfigurationValid", - Status: "False", - Reason: "InvalidTLSConfig", - Message: "spec.tls is invalid: both tls.certificateAuthorityDataSource and tls.certificateAuthorityData provided", - }, - }, - wantPhase: authenticationv1alpha1.JWTAuthenticatorPhaseError, - }, - { - name: "invalid when spec.tls.certificateAuthorityDataSource refers to a configmap that does not exist", - spec: authenticationv1alpha1.JWTAuthenticatorSpec{ - Issuer: env.CLIUpstreamOIDC.Issuer, - Audience: "foo", - Claims: authenticationv1alpha1.JWTTokenClaims{ - Groups: "", - Username: "", - }, - TLS: &authenticationv1alpha1.TLSSpec{ - CertificateAuthorityDataSource: &authenticationv1alpha1.CertificateAuthorityDataSourceSpec{ - Kind: "ConfigMap", - Name: "does-not-exist", - Key: "does-not-matter", - }, - }, - }, - wantConditions: []metav1.Condition{ - { - Type: "AuthenticatorValid", - Status: "Unknown", - Reason: "UnableToValidate", - Message: "unable to validate; see other conditions for details", - }, { - Type: "DiscoveryURLValid", - Status: "Unknown", - Reason: "UnableToValidate", - Message: "unable to validate; see other conditions for details", - }, { - Type: "IssuerURLValid", - Status: "True", - Reason: "Success", - Message: "issuer is a valid URL", - }, { - Type: "JWKSFetchValid", - Status: "Unknown", - Reason: "UnableToValidate", - Message: "unable to validate; see other conditions for details", - }, { - Type: "JWKSURLValid", - Status: "Unknown", - Reason: "UnableToValidate", - Message: "unable to validate; see other conditions for details", - }, { - Type: "Ready", - Status: "False", - Reason: "NotReady", - Message: "the JWTAuthenticator is not ready: see other conditions for details", - }, - { - Type: "TLSConfigurationValid", - Status: "False", - Reason: "InvalidTLSConfig", - Message: "spec.tls.certificateAuthorityDataSource is invalid: failed to get configmap \"concierge/does-not-exist\": configmap \"does-not-exist\" not found", - }, - }, - wantPhase: authenticationv1alpha1.JWTAuthenticatorPhaseError, - }, - { - name: "invalid when spec.tls.certificateAuthorityDataSource refers to a secret that does not exist", - spec: authenticationv1alpha1.JWTAuthenticatorSpec{ - Issuer: env.CLIUpstreamOIDC.Issuer, - Audience: "foo", - Claims: authenticationv1alpha1.JWTTokenClaims{ - Groups: "", - Username: "", - }, - TLS: &authenticationv1alpha1.TLSSpec{ - CertificateAuthorityDataSource: &authenticationv1alpha1.CertificateAuthorityDataSourceSpec{ - Kind: "Secret", - Name: "does-not-exist", - Key: "does-not-matter", - }, - }, - }, - wantConditions: []metav1.Condition{ - { - Type: "AuthenticatorValid", - Status: "Unknown", - Reason: "UnableToValidate", - Message: "unable to validate; see other conditions for details", - }, { - Type: "DiscoveryURLValid", - Status: "Unknown", - Reason: "UnableToValidate", - Message: "unable to validate; see other conditions for details", - }, { - Type: "IssuerURLValid", - Status: "True", - Reason: "Success", - Message: "issuer is a valid URL", - }, { - Type: "JWKSFetchValid", - Status: "Unknown", - Reason: "UnableToValidate", - Message: "unable to validate; see other conditions for details", - }, { - Type: "JWKSURLValid", - Status: "Unknown", - Reason: "UnableToValidate", - Message: "unable to validate; see other conditions for details", - }, { - Type: "Ready", - Status: "False", - Reason: "NotReady", - Message: "the JWTAuthenticator is not ready: see other conditions for details", - }, - { - Type: "TLSConfigurationValid", - Status: "False", - Reason: "InvalidTLSConfig", - Message: "spec.tls.certificateAuthorityDataSource is invalid: failed to get secret \"concierge/does-not-exist\": secret \"does-not-exist\" not found", - }, - }, - wantPhase: authenticationv1alpha1.JWTAuthenticatorPhaseError, - }, - { - name: "invalid when spec.tls.certificateAuthorityDataSource refers to a configmap that does not have valid PEM bytes", - spec: authenticationv1alpha1.JWTAuthenticatorSpec{ - Issuer: env.CLIUpstreamOIDC.Issuer, - Audience: "foo", - Claims: authenticationv1alpha1.JWTTokenClaims{ - Groups: "", - Username: "", - }, - TLS: &authenticationv1alpha1.TLSSpec{ - CertificateAuthorityDataSource: &authenticationv1alpha1.CertificateAuthorityDataSourceSpec{ - Kind: "ConfigMap", - Name: badCABundleConfigMap.Name, - Key: "ca.crt", - }, - }, - }, - wantConditions: []metav1.Condition{ - { - Type: "AuthenticatorValid", - Status: "Unknown", - Reason: "UnableToValidate", - Message: "unable to validate; see other conditions for details", - }, { - Type: "DiscoveryURLValid", - Status: "Unknown", - Reason: "UnableToValidate", - Message: "unable to validate; see other conditions for details", - }, { - Type: "IssuerURLValid", - Status: "True", - Reason: "Success", - Message: "issuer is a valid URL", - }, { - Type: "JWKSFetchValid", - Status: "Unknown", - Reason: "UnableToValidate", - Message: "unable to validate; see other conditions for details", - }, { - Type: "JWKSURLValid", - Status: "Unknown", - Reason: "UnableToValidate", - Message: "unable to validate; see other conditions for details", - }, { - Type: "Ready", - Status: "False", - Reason: "NotReady", - Message: "the JWTAuthenticator is not ready: see other conditions for details", - }, - { - Type: "TLSConfigurationValid", - Status: "False", - Reason: "InvalidTLSConfig", - Message: fmt.Sprintf("spec.tls.certificateAuthorityDataSource is invalid: key \"ca.crt\" with 28 bytes of data in configmap \"concierge/%s\" is not a PEM-encoded certificate (PEM certificates must begin with \"-----BEGIN CERTIFICATE-----\")", badCABundleConfigMap.Name), - }, - }, - wantPhase: authenticationv1alpha1.JWTAuthenticatorPhaseError, - }, - { - name: "invalid when spec.tls.certificateAuthorityDataSource refers to a key in a configmap that does not exist", - spec: authenticationv1alpha1.JWTAuthenticatorSpec{ - Issuer: env.CLIUpstreamOIDC.Issuer, - Audience: "foo", - Claims: authenticationv1alpha1.JWTTokenClaims{ - Groups: "", - Username: "", - }, - TLS: &authenticationv1alpha1.TLSSpec{ - CertificateAuthorityDataSource: &authenticationv1alpha1.CertificateAuthorityDataSourceSpec{ - Kind: "ConfigMap", - Name: badCABundleConfigMap.Name, - Key: "key-not-present", - }, - }, - }, - wantConditions: []metav1.Condition{ - { - Type: "AuthenticatorValid", - Status: "Unknown", - Reason: "UnableToValidate", - Message: "unable to validate; see other conditions for details", - }, { - Type: "DiscoveryURLValid", - Status: "Unknown", - Reason: "UnableToValidate", - Message: "unable to validate; see other conditions for details", - }, { - Type: "IssuerURLValid", - Status: "True", - Reason: "Success", - Message: "issuer is a valid URL", - }, { - Type: "JWKSFetchValid", - Status: "Unknown", - Reason: "UnableToValidate", - Message: "unable to validate; see other conditions for details", - }, { - Type: "JWKSURLValid", - Status: "Unknown", - Reason: "UnableToValidate", - Message: "unable to validate; see other conditions for details", - }, { - Type: "Ready", - Status: "False", - Reason: "NotReady", - Message: "the JWTAuthenticator is not ready: see other conditions for details", - }, - { - Type: "TLSConfigurationValid", - Status: "False", - Reason: "InvalidTLSConfig", - Message: fmt.Sprintf("spec.tls.certificateAuthorityDataSource is invalid: key \"key-not-present\" not found in configmap \"concierge/%s\"", badCABundleConfigMap.Name), - }, - }, - wantPhase: authenticationv1alpha1.JWTAuthenticatorPhaseError, - }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { diff --git a/test/integration/concierge_webhookauthenticator_status_test.go b/test/integration/concierge_webhookauthenticator_status_test.go index 8c8b3b515..c5a9dec3c 100644 --- a/test/integration/concierge_webhookauthenticator_status_test.go +++ b/test/integration/concierge_webhookauthenticator_status_test.go @@ -155,10 +155,6 @@ func TestConciergeWebhookAuthenticatorStatus_Parallel(t *testing.T) { caBundleSomePivotalCA := "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURVVENDQWptZ0F3SUJBZ0lWQUpzNStTbVRtaTJXeUI0bGJJRXBXaUs5a1RkUE1BMEdDU3FHU0liM0RRRUIKQ3dVQU1COHhDekFKQmdOVkJBWVRBbFZUTVJBd0RnWURWUVFLREFkUWFYWnZkR0ZzTUI0WERUSXdNRFV3TkRFMgpNamMxT0ZvWERUSTBNRFV3TlRFMk1qYzFPRm93SHpFTE1Ba0dBMVVFQmhNQ1ZWTXhFREFPQmdOVkJBb01CMUJwCmRtOTBZV3d3Z2dFaU1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQkR3QXdnZ0VLQW9JQkFRRERZWmZvWGR4Z2NXTEMKZEJtbHB5a0tBaG9JMlBuUWtsVFNXMno1cGcwaXJjOGFRL1E3MXZzMTRZYStmdWtFTGlvOTRZYWw4R01DdVFrbApMZ3AvUEE5N1VYelhQNDBpK25iNXcwRGpwWWd2dU9KQXJXMno2MFRnWE5NSFh3VHk4ME1SZEhpUFVWZ0VZd0JpCmtkNThzdEFVS1Y1MnBQTU1reTJjNy9BcFhJNmRXR2xjalUvaFBsNmtpRzZ5dEw2REtGYjJQRWV3MmdJM3pHZ2IKOFVVbnA1V05DZDd2WjNVY0ZHNXlsZEd3aGc3cnZ4U1ZLWi9WOEhCMGJmbjlxamlrSVcxWFM4dzdpUUNlQmdQMApYZWhKZmVITlZJaTJtZlczNlVQbWpMdnVKaGpqNDIrdFBQWndvdDkzdWtlcEgvbWpHcFJEVm9wamJyWGlpTUYrCkYxdnlPNGMxQWdNQkFBR2pnWU13Z1lBd0hRWURWUjBPQkJZRUZNTWJpSXFhdVkwajRVWWphWDl0bDJzby9LQ1IKTUI4R0ExVWRJd1FZTUJhQUZNTWJpSXFhdVkwajRVWWphWDl0bDJzby9LQ1JNQjBHQTFVZEpRUVdNQlFHQ0NzRwpBUVVGQndNQ0JnZ3JCZ0VGQlFjREFUQVBCZ05WSFJNQkFmOEVCVEFEQVFIL01BNEdBMVVkRHdFQi93UUVBd0lCCkJqQU5CZ2txaGtpRzl3MEJBUXNGQUFPQ0FRRUFYbEh4M2tIMDZwY2NDTDlEVE5qTnBCYnlVSytGd2R6T2IwWFYKcmpNaGtxdHVmdEpUUnR5T3hKZ0ZKNXhUR3pCdEtKamcrVU1pczBOV0t0VDBNWThVMU45U2c5SDl0RFpHRHBjVQpxMlVRU0Y4dXRQMVR3dnJIUzIrdzB2MUoxdHgrTEFiU0lmWmJCV0xXQ21EODUzRlVoWlFZekkvYXpFM28vd0p1CmlPUklMdUpNUk5vNlBXY3VLZmRFVkhaS1RTWnk3a25FcHNidGtsN3EwRE91eUFWdG9HVnlkb3VUR0FOdFhXK2YKczNUSTJjKzErZXg3L2RZOEJGQTFzNWFUOG5vZnU3T1RTTzdiS1kzSkRBUHZOeFQzKzVZUXJwNGR1Nmh0YUFMbAppOHNaRkhidmxpd2EzdlhxL3p1Y2JEaHEzQzBhZnAzV2ZwRGxwSlpvLy9QUUFKaTZLQT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K" - badCABundleConfigMap := testlib.CreateTestConfigMap(t, env.ConciergeNamespace, "ca-bundle", map[string]string{ - "ca.crt": "This is not a real CA bundle", - }) - tests := []struct { name string spec func() *authenticationv1alpha1.WebhookAuthenticatorSpec @@ -277,222 +273,6 @@ func TestConciergeWebhookAuthenticatorStatus_Parallel(t *testing.T) { }, ), }, - { - name: "invalid when spec.tls supplies both certificateAuthorityData and certificateAuthorityDataSource", - spec: func() *authenticationv1alpha1.WebhookAuthenticatorSpec { - webhookSpec := env.TestWebhook.DeepCopy() - webhookSpec.TLS = &authenticationv1alpha1.TLSSpec{ - CertificateAuthorityData: caBundleSomePivotalCA, - CertificateAuthorityDataSource: &authenticationv1alpha1.CertificateAuthorityDataSourceSpec{ - Kind: "ConfigMap", - Name: "does-not-matter", - Key: "also-does-not-matter", - }, - } - webhookSpec.Endpoint = "https://127.0.0.1:443/some-fake-endpoint" - return webhookSpec - }, - initialPhase: authenticationv1alpha1.WebhookAuthenticatorPhaseError, - finalConditions: replaceSomeConditions( - allSuccessfulWebhookAuthenticatorConditions(), - []metav1.Condition{ - { - Type: "Ready", - Status: "False", - Reason: "NotReady", - Message: "the WebhookAuthenticator is not ready: see other conditions for details", - }, { - Type: "AuthenticatorValid", - Status: "Unknown", - Reason: "UnableToValidate", - Message: "unable to validate; see other conditions for details", - }, { - Type: "WebhookConnectionValid", - Status: "Unknown", - Reason: "UnableToValidate", - Message: "unable to validate; see other conditions for details", - }, - { - Type: "TLSConfigurationValid", - Status: "False", - Reason: "InvalidTLSConfig", - Message: "spec.tls is invalid: both tls.certificateAuthorityDataSource and tls.certificateAuthorityData provided", - }, - }, - ), - }, - { - name: "invalid when spec.tls.certificateAuthorityDataSource refers to a configmap that does not exist", - spec: func() *authenticationv1alpha1.WebhookAuthenticatorSpec { - webhookSpec := env.TestWebhook.DeepCopy() - webhookSpec.TLS = &authenticationv1alpha1.TLSSpec{ - CertificateAuthorityDataSource: &authenticationv1alpha1.CertificateAuthorityDataSourceSpec{ - Kind: "ConfigMap", - Name: "does-not-exist", - Key: "does-not-matter", - }, - } - webhookSpec.Endpoint = "https://127.0.0.1:443/some-fake-endpoint" - return webhookSpec - }, - initialPhase: authenticationv1alpha1.WebhookAuthenticatorPhaseError, - finalConditions: replaceSomeConditions( - allSuccessfulWebhookAuthenticatorConditions(), - []metav1.Condition{ - { - Type: "Ready", - Status: "False", - Reason: "NotReady", - Message: "the WebhookAuthenticator is not ready: see other conditions for details", - }, { - Type: "AuthenticatorValid", - Status: "Unknown", - Reason: "UnableToValidate", - Message: "unable to validate; see other conditions for details", - }, { - Type: "WebhookConnectionValid", - Status: "Unknown", - Reason: "UnableToValidate", - Message: "unable to validate; see other conditions for details", - }, - { - Type: "TLSConfigurationValid", - Status: "False", - Reason: "InvalidTLSConfig", - Message: "spec.tls.certificateAuthorityDataSource is invalid: failed to get configmap \"concierge/does-not-exist\": configmap \"does-not-exist\" not found", - }, - }, - ), - }, - { - name: "invalid when spec.tls.certificateAuthorityDataSource refers to a secret that does not exist", - spec: func() *authenticationv1alpha1.WebhookAuthenticatorSpec { - webhookSpec := env.TestWebhook.DeepCopy() - webhookSpec.TLS = &authenticationv1alpha1.TLSSpec{ - CertificateAuthorityDataSource: &authenticationv1alpha1.CertificateAuthorityDataSourceSpec{ - Kind: "Secret", - Name: "does-not-exist", - Key: "does-not-matter", - }, - } - webhookSpec.Endpoint = "https://127.0.0.1:443/some-fake-endpoint" - return webhookSpec - }, - initialPhase: authenticationv1alpha1.WebhookAuthenticatorPhaseError, - finalConditions: replaceSomeConditions( - allSuccessfulWebhookAuthenticatorConditions(), - []metav1.Condition{ - { - Type: "Ready", - Status: "False", - Reason: "NotReady", - Message: "the WebhookAuthenticator is not ready: see other conditions for details", - }, { - Type: "AuthenticatorValid", - Status: "Unknown", - Reason: "UnableToValidate", - Message: "unable to validate; see other conditions for details", - }, { - Type: "WebhookConnectionValid", - Status: "Unknown", - Reason: "UnableToValidate", - Message: "unable to validate; see other conditions for details", - }, - { - Type: "TLSConfigurationValid", - Status: "False", - Reason: "InvalidTLSConfig", - Message: "spec.tls.certificateAuthorityDataSource is invalid: failed to get secret \"concierge/does-not-exist\": secret \"does-not-exist\" not found", - }, - }, - ), - }, - { - name: "invalid when spec.tls.certificateAuthorityDataSource refers to a configmap that does not have valid PEM bytes", - spec: func() *authenticationv1alpha1.WebhookAuthenticatorSpec { - webhookSpec := env.TestWebhook.DeepCopy() - webhookSpec.TLS = &authenticationv1alpha1.TLSSpec{ - CertificateAuthorityDataSource: &authenticationv1alpha1.CertificateAuthorityDataSourceSpec{ - Kind: "ConfigMap", - Name: badCABundleConfigMap.Name, - Key: "ca.crt", - }, - } - webhookSpec.Endpoint = "https://127.0.0.1:443/some-fake-endpoint" - return webhookSpec - }, - initialPhase: authenticationv1alpha1.WebhookAuthenticatorPhaseError, - finalConditions: replaceSomeConditions( - allSuccessfulWebhookAuthenticatorConditions(), - []metav1.Condition{ - { - Type: "Ready", - Status: "False", - Reason: "NotReady", - Message: "the WebhookAuthenticator is not ready: see other conditions for details", - }, { - Type: "AuthenticatorValid", - Status: "Unknown", - Reason: "UnableToValidate", - Message: "unable to validate; see other conditions for details", - }, { - Type: "WebhookConnectionValid", - Status: "Unknown", - Reason: "UnableToValidate", - Message: "unable to validate; see other conditions for details", - }, - { - Type: "TLSConfigurationValid", - Status: "False", - Reason: "InvalidTLSConfig", - Message: fmt.Sprintf("spec.tls.certificateAuthorityDataSource is invalid: key \"ca.crt\" with 28 bytes of data in configmap \"concierge/%s\" is not a PEM-encoded certificate (PEM certificates must begin with \"-----BEGIN CERTIFICATE-----\")", badCABundleConfigMap.Name), - }, - }, - ), - }, - { - name: "invalid when spec.tls.certificateAuthorityDataSource refers to a key in a configmap that does not exist", - spec: func() *authenticationv1alpha1.WebhookAuthenticatorSpec { - webhookSpec := env.TestWebhook.DeepCopy() - webhookSpec.TLS = &authenticationv1alpha1.TLSSpec{ - CertificateAuthorityDataSource: &authenticationv1alpha1.CertificateAuthorityDataSourceSpec{ - Kind: "ConfigMap", - Name: badCABundleConfigMap.Name, - Key: "key-not-present", - }, - } - webhookSpec.Endpoint = "https://127.0.0.1:443/some-fake-endpoint" - return webhookSpec - }, - initialPhase: authenticationv1alpha1.WebhookAuthenticatorPhaseError, - finalConditions: replaceSomeConditions( - allSuccessfulWebhookAuthenticatorConditions(), - []metav1.Condition{ - { - Type: "Ready", - Status: "False", - Reason: "NotReady", - Message: "the WebhookAuthenticator is not ready: see other conditions for details", - }, { - Type: "AuthenticatorValid", - Status: "Unknown", - Reason: "UnableToValidate", - Message: "unable to validate; see other conditions for details", - }, { - Type: "WebhookConnectionValid", - Status: "Unknown", - Reason: "UnableToValidate", - Message: "unable to validate; see other conditions for details", - }, - { - Type: "TLSConfigurationValid", - Status: "False", - Reason: "InvalidTLSConfig", - Message: fmt.Sprintf("spec.tls.certificateAuthorityDataSource is invalid: key \"key-not-present\" not found in configmap \"concierge/%s\"", badCABundleConfigMap.Name), - }, - }, - ), - }, } for _, test := range tests { tt := test From fdeca2c026dd1bb5b25796bd2661411b653fd6d4 Mon Sep 17 00:00:00 2001 From: Ryan Richard Date: Mon, 5 Aug 2024 12:52:29 -0700 Subject: [PATCH 98/99] Revert "add integration test for TLS config validation in OIDCIdentityProvider" This reverts commit 59402bca7b0253c9d1e68e5ba96d2cfa2354397a. --- test/integration/supervisor_upstream_test.go | 247 ------------------- 1 file changed, 247 deletions(-) diff --git a/test/integration/supervisor_upstream_test.go b/test/integration/supervisor_upstream_test.go index 47f18a127..c20da7301 100644 --- a/test/integration/supervisor_upstream_test.go +++ b/test/integration/supervisor_upstream_test.go @@ -5,7 +5,6 @@ package integration import ( "encoding/base64" - "fmt" "testing" "github.com/stretchr/testify/require" @@ -127,252 +126,6 @@ oidc: issuer did not match the issuer returned by provider, expected "` + env.Su expectedTLSConfigValidCondition(env.SupervisorUpstreamOIDC.CABundle != ""), }) }) - - t.Run("invalid when tlsSpec supplies both certificateAuthorityData and certificateAuthorityDataSource", func(t *testing.T) { - t.Parallel() - spec := idpv1alpha1.OIDCIdentityProviderSpec{ - Issuer: env.SupervisorUpstreamOIDC.Issuer, - TLS: &idpv1alpha1.TLSSpec{ - CertificateAuthorityData: base64.StdEncoding.EncodeToString([]byte(env.SupervisorUpstreamOIDC.CABundle)), - CertificateAuthorityDataSource: &idpv1alpha1.CertificateAuthorityDataSourceSpec{ - Kind: "ConfigMap", - Name: "does=not-matter", - Key: "also-does-not-matter", - }, - }, - AuthorizationConfig: idpv1alpha1.OIDCAuthorizationConfig{ - AdditionalScopes: []string{"email", "profile"}, - }, - Client: idpv1alpha1.OIDCClient{ - SecretName: testlib.CreateOIDCClientCredentialsSecret(t, "test-client-id", "test-client-secret").Name, - }, - } - upstream := testlib.CreateTestOIDCIdentityProvider(t, spec, idpv1alpha1.PhaseError) - expectUpstreamConditions(t, upstream, []metav1.Condition{ - { - Type: "ClientCredentialsSecretValid", - Status: "True", - Reason: "Success", - Message: "loaded client credentials", - }, - { - Type: "OIDCDiscoverySucceeded", - Status: "False", - Reason: "InvalidTLSConfig", - Message: "spec.tls is invalid: both tls.certificateAuthorityDataSource and tls.certificateAuthorityData provided", - }, - { - Type: "AdditionalAuthorizeParametersValid", - Status: "True", - Reason: "Success", - Message: "additionalAuthorizeParameters parameter names are allowed", - }, - { - Type: "TLSConfigurationValid", - Status: "False", - Reason: "InvalidTLSConfig", - Message: "spec.tls is invalid: both tls.certificateAuthorityDataSource and tls.certificateAuthorityData provided", - }, - }) - }) - - t.Run("invalid when spec.tls.certificateAuthorityDataSource refers to a configmap that does not exist", func(t *testing.T) { - t.Parallel() - spec := idpv1alpha1.OIDCIdentityProviderSpec{ - Issuer: env.SupervisorUpstreamOIDC.Issuer, - TLS: &idpv1alpha1.TLSSpec{ - CertificateAuthorityDataSource: &idpv1alpha1.CertificateAuthorityDataSourceSpec{ - Kind: "ConfigMap", - Name: "does=not-exist", - Key: "does-not-matter", - }, - }, - AuthorizationConfig: idpv1alpha1.OIDCAuthorizationConfig{ - AdditionalScopes: []string{"email", "profile"}, - }, - Client: idpv1alpha1.OIDCClient{ - SecretName: testlib.CreateOIDCClientCredentialsSecret(t, "test-client-id", "test-client-secret").Name, - }, - } - upstream := testlib.CreateTestOIDCIdentityProvider(t, spec, idpv1alpha1.PhaseError) - expectUpstreamConditions(t, upstream, []metav1.Condition{ - { - Type: "ClientCredentialsSecretValid", - Status: "True", - Reason: "Success", - Message: "loaded client credentials", - }, - { - Type: "OIDCDiscoverySucceeded", - Status: "False", - Reason: "InvalidTLSConfig", - Message: "spec.tls.certificateAuthorityDataSource is invalid: failed to get configmap \"supervisor/does=not-exist\": configmap \"does=not-exist\" not found", - }, - { - Type: "AdditionalAuthorizeParametersValid", - Status: "True", - Reason: "Success", - Message: "additionalAuthorizeParameters parameter names are allowed", - }, - { - Type: "TLSConfigurationValid", - Status: "False", - Reason: "InvalidTLSConfig", - Message: "spec.tls.certificateAuthorityDataSource is invalid: failed to get configmap \"supervisor/does=not-exist\": configmap \"does=not-exist\" not found", - }, - }) - }) - - t.Run("invalid when spec.tls.certificateAuthorityDataSource refers to a secret that does not exist", func(t *testing.T) { - t.Parallel() - spec := idpv1alpha1.OIDCIdentityProviderSpec{ - Issuer: env.SupervisorUpstreamOIDC.Issuer, - TLS: &idpv1alpha1.TLSSpec{ - CertificateAuthorityDataSource: &idpv1alpha1.CertificateAuthorityDataSourceSpec{ - Kind: "Secret", - Name: "does=not-exist", - Key: "does-not-matter", - }, - }, - AuthorizationConfig: idpv1alpha1.OIDCAuthorizationConfig{ - AdditionalScopes: []string{"email", "profile"}, - }, - Client: idpv1alpha1.OIDCClient{ - SecretName: testlib.CreateOIDCClientCredentialsSecret(t, "test-client-id", "test-client-secret").Name, - }, - } - upstream := testlib.CreateTestOIDCIdentityProvider(t, spec, idpv1alpha1.PhaseError) - expectUpstreamConditions(t, upstream, []metav1.Condition{ - { - Type: "ClientCredentialsSecretValid", - Status: "True", - Reason: "Success", - Message: "loaded client credentials", - }, - { - Type: "OIDCDiscoverySucceeded", - Status: "False", - Reason: "InvalidTLSConfig", - Message: "spec.tls.certificateAuthorityDataSource is invalid: failed to get secret \"supervisor/does=not-exist\": secret \"does=not-exist\" not found", - }, - { - Type: "AdditionalAuthorizeParametersValid", - Status: "True", - Reason: "Success", - Message: "additionalAuthorizeParameters parameter names are allowed", - }, - { - Type: "TLSConfigurationValid", - Status: "False", - Reason: "InvalidTLSConfig", - Message: "spec.tls.certificateAuthorityDataSource is invalid: failed to get secret \"supervisor/does=not-exist\": secret \"does=not-exist\" not found", - }, - }) - }) - - t.Run("invalid when spec.tls.certificateAuthorityDataSource refers to a configmap that does not have valid PEM bytes", func(t *testing.T) { - t.Parallel() - - badCABundleConfigMap := testlib.CreateTestConfigMap(t, env.SupervisorNamespace, "ca-bundle", map[string]string{ - "ca.crt": "This is not a real CA bundle", - }) - - spec := idpv1alpha1.OIDCIdentityProviderSpec{ - Issuer: env.SupervisorUpstreamOIDC.Issuer, - TLS: &idpv1alpha1.TLSSpec{ - CertificateAuthorityDataSource: &idpv1alpha1.CertificateAuthorityDataSourceSpec{ - Kind: "ConfigMap", - Name: badCABundleConfigMap.Name, - Key: "ca.crt", - }, - }, - AuthorizationConfig: idpv1alpha1.OIDCAuthorizationConfig{ - AdditionalScopes: []string{"email", "profile"}, - }, - Client: idpv1alpha1.OIDCClient{ - SecretName: testlib.CreateOIDCClientCredentialsSecret(t, "test-client-id", "test-client-secret").Name, - }, - } - upstream := testlib.CreateTestOIDCIdentityProvider(t, spec, idpv1alpha1.PhaseError) - expectUpstreamConditions(t, upstream, []metav1.Condition{ - { - Type: "ClientCredentialsSecretValid", - Status: "True", - Reason: "Success", - Message: "loaded client credentials", - }, - { - Type: "OIDCDiscoverySucceeded", - Status: "False", - Reason: "InvalidTLSConfig", - Message: fmt.Sprintf("spec.tls.certificateAuthorityDataSource is invalid: failed to get configmap \"supervisor/%s\": configmap \"%s\" not found", badCABundleConfigMap.Name, badCABundleConfigMap.Name), - }, - { - Type: "AdditionalAuthorizeParametersValid", - Status: "True", - Reason: "Success", - Message: "additionalAuthorizeParameters parameter names are allowed", - }, - { - Type: "TLSConfigurationValid", - Status: "False", - Reason: "InvalidTLSConfig", - Message: fmt.Sprintf("spec.tls.certificateAuthorityDataSource is invalid: failed to get configmap \"supervisor/%s\": configmap \"%s\" not found", badCABundleConfigMap.Name, badCABundleConfigMap.Name), - }, - }) - }) - - t.Run("invalid when spec.tls.certificateAuthorityDataSource refers to a key in a configmap that does not exist", func(t *testing.T) { - t.Parallel() - - badCABundleConfigMap := testlib.CreateTestConfigMap(t, env.SupervisorNamespace, "ca-bundle", map[string]string{ - "ca.crt": "This is not a real CA bundle", - }) - - spec := idpv1alpha1.OIDCIdentityProviderSpec{ - Issuer: env.SupervisorUpstreamOIDC.Issuer, - TLS: &idpv1alpha1.TLSSpec{ - CertificateAuthorityDataSource: &idpv1alpha1.CertificateAuthorityDataSourceSpec{ - Kind: "ConfigMap", - Name: badCABundleConfigMap.Name, - Key: "key-not-present", - }, - }, - AuthorizationConfig: idpv1alpha1.OIDCAuthorizationConfig{ - AdditionalScopes: []string{"email", "profile"}, - }, - Client: idpv1alpha1.OIDCClient{ - SecretName: testlib.CreateOIDCClientCredentialsSecret(t, "test-client-id", "test-client-secret").Name, - }, - } - upstream := testlib.CreateTestOIDCIdentityProvider(t, spec, idpv1alpha1.PhaseError) - expectUpstreamConditions(t, upstream, []metav1.Condition{ - { - Type: "ClientCredentialsSecretValid", - Status: "True", - Reason: "Success", - Message: "loaded client credentials", - }, - { - Type: "OIDCDiscoverySucceeded", - Status: "False", - Reason: "InvalidTLSConfig", - Message: fmt.Sprintf("spec.tls.certificateAuthorityDataSource is invalid: key \"key-not-present\" not found in configmap \"supervisor/%s\"", badCABundleConfigMap.Name), - }, - { - Type: "AdditionalAuthorizeParametersValid", - Status: "True", - Reason: "Success", - Message: "additionalAuthorizeParameters parameter names are allowed", - }, - { - Type: "TLSConfigurationValid", - Status: "False", - Reason: "InvalidTLSConfig", - Message: fmt.Sprintf("spec.tls.certificateAuthorityDataSource is invalid: key \"key-not-present\" not found in configmap \"supervisor/%s\"", badCABundleConfigMap.Name), - }, - }) - }) } func expectUpstreamConditions(t *testing.T, upstream *idpv1alpha1.OIDCIdentityProvider, expected []metav1.Condition) { From 2af510a3eeb6e9b50835bd5cf6506a6a3294bbac Mon Sep 17 00:00:00 2001 From: Ryan Richard Date: Mon, 5 Aug 2024 12:52:41 -0700 Subject: [PATCH 99/99] Revert "add integration test for TLS config validation in GitHubIdentityProvider" This reverts commit 23129da3e239ea1a47ca55df1bb0c8f9d99639a8. --- .../integration/supervisor_github_idp_test.go | 386 +----------------- 1 file changed, 2 insertions(+), 384 deletions(-) diff --git a/test/integration/supervisor_github_idp_test.go b/test/integration/supervisor_github_idp_test.go index ed0781668..3a135dbc1 100644 --- a/test/integration/supervisor_github_idp_test.go +++ b/test/integration/supervisor_github_idp_test.go @@ -328,15 +328,10 @@ func TestGitHubIDPSetsDefaultsWithKubectl_Parallel(t *testing.T) { func TestGitHubIDPPhaseAndConditions_Parallel(t *testing.T) { // These operations must be performed in the Supervisor's namespace so that the controller can find GitHubIdentityProvider - env := testlib.IntegrationEnv(t) - supervisorNamespace := env.SupervisorNamespace + supervisorNamespace := testlib.IntegrationEnv(t).SupervisorNamespace ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute) t.Cleanup(cancel) - badCABundleConfigMap := testlib.CreateTestConfigMap(t, supervisorNamespace, "ca-bundle", map[string]string{ - "ca.crt": "This is not a real CA bundle", - }) - kubernetesClient := testlib.NewKubernetesClientset(t) secretsClient := kubernetesClient.CoreV1().Secrets(supervisorNamespace) gitHubIDPClient := testlib.NewSupervisorClientset(t).IDPV1alpha1().GitHubIdentityProviders(supervisorNamespace) @@ -488,382 +483,6 @@ func TestGitHubIDPPhaseAndConditions_Parallel(t *testing.T) { }, }, }, - { - name: "invalid when spec.githubAPI.tls supplies both certificateAuthorityData and certificateAuthorityDataSource", - secrets: []*corev1.Secret{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: happySecretName, - }, - Type: "secrets.pinniped.dev/github-client", - Data: map[string][]byte{ - "clientID": []byte("foo"), - "clientSecret": []byte("bar"), - }, - }, - }, - idps: []*idpv1alpha1.GitHubIdentityProvider{ - { - Spec: idpv1alpha1.GitHubIdentityProviderSpec{ - GitHubAPI: idpv1alpha1.GitHubAPIConfig{ - TLS: &idpv1alpha1.TLSSpec{ - CertificateAuthorityData: "this is not a CA bundle", - CertificateAuthorityDataSource: &idpv1alpha1.CertificateAuthorityDataSourceSpec{ - Kind: "ConfigMap", - Name: "does-not-matter", - Key: "also-does-not-matter", - }, - }, - Host: ptr.To("github.com"), - }, - AllowAuthentication: idpv1alpha1.GitHubAllowAuthenticationSpec{ - Organizations: idpv1alpha1.GitHubOrganizationsSpec{ - Policy: ptr.To(idpv1alpha1.GitHubAllowedAuthOrganizationsPolicyAllGitHubUsers), - }, - }, - }, - }, - }, - wantPhase: idpv1alpha1.GitHubPhaseError, - wantConditions: []*metav1.Condition{ - { - Type: "ClaimsValid", - Status: metav1.ConditionTrue, - Reason: "Success", - Message: "spec.claims are valid", - }, - { - Type: "ClientCredentialsSecretValid", - Status: metav1.ConditionTrue, - Reason: "Success", - Message: fmt.Sprintf("clientID and clientSecret have been read from spec.client.SecretName (%q)", happySecretName), - }, - { - Type: "GitHubConnectionValid", - Status: "Unknown", - Reason: "UnableToValidate", - Message: "unable to validate; see other conditions for details", - }, - { - Type: "HostValid", - Status: metav1.ConditionTrue, - Reason: "Success", - Message: `spec.githubAPI.host ("github.com") is valid`, - }, - { - Type: "OrganizationsPolicyValid", - Status: metav1.ConditionTrue, - Reason: "Success", - Message: `spec.allowAuthentication.organizations.policy ("AllGitHubUsers") is valid`, - }, - { - Type: "TLSConfigurationValid", - Status: metav1.ConditionFalse, - Reason: "InvalidTLSConfig", - Message: "spec.githubAPI.tls is invalid: both tls.certificateAuthorityDataSource and tls.certificateAuthorityData provided", - }, - }, - }, - { - name: "invalid when spec.githubAPI.tls.certificateAuthorityDataSource refers to a configmap that does not exist", - secrets: []*corev1.Secret{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: happySecretName, - }, - Type: "secrets.pinniped.dev/github-client", - Data: map[string][]byte{ - "clientID": []byte("foo"), - "clientSecret": []byte("bar"), - }, - }, - }, - idps: []*idpv1alpha1.GitHubIdentityProvider{ - { - Spec: idpv1alpha1.GitHubIdentityProviderSpec{ - GitHubAPI: idpv1alpha1.GitHubAPIConfig{ - TLS: &idpv1alpha1.TLSSpec{ - CertificateAuthorityDataSource: &idpv1alpha1.CertificateAuthorityDataSourceSpec{ - Kind: "ConfigMap", - Name: "does-not-exist", - Key: "does-not-matter", - }, - }, - Host: ptr.To("github.com"), - }, - AllowAuthentication: idpv1alpha1.GitHubAllowAuthenticationSpec{ - Organizations: idpv1alpha1.GitHubOrganizationsSpec{ - Policy: ptr.To(idpv1alpha1.GitHubAllowedAuthOrganizationsPolicyAllGitHubUsers), - }, - }, - }, - }, - }, - wantPhase: idpv1alpha1.GitHubPhaseError, - wantConditions: []*metav1.Condition{ - { - Type: "ClaimsValid", - Status: metav1.ConditionTrue, - Reason: "Success", - Message: "spec.claims are valid", - }, - { - Type: "ClientCredentialsSecretValid", - Status: metav1.ConditionTrue, - Reason: "Success", - Message: fmt.Sprintf("clientID and clientSecret have been read from spec.client.SecretName (%q)", happySecretName), - }, - { - Type: "GitHubConnectionValid", - Status: "Unknown", - Reason: "UnableToValidate", - Message: "unable to validate; see other conditions for details", - }, - { - Type: "HostValid", - Status: metav1.ConditionTrue, - Reason: "Success", - Message: `spec.githubAPI.host ("github.com") is valid`, - }, - { - Type: "OrganizationsPolicyValid", - Status: metav1.ConditionTrue, - Reason: "Success", - Message: `spec.allowAuthentication.organizations.policy ("AllGitHubUsers") is valid`, - }, - { - Type: "TLSConfigurationValid", - Status: metav1.ConditionFalse, - Reason: "InvalidTLSConfig", - Message: "spec.githubAPI.tls.certificateAuthorityDataSource is invalid: failed to get configmap \"supervisor/does-not-exist\": configmap \"does-not-exist\" not found", - }, - }, - }, - { - name: "invalid when spec.githubAPI.tls.certificateAuthorityDataSource refers to a secret that does not exist", - secrets: []*corev1.Secret{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: happySecretName, - }, - Type: "secrets.pinniped.dev/github-client", - Data: map[string][]byte{ - "clientID": []byte("foo"), - "clientSecret": []byte("bar"), - }, - }, - }, - idps: []*idpv1alpha1.GitHubIdentityProvider{ - { - Spec: idpv1alpha1.GitHubIdentityProviderSpec{ - GitHubAPI: idpv1alpha1.GitHubAPIConfig{ - TLS: &idpv1alpha1.TLSSpec{ - CertificateAuthorityDataSource: &idpv1alpha1.CertificateAuthorityDataSourceSpec{ - Kind: "Secret", - Name: "does-not-exist", - Key: "does-not-matter", - }, - }, - Host: ptr.To("github.com"), - }, - AllowAuthentication: idpv1alpha1.GitHubAllowAuthenticationSpec{ - Organizations: idpv1alpha1.GitHubOrganizationsSpec{ - Policy: ptr.To(idpv1alpha1.GitHubAllowedAuthOrganizationsPolicyAllGitHubUsers), - }, - }, - }, - }, - }, - wantPhase: idpv1alpha1.GitHubPhaseError, - wantConditions: []*metav1.Condition{ - { - Type: "ClaimsValid", - Status: metav1.ConditionTrue, - Reason: "Success", - Message: "spec.claims are valid", - }, - { - Type: "ClientCredentialsSecretValid", - Status: metav1.ConditionTrue, - Reason: "Success", - Message: fmt.Sprintf("clientID and clientSecret have been read from spec.client.SecretName (%q)", happySecretName), - }, - { - Type: "GitHubConnectionValid", - Status: "Unknown", - Reason: "UnableToValidate", - Message: "unable to validate; see other conditions for details", - }, - { - Type: "HostValid", - Status: metav1.ConditionTrue, - Reason: "Success", - Message: `spec.githubAPI.host ("github.com") is valid`, - }, - { - Type: "OrganizationsPolicyValid", - Status: metav1.ConditionTrue, - Reason: "Success", - Message: `spec.allowAuthentication.organizations.policy ("AllGitHubUsers") is valid`, - }, - { - Type: "TLSConfigurationValid", - Status: metav1.ConditionFalse, - Reason: "InvalidTLSConfig", - Message: "spec.githubAPI.tls.certificateAuthorityDataSource is invalid: failed to get secret \"supervisor/does-not-exist\": secret \"does-not-exist\" not found", - }, - }, - }, - { - name: "invalid when spec.githubAPI.tls.certificateAuthorityDataSource refers to a configmap that does not have valid PEM bytes", - secrets: []*corev1.Secret{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: happySecretName, - }, - Type: "secrets.pinniped.dev/github-client", - Data: map[string][]byte{ - "clientID": []byte("foo"), - "clientSecret": []byte("bar"), - }, - }, - }, - idps: []*idpv1alpha1.GitHubIdentityProvider{ - { - Spec: idpv1alpha1.GitHubIdentityProviderSpec{ - GitHubAPI: idpv1alpha1.GitHubAPIConfig{ - TLS: &idpv1alpha1.TLSSpec{ - CertificateAuthorityDataSource: &idpv1alpha1.CertificateAuthorityDataSourceSpec{ - Kind: "ConfigMap", - Name: badCABundleConfigMap.Name, - Key: "ca.crt", - }, - }, - Host: ptr.To("github.com"), - }, - AllowAuthentication: idpv1alpha1.GitHubAllowAuthenticationSpec{ - Organizations: idpv1alpha1.GitHubOrganizationsSpec{ - Policy: ptr.To(idpv1alpha1.GitHubAllowedAuthOrganizationsPolicyAllGitHubUsers), - }, - }, - }, - }, - }, - wantPhase: idpv1alpha1.GitHubPhaseError, - wantConditions: []*metav1.Condition{ - { - Type: "ClaimsValid", - Status: metav1.ConditionTrue, - Reason: "Success", - Message: "spec.claims are valid", - }, - { - Type: "ClientCredentialsSecretValid", - Status: metav1.ConditionTrue, - Reason: "Success", - Message: fmt.Sprintf("clientID and clientSecret have been read from spec.client.SecretName (%q)", happySecretName), - }, - { - Type: "GitHubConnectionValid", - Status: "Unknown", - Reason: "UnableToValidate", - Message: "unable to validate; see other conditions for details", - }, - { - Type: "HostValid", - Status: metav1.ConditionTrue, - Reason: "Success", - Message: `spec.githubAPI.host ("github.com") is valid`, - }, - { - Type: "OrganizationsPolicyValid", - Status: metav1.ConditionTrue, - Reason: "Success", - Message: `spec.allowAuthentication.organizations.policy ("AllGitHubUsers") is valid`, - }, - { - Type: "TLSConfigurationValid", - Status: metav1.ConditionFalse, - Reason: "InvalidTLSConfig", - Message: fmt.Sprintf("spec.githubAPI.tls.certificateAuthorityDataSource is invalid: key \"ca.crt\" with 28 bytes of data in configmap \"supervisor/%s\" is not a PEM-encoded certificate (PEM certificates must begin with \"-----BEGIN CERTIFICATE-----\")", badCABundleConfigMap.Name), - }, - }, - }, - { - name: "invalid when spec.githubAPI.tls.certificateAuthorityDataSource refers to a key in a configmap that does not exist", - secrets: []*corev1.Secret{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: happySecretName, - }, - Type: "secrets.pinniped.dev/github-client", - Data: map[string][]byte{ - "clientID": []byte("foo"), - "clientSecret": []byte("bar"), - }, - }, - }, - idps: []*idpv1alpha1.GitHubIdentityProvider{ - { - Spec: idpv1alpha1.GitHubIdentityProviderSpec{ - GitHubAPI: idpv1alpha1.GitHubAPIConfig{ - TLS: &idpv1alpha1.TLSSpec{ - CertificateAuthorityDataSource: &idpv1alpha1.CertificateAuthorityDataSourceSpec{ - Kind: "ConfigMap", - Name: badCABundleConfigMap.Name, - Key: "key-not-present", - }, - }, - Host: ptr.To("github.com"), - }, - AllowAuthentication: idpv1alpha1.GitHubAllowAuthenticationSpec{ - Organizations: idpv1alpha1.GitHubOrganizationsSpec{ - Policy: ptr.To(idpv1alpha1.GitHubAllowedAuthOrganizationsPolicyAllGitHubUsers), - }, - }, - }, - }, - }, - wantPhase: idpv1alpha1.GitHubPhaseError, - wantConditions: []*metav1.Condition{ - { - Type: "ClaimsValid", - Status: metav1.ConditionTrue, - Reason: "Success", - Message: "spec.claims are valid", - }, - { - Type: "ClientCredentialsSecretValid", - Status: metav1.ConditionTrue, - Reason: "Success", - Message: fmt.Sprintf("clientID and clientSecret have been read from spec.client.SecretName (%q)", happySecretName), - }, - { - Type: "GitHubConnectionValid", - Status: "Unknown", - Reason: "UnableToValidate", - Message: "unable to validate; see other conditions for details", - }, - { - Type: "HostValid", - Status: metav1.ConditionTrue, - Reason: "Success", - Message: `spec.githubAPI.host ("github.com") is valid`, - }, - { - Type: "OrganizationsPolicyValid", - Status: metav1.ConditionTrue, - Reason: "Success", - Message: `spec.allowAuthentication.organizations.policy ("AllGitHubUsers") is valid`, - }, - { - Type: "TLSConfigurationValid", - Status: metav1.ConditionFalse, - Reason: "InvalidTLSConfig", - Message: fmt.Sprintf("spec.githubAPI.tls.certificateAuthorityDataSource is invalid: key \"key-not-present\" not found in configmap \"supervisor/%s\"", badCABundleConfigMap.Name), - }, - }, - }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -963,8 +582,7 @@ func TestGitHubIDPInWrongNamespace_Parallel(t *testing.T) { func TestGitHubIDPSecretInOtherNamespace_Parallel(t *testing.T) { // The GitHubIdentityProvider must be in the same namespace as the controller - env := testlib.IntegrationEnv(t) - supervisorNamespace := env.SupervisorNamespace + supervisorNamespace := testlib.IntegrationEnv(t).SupervisorNamespace ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute) t.Cleanup(cancel)