add claimValidationRules, userValidationRules, and claims.extra to JWTAuthenticator CRD

This commit is contained in:
Ryan Richard
2025-07-15 14:38:54 -07:00
parent fdfe2a3c9f
commit 2a83d00373
65 changed files with 5486 additions and 487 deletions

View File

@@ -18,7 +18,7 @@ const (
JWTAuthenticatorPhaseError JWTAuthenticatorPhase = "Error"
)
// Status of a JWT authenticator.
// JWTAuthenticatorStatus is the status of a JWT authenticator.
type JWTAuthenticatorStatus struct {
// Represents the observations of the authenticator's current state.
// +patchMergeKey=type
@@ -26,46 +26,168 @@ type JWTAuthenticatorStatus struct {
// +listType=map
// +listMapKey=type
Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"`
// Phase summarizes the overall status of the JWTAuthenticator.
// +kubebuilder:default=Pending
// +kubebuilder:validation:Enum=Pending;Ready;Error
Phase JWTAuthenticatorPhase `json:"phase,omitempty"`
}
// Spec for configuring a JWT authenticator.
// JWTAuthenticatorSpec is the spec for configuring a JWT authenticator.
type JWTAuthenticatorSpec struct {
// Issuer is the OIDC issuer URL that will be used to discover public signing keys. Issuer is
// issuer is the OIDC issuer URL that will be used to discover public signing keys. Issuer is
// also used to validate the "iss" JWT claim.
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:Pattern=`^https://`
Issuer string `json:"issuer"`
// Audience is the required value of the "aud" JWT claim.
// audience is the required value of the "aud" JWT claim.
// +kubebuilder:validation:MinLength=1
Audience string `json:"audience"`
// Claims allows customization of the claims that will be mapped to user identity
// claims allows customization of the claims that will be mapped to user identity
// for Kubernetes access.
// +optional
Claims JWTTokenClaims `json:"claims"`
// TLS configuration for communicating with the OIDC provider.
// claimValidationRules are rules that are applied to validate token claims to authenticate users.
// This is similar to claimValidationRules from Kubernetes AuthenticationConfiguration as documented in
// https://kubernetes.io/docs/reference/access-authn-authz/authentication.
// This is an advanced configuration option. During an end-user login flow, mistakes in this
// configuration will cause the user's login to fail.
// +optional
ClaimValidationRules []ClaimValidationRule `json:"claimValidationRules,omitempty"`
// userValidationRules are rules that are applied to final user before completing authentication.
// These allow invariants to be applied to incoming identities such as preventing the
// use of the system: prefix that is commonly used by Kubernetes components.
// The validation rules are logically ANDed together and must all return true for the validation to pass.
// This is similar to claimValidationRules from Kubernetes AuthenticationConfiguration as documented in
// https://kubernetes.io/docs/reference/access-authn-authz/authentication.
// This is an advanced configuration option. During an end-user login flow, mistakes in this
// configuration will cause the user's login to fail.
// +optional
UserValidationRules []UserValidationRule `json:"userValidationRules,omitempty"`
// tls is the configuration for communicating with the OIDC provider via TLS.
// +optional
TLS *TLSSpec `json:"tls,omitempty"`
}
// ClaimValidationRule provides the configuration for a single claim validation rule.
type ClaimValidationRule struct {
// claim is the name of a required claim.
// Same as --oidc-required-claim flag.
// Only string claim keys are supported.
// Mutually exclusive with expression and message.
// +optional
Claim string `json:"claim,omitempty"`
// requiredValue is the value of a required claim.
// Same as --oidc-required-claim flag.
// Only string claim values are supported.
// If claim is set and requiredValue is not set, the claim must be present with a value set to the empty string.
// Mutually exclusive with expression and message.
// +optional
RequiredValue string `json:"requiredValue,omitempty"`
// expression represents the expression which will be evaluated by CEL.
// Must produce a boolean.
//
// CEL expressions have access to the contents of the token claims, organized into CEL variable:
// - 'claims' is a map of claim names to claim values.
// For example, a variable named 'sub' can be accessed as 'claims.sub'.
// Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'.
// Must return true for the validation to pass.
//
// Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
//
// Mutually exclusive with claim and requiredValue.
// +optional
Expression string `json:"expression,omitempty"`
// message customizes the returned error message when expression returns false.
// message is a literal string.
// Mutually exclusive with claim and requiredValue.
// +optional
Message string `json:"message,omitempty"`
}
// UserValidationRule provides the configuration for a single user info validation rule.
type UserValidationRule struct {
// expression represents the expression which will be evaluated by CEL.
// Must return true for the validation to pass.
//
// CEL expressions have access to the contents of UserInfo, organized into CEL variable:
// - 'user' - authentication.k8s.io/v1, Kind=UserInfo object
// Refer to https://github.com/kubernetes/api/blob/release-1.28/authentication/v1/types.go#L105-L122 for the definition.
// API documentation: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#userinfo-v1-authentication-k8s-io
//
// Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
//
// +required
Expression string `json:"expression"`
// message customizes the returned error message when rule returns false.
// message is a literal string.
// +optional
Message string `json:"message,omitempty"`
}
// JWTTokenClaims allows customization of the claims that will be mapped to user identity
// for Kubernetes access.
type JWTTokenClaims struct {
// Groups is the name of the claim which should be read to extract the user's
// groups is the name of the claim which should be read to extract the user's
// group membership from the JWT token. When not specified, it will default to "groups".
// +optional
Groups string `json:"groups"`
// Username is the name of the claim which should be read to extract the
// username is the name of the claim which should be read to extract the
// username from the JWT token. When not specified, it will default to "username".
// +optional
Username string `json:"username"`
// extra is similar to claimMappings.extra from Kubernetes AuthenticationConfiguration as documented in
// https://kubernetes.io/docs/reference/access-authn-authz/authentication. However, note that the
// Pinniped Concierge issues client certificates to users for the purpose of authenticating, and
// the Kubernetes API server does not have any mechanism for transmitting auth extras via client
// certificates. When configured, these extras will appear in client certificates issued by the
// Pinniped Supervisor in the x509 Subject field as Organizational Units (OU). However, when this
// client certificate is presented to Kubernetes for authentication, Kubernetes will ignore these
// extras. This is probably only useful if you are using a custom authenticating proxy in front
// of your Kubernetes API server which can translate these OUs into auth extras, as described by
// https://kubernetes.io/docs/reference/access-authn-authz/authentication/#authenticating-proxy.
// This is an advanced configuration option. During an end-user login flow, each of these CEL expressions
// must evaluate to either a string or an array of strings, or else the user's login will fail.
// +optional
Extra []ExtraMapping `json:"extra,omitempty"`
}
// ExtraMapping provides the configuration for a single extra mapping.
type ExtraMapping struct {
// key is a string to use as the extra attribute key.
// key must be a domain-prefix path (e.g. example.org/foo). All characters before the first "/" must be a valid
// subdomain as defined by RFC 1123. All characters trailing the first "/" must
// be valid HTTP Path characters as defined by RFC 3986.
// key must be lowercase.
// Required to be unique.
// +required
Key string `json:"key"`
// valueExpression is a CEL expression to extract extra attribute value.
// valueExpression must produce a string or string array value.
// "", [], and null values are treated as the extra mapping not being present.
// Empty string values contained within a string array are filtered out.
//
// CEL expressions have access to the contents of the token claims, organized into CEL variable:
// - 'claims' is a map of claim names to claim values.
// For example, a variable named 'sub' can be accessed as 'claims.sub'.
// Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'.
//
// Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
//
// +required
ValueExpression string `json:"valueExpression"`
}
// JWTAuthenticator describes the configuration of a JWT authenticator.
@@ -86,14 +208,14 @@ type JWTAuthenticator struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
// Spec for configuring the authenticator.
// spec for configuring the authenticator.
Spec JWTAuthenticatorSpec `json:"spec"`
// Status of the authenticator.
// status of the authenticator.
Status JWTAuthenticatorStatus `json:"status,omitempty"`
}
// List of JWTAuthenticator objects.
// JWTAuthenticatorList is a list of JWTAuthenticator objects.
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type JWTAuthenticatorList struct {
metav1.TypeMeta `json:",inline"`

View File

@@ -58,37 +58,133 @@ spec:
metadata:
type: object
spec:
description: Spec for configuring the authenticator.
description: spec for configuring the authenticator.
properties:
audience:
description: Audience is the required value of the "aud" JWT claim.
description: audience is the required value of the "aud" JWT claim.
minLength: 1
type: string
claimValidationRules:
description: |-
claimValidationRules are rules that are applied to validate token claims to authenticate users.
This is similar to claimValidationRules from Kubernetes AuthenticationConfiguration as documented in
https://kubernetes.io/docs/reference/access-authn-authz/authentication.
This is an advanced configuration option. During an end-user login flow, mistakes in this
configuration will cause the user's login to fail.
items:
description: ClaimValidationRule provides the configuration for
a single claim validation rule.
properties:
claim:
description: |-
claim is the name of a required claim.
Same as --oidc-required-claim flag.
Only string claim keys are supported.
Mutually exclusive with expression and message.
type: string
expression:
description: |-
expression represents the expression which will be evaluated by CEL.
Must produce a boolean.
CEL expressions have access to the contents of the token claims, organized into CEL variable:
- 'claims' is a map of claim names to claim values.
For example, a variable named 'sub' can be accessed as 'claims.sub'.
Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'.
Must return true for the validation to pass.
Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
Mutually exclusive with claim and requiredValue.
type: string
message:
description: |-
message customizes the returned error message when expression returns false.
message is a literal string.
Mutually exclusive with claim and requiredValue.
type: string
requiredValue:
description: |-
requiredValue is the value of a required claim.
Same as --oidc-required-claim flag.
Only string claim values are supported.
If claim is set and requiredValue is not set, the claim must be present with a value set to the empty string.
Mutually exclusive with expression and message.
type: string
type: object
type: array
claims:
description: |-
Claims allows customization of the claims that will be mapped to user identity
claims allows customization of the claims that will be mapped to user identity
for Kubernetes access.
properties:
extra:
description: |-
extra is similar to claimMappings.extra from Kubernetes AuthenticationConfiguration as documented in
https://kubernetes.io/docs/reference/access-authn-authz/authentication. However, note that the
Pinniped Concierge issues client certificates to users for the purpose of authenticating, and
the Kubernetes API server does not have any mechanism for transmitting auth extras via client
certificates. When configured, these extras will appear in client certificates issued by the
Pinniped Supervisor in the x509 Subject field as Organizational Units (OU). However, when this
client certificate is presented to Kubernetes for authentication, Kubernetes will ignore these
extras. This is probably only useful if you are using a custom authenticating proxy in front
of your Kubernetes API server which can translate these OUs into auth extras, as described by
https://kubernetes.io/docs/reference/access-authn-authz/authentication/#authenticating-proxy.
This is an advanced configuration option. During an end-user login flow, each of these CEL expressions
must evaluate to either a string or an array of strings, or else the user's login will fail.
items:
description: ExtraMapping provides the configuration for a single
extra mapping.
properties:
key:
description: |-
key is a string to use as the extra attribute key.
key must be a domain-prefix path (e.g. example.org/foo). All characters before the first "/" must be a valid
subdomain as defined by RFC 1123. All characters trailing the first "/" must
be valid HTTP Path characters as defined by RFC 3986.
key must be lowercase.
Required to be unique.
type: string
valueExpression:
description: |-
valueExpression is a CEL expression to extract extra attribute value.
valueExpression must produce a string or string array value.
"", [], and null values are treated as the extra mapping not being present.
Empty string values contained within a string array are filtered out.
CEL expressions have access to the contents of the token claims, organized into CEL variable:
- 'claims' is a map of claim names to claim values.
For example, a variable named 'sub' can be accessed as 'claims.sub'.
Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'.
Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
type: string
required:
- key
- valueExpression
type: object
type: array
groups:
description: |-
Groups is the name of the claim which should be read to extract the user's
groups is the name of the claim which should be read to extract the user's
group membership from the JWT token. When not specified, it will default to "groups".
type: string
username:
description: |-
Username is the name of the claim which should be read to extract the
username is the name of the claim which should be read to extract the
username from the JWT token. When not specified, it will default to "username".
type: string
type: object
issuer:
description: |-
Issuer is the OIDC issuer URL that will be used to discover public signing keys. Issuer is
issuer is the OIDC issuer URL that will be used to discover public signing keys. Issuer is
also used to validate the "iss" JWT claim.
minLength: 1
pattern: ^https://
type: string
tls:
description: TLS configuration for communicating with the OIDC provider.
description: tls is the configuration for communicating with the OIDC
provider via TLS.
properties:
certificateAuthorityData:
description: X.509 Certificate Authority (base64-encoded PEM bundle).
@@ -128,12 +224,47 @@ spec:
- name
type: object
type: object
userValidationRules:
description: |-
userValidationRules are rules that are applied to final user before completing authentication.
These allow invariants to be applied to incoming identities such as preventing the
use of the system: prefix that is commonly used by Kubernetes components.
The validation rules are logically ANDed together and must all return true for the validation to pass.
This is similar to claimValidationRules from Kubernetes AuthenticationConfiguration as documented in
https://kubernetes.io/docs/reference/access-authn-authz/authentication.
This is an advanced configuration option. During an end-user login flow, mistakes in this
configuration will cause the user's login to fail.
items:
description: UserValidationRule provides the configuration for a
single user info validation rule.
properties:
expression:
description: |-
expression represents the expression which will be evaluated by CEL.
Must return true for the validation to pass.
CEL expressions have access to the contents of UserInfo, organized into CEL variable:
- 'user' - authentication.k8s.io/v1, Kind=UserInfo object
Refer to https://github.com/kubernetes/api/blob/release-1.28/authentication/v1/types.go#L105-L122 for the definition.
API documentation: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#userinfo-v1-authentication-k8s-io
Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
type: string
message:
description: |-
message customizes the returned error message when rule returns false.
message is a literal string.
type: string
required:
- expression
type: object
type: array
required:
- audience
- issuer
type: object
status:
description: Status of the authenticator.
description: status of the authenticator.
properties:
conditions:
description: Represents the observations of the authenticator's current

View File

@@ -60,6 +60,79 @@ certificate bundle. +
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-26-apis-concierge-authentication-v1alpha1-claimvalidationrule"]
==== ClaimValidationRule
ClaimValidationRule provides the configuration for a single claim validation rule.
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-26-apis-concierge-authentication-v1alpha1-jwtauthenticatorspec[$$JWTAuthenticatorSpec$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`claim`* __string__ | claim is the name of a required claim. +
Same as --oidc-required-claim flag. +
Only string claim keys are supported. +
Mutually exclusive with expression and message. +
| *`requiredValue`* __string__ | requiredValue is the value of a required claim. +
Same as --oidc-required-claim flag. +
Only string claim values are supported. +
If claim is set and requiredValue is not set, the claim must be present with a value set to the empty string. +
Mutually exclusive with expression and message. +
| *`expression`* __string__ | expression represents the expression which will be evaluated by CEL. +
Must produce a boolean. +
CEL expressions have access to the contents of the token claims, organized into CEL variable: +
- 'claims' is a map of claim names to claim values. +
For example, a variable named 'sub' can be accessed as 'claims.sub'. +
Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'. +
Must return true for the validation to pass. +
Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ +
Mutually exclusive with claim and requiredValue. +
| *`message`* __string__ | message customizes the returned error message when expression returns false. +
message is a literal string. +
Mutually exclusive with claim and requiredValue. +
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-26-apis-concierge-authentication-v1alpha1-extramapping"]
==== ExtraMapping
ExtraMapping provides the configuration for a single extra mapping.
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-26-apis-concierge-authentication-v1alpha1-jwttokenclaims[$$JWTTokenClaims$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`key`* __string__ | key is a string to use as the extra attribute key. +
key must be a domain-prefix path (e.g. example.org/foo). All characters before the first "/" must be a valid +
subdomain as defined by RFC 1123. All characters trailing the first "/" must +
be valid HTTP Path characters as defined by RFC 3986. +
key must be lowercase. +
Required to be unique. +
| *`valueExpression`* __string__ | valueExpression is a CEL expression to extract extra attribute value. +
valueExpression must produce a string or string array value. +
"", [], and null values are treated as the extra mapping not being present. +
Empty string values contained within a string array are filtered out. +
CEL expressions have access to the contents of the token claims, organized into CEL variable: +
- 'claims' is a map of claim names to claim values. +
For example, a variable named 'sub' can be accessed as 'claims.sub'. +
Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'. +
Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ +
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-26-apis-concierge-authentication-v1alpha1-jwtauthenticator"]
==== JWTAuthenticator
@@ -78,8 +151,8 @@ signature, existence of claims, etc.) and extract the username and groups from t
| Field | Description
| *`metadata`* __link:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#objectmeta-v1-meta[$$ObjectMeta$$]__ | Refer to Kubernetes API documentation for fields of `metadata`.
| *`spec`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-26-apis-concierge-authentication-v1alpha1-jwtauthenticatorspec[$$JWTAuthenticatorSpec$$]__ | Spec for configuring the authenticator. +
| *`status`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-26-apis-concierge-authentication-v1alpha1-jwtauthenticatorstatus[$$JWTAuthenticatorStatus$$]__ | Status of the authenticator. +
| *`spec`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-26-apis-concierge-authentication-v1alpha1-jwtauthenticatorspec[$$JWTAuthenticatorSpec$$]__ | spec for configuring the authenticator. +
| *`status`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-26-apis-concierge-authentication-v1alpha1-jwtauthenticatorstatus[$$JWTAuthenticatorStatus$$]__ | status of the authenticator. +
|===
@@ -100,7 +173,7 @@ signature, existence of claims, etc.) and extract the username and groups from t
[id="{anchor_prefix}-go-pinniped-dev-generated-1-26-apis-concierge-authentication-v1alpha1-jwtauthenticatorspec"]
==== JWTAuthenticatorSpec
Spec for configuring a JWT authenticator.
JWTAuthenticatorSpec is the spec for configuring a JWT authenticator.
.Appears In:
****
@@ -110,19 +183,32 @@ Spec for configuring a JWT authenticator.
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`issuer`* __string__ | Issuer is the OIDC issuer URL that will be used to discover public signing keys. Issuer is +
| *`issuer`* __string__ | issuer is the OIDC issuer URL that will be used to discover public signing keys. Issuer is +
also used to validate the "iss" JWT claim. +
| *`audience`* __string__ | Audience is the required value of the "aud" JWT claim. +
| *`claims`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-26-apis-concierge-authentication-v1alpha1-jwttokenclaims[$$JWTTokenClaims$$]__ | Claims allows customization of the claims that will be mapped to user identity +
| *`audience`* __string__ | audience is the required value of the "aud" JWT claim. +
| *`claims`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-26-apis-concierge-authentication-v1alpha1-jwttokenclaims[$$JWTTokenClaims$$]__ | claims allows customization of the claims that will be mapped to user identity +
for Kubernetes access. +
| *`tls`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-26-apis-concierge-authentication-v1alpha1-tlsspec[$$TLSSpec$$]__ | TLS configuration for communicating with the OIDC provider. +
| *`claimValidationRules`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-26-apis-concierge-authentication-v1alpha1-claimvalidationrule[$$ClaimValidationRule$$] array__ | claimValidationRules are rules that are applied to validate token claims to authenticate users. +
This is similar to claimValidationRules from Kubernetes AuthenticationConfiguration as documented in +
https://kubernetes.io/docs/reference/access-authn-authz/authentication. +
This is an advanced configuration option. During an end-user login flow, mistakes in this +
configuration will cause the user's login to fail. +
| *`userValidationRules`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-26-apis-concierge-authentication-v1alpha1-uservalidationrule[$$UserValidationRule$$] array__ | userValidationRules are rules that are applied to final user before completing authentication. +
These allow invariants to be applied to incoming identities such as preventing the +
use of the system: prefix that is commonly used by Kubernetes components. +
The validation rules are logically ANDed together and must all return true for the validation to pass. +
This is similar to claimValidationRules from Kubernetes AuthenticationConfiguration as documented in +
https://kubernetes.io/docs/reference/access-authn-authz/authentication. +
This is an advanced configuration option. During an end-user login flow, mistakes in this +
configuration will cause the user's login to fail. +
| *`tls`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-26-apis-concierge-authentication-v1alpha1-tlsspec[$$TLSSpec$$]__ | tls is the configuration for communicating with the OIDC provider via TLS. +
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-26-apis-concierge-authentication-v1alpha1-jwtauthenticatorstatus"]
==== JWTAuthenticatorStatus
Status of a JWT authenticator.
JWTAuthenticatorStatus is the status of a JWT authenticator.
.Appears In:
****
@@ -151,10 +237,22 @@ for Kubernetes access.
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`groups`* __string__ | Groups is the name of the claim which should be read to extract the user's +
| *`groups`* __string__ | groups is the name of the claim which should be read to extract the user's +
group membership from the JWT token. When not specified, it will default to "groups". +
| *`username`* __string__ | Username is the name of the claim which should be read to extract the +
| *`username`* __string__ | username is the name of the claim which should be read to extract the +
username from the JWT token. When not specified, it will default to "username". +
| *`extra`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-26-apis-concierge-authentication-v1alpha1-extramapping[$$ExtraMapping$$] array__ | extra is similar to claimMappings.extra from Kubernetes AuthenticationConfiguration as documented in +
https://kubernetes.io/docs/reference/access-authn-authz/authentication. However, note that the +
Pinniped Concierge issues client certificates to users for the purpose of authenticating, and +
the Kubernetes API server does not have any mechanism for transmitting auth extras via client +
certificates. When configured, these extras will appear in client certificates issued by the +
Pinniped Supervisor in the x509 Subject field as Organizational Units (OU). However, when this +
client certificate is presented to Kubernetes for authentication, Kubernetes will ignore these +
extras. This is probably only useful if you are using a custom authenticating proxy in front +
of your Kubernetes API server which can translate these OUs into auth extras, as described by +
https://kubernetes.io/docs/reference/access-authn-authz/authentication/#authenticating-proxy. +
This is an advanced configuration option. During an end-user login flow, each of these CEL expressions +
must evaluate to either a string or an array of strings, or else the user's login will fail. +
|===
@@ -178,6 +276,33 @@ Any changes to the CA bundle in the secret or configmap will be dynamically relo
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-26-apis-concierge-authentication-v1alpha1-uservalidationrule"]
==== UserValidationRule
UserValidationRule provides the configuration for a single user info validation rule.
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-26-apis-concierge-authentication-v1alpha1-jwtauthenticatorspec[$$JWTAuthenticatorSpec$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`expression`* __string__ | expression represents the expression which will be evaluated by CEL. +
Must return true for the validation to pass. +
CEL expressions have access to the contents of UserInfo, organized into CEL variable: +
- 'user' - authentication.k8s.io/v1, Kind=UserInfo object +
Refer to https://github.com/kubernetes/api/blob/release-1.28/authentication/v1/types.go#L105-L122 for the definition. +
API documentation: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#userinfo-v1-authentication-k8s-io +
Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ +
| *`message`* __string__ | message customizes the returned error message when rule returns false. +
message is a literal string. +
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-26-apis-concierge-authentication-v1alpha1-webhookauthenticator"]
==== WebhookAuthenticator

View File

@@ -18,7 +18,7 @@ const (
JWTAuthenticatorPhaseError JWTAuthenticatorPhase = "Error"
)
// Status of a JWT authenticator.
// JWTAuthenticatorStatus is the status of a JWT authenticator.
type JWTAuthenticatorStatus struct {
// Represents the observations of the authenticator's current state.
// +patchMergeKey=type
@@ -26,46 +26,168 @@ type JWTAuthenticatorStatus struct {
// +listType=map
// +listMapKey=type
Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"`
// Phase summarizes the overall status of the JWTAuthenticator.
// +kubebuilder:default=Pending
// +kubebuilder:validation:Enum=Pending;Ready;Error
Phase JWTAuthenticatorPhase `json:"phase,omitempty"`
}
// Spec for configuring a JWT authenticator.
// JWTAuthenticatorSpec is the spec for configuring a JWT authenticator.
type JWTAuthenticatorSpec struct {
// Issuer is the OIDC issuer URL that will be used to discover public signing keys. Issuer is
// issuer is the OIDC issuer URL that will be used to discover public signing keys. Issuer is
// also used to validate the "iss" JWT claim.
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:Pattern=`^https://`
Issuer string `json:"issuer"`
// Audience is the required value of the "aud" JWT claim.
// audience is the required value of the "aud" JWT claim.
// +kubebuilder:validation:MinLength=1
Audience string `json:"audience"`
// Claims allows customization of the claims that will be mapped to user identity
// claims allows customization of the claims that will be mapped to user identity
// for Kubernetes access.
// +optional
Claims JWTTokenClaims `json:"claims"`
// TLS configuration for communicating with the OIDC provider.
// claimValidationRules are rules that are applied to validate token claims to authenticate users.
// This is similar to claimValidationRules from Kubernetes AuthenticationConfiguration as documented in
// https://kubernetes.io/docs/reference/access-authn-authz/authentication.
// This is an advanced configuration option. During an end-user login flow, mistakes in this
// configuration will cause the user's login to fail.
// +optional
ClaimValidationRules []ClaimValidationRule `json:"claimValidationRules,omitempty"`
// userValidationRules are rules that are applied to final user before completing authentication.
// These allow invariants to be applied to incoming identities such as preventing the
// use of the system: prefix that is commonly used by Kubernetes components.
// The validation rules are logically ANDed together and must all return true for the validation to pass.
// This is similar to claimValidationRules from Kubernetes AuthenticationConfiguration as documented in
// https://kubernetes.io/docs/reference/access-authn-authz/authentication.
// This is an advanced configuration option. During an end-user login flow, mistakes in this
// configuration will cause the user's login to fail.
// +optional
UserValidationRules []UserValidationRule `json:"userValidationRules,omitempty"`
// tls is the configuration for communicating with the OIDC provider via TLS.
// +optional
TLS *TLSSpec `json:"tls,omitempty"`
}
// ClaimValidationRule provides the configuration for a single claim validation rule.
type ClaimValidationRule struct {
// claim is the name of a required claim.
// Same as --oidc-required-claim flag.
// Only string claim keys are supported.
// Mutually exclusive with expression and message.
// +optional
Claim string `json:"claim,omitempty"`
// requiredValue is the value of a required claim.
// Same as --oidc-required-claim flag.
// Only string claim values are supported.
// If claim is set and requiredValue is not set, the claim must be present with a value set to the empty string.
// Mutually exclusive with expression and message.
// +optional
RequiredValue string `json:"requiredValue,omitempty"`
// expression represents the expression which will be evaluated by CEL.
// Must produce a boolean.
//
// CEL expressions have access to the contents of the token claims, organized into CEL variable:
// - 'claims' is a map of claim names to claim values.
// For example, a variable named 'sub' can be accessed as 'claims.sub'.
// Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'.
// Must return true for the validation to pass.
//
// Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
//
// Mutually exclusive with claim and requiredValue.
// +optional
Expression string `json:"expression,omitempty"`
// message customizes the returned error message when expression returns false.
// message is a literal string.
// Mutually exclusive with claim and requiredValue.
// +optional
Message string `json:"message,omitempty"`
}
// UserValidationRule provides the configuration for a single user info validation rule.
type UserValidationRule struct {
// expression represents the expression which will be evaluated by CEL.
// Must return true for the validation to pass.
//
// CEL expressions have access to the contents of UserInfo, organized into CEL variable:
// - 'user' - authentication.k8s.io/v1, Kind=UserInfo object
// Refer to https://github.com/kubernetes/api/blob/release-1.28/authentication/v1/types.go#L105-L122 for the definition.
// API documentation: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#userinfo-v1-authentication-k8s-io
//
// Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
//
// +required
Expression string `json:"expression"`
// message customizes the returned error message when rule returns false.
// message is a literal string.
// +optional
Message string `json:"message,omitempty"`
}
// JWTTokenClaims allows customization of the claims that will be mapped to user identity
// for Kubernetes access.
type JWTTokenClaims struct {
// Groups is the name of the claim which should be read to extract the user's
// groups is the name of the claim which should be read to extract the user's
// group membership from the JWT token. When not specified, it will default to "groups".
// +optional
Groups string `json:"groups"`
// Username is the name of the claim which should be read to extract the
// username is the name of the claim which should be read to extract the
// username from the JWT token. When not specified, it will default to "username".
// +optional
Username string `json:"username"`
// extra is similar to claimMappings.extra from Kubernetes AuthenticationConfiguration as documented in
// https://kubernetes.io/docs/reference/access-authn-authz/authentication. However, note that the
// Pinniped Concierge issues client certificates to users for the purpose of authenticating, and
// the Kubernetes API server does not have any mechanism for transmitting auth extras via client
// certificates. When configured, these extras will appear in client certificates issued by the
// Pinniped Supervisor in the x509 Subject field as Organizational Units (OU). However, when this
// client certificate is presented to Kubernetes for authentication, Kubernetes will ignore these
// extras. This is probably only useful if you are using a custom authenticating proxy in front
// of your Kubernetes API server which can translate these OUs into auth extras, as described by
// https://kubernetes.io/docs/reference/access-authn-authz/authentication/#authenticating-proxy.
// This is an advanced configuration option. During an end-user login flow, each of these CEL expressions
// must evaluate to either a string or an array of strings, or else the user's login will fail.
// +optional
Extra []ExtraMapping `json:"extra,omitempty"`
}
// ExtraMapping provides the configuration for a single extra mapping.
type ExtraMapping struct {
// key is a string to use as the extra attribute key.
// key must be a domain-prefix path (e.g. example.org/foo). All characters before the first "/" must be a valid
// subdomain as defined by RFC 1123. All characters trailing the first "/" must
// be valid HTTP Path characters as defined by RFC 3986.
// key must be lowercase.
// Required to be unique.
// +required
Key string `json:"key"`
// valueExpression is a CEL expression to extract extra attribute value.
// valueExpression must produce a string or string array value.
// "", [], and null values are treated as the extra mapping not being present.
// Empty string values contained within a string array are filtered out.
//
// CEL expressions have access to the contents of the token claims, organized into CEL variable:
// - 'claims' is a map of claim names to claim values.
// For example, a variable named 'sub' can be accessed as 'claims.sub'.
// Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'.
//
// Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
//
// +required
ValueExpression string `json:"valueExpression"`
}
// JWTAuthenticator describes the configuration of a JWT authenticator.
@@ -86,14 +208,14 @@ type JWTAuthenticator struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
// Spec for configuring the authenticator.
// spec for configuring the authenticator.
Spec JWTAuthenticatorSpec `json:"spec"`
// Status of the authenticator.
// status of the authenticator.
Status JWTAuthenticatorStatus `json:"status,omitempty"`
}
// List of JWTAuthenticator objects.
// JWTAuthenticatorList is a list of JWTAuthenticator objects.
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type JWTAuthenticatorList struct {
metav1.TypeMeta `json:",inline"`

View File

@@ -29,6 +29,38 @@ func (in *CertificateAuthorityDataSourceSpec) DeepCopy() *CertificateAuthorityDa
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ClaimValidationRule) DeepCopyInto(out *ClaimValidationRule) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClaimValidationRule.
func (in *ClaimValidationRule) DeepCopy() *ClaimValidationRule {
if in == nil {
return nil
}
out := new(ClaimValidationRule)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ExtraMapping) DeepCopyInto(out *ExtraMapping) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExtraMapping.
func (in *ExtraMapping) DeepCopy() *ExtraMapping {
if in == nil {
return nil
}
out := new(ExtraMapping)
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
@@ -93,7 +125,17 @@ func (in *JWTAuthenticatorList) DeepCopyObject() runtime.Object {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *JWTAuthenticatorSpec) DeepCopyInto(out *JWTAuthenticatorSpec) {
*out = *in
out.Claims = in.Claims
in.Claims.DeepCopyInto(&out.Claims)
if in.ClaimValidationRules != nil {
in, out := &in.ClaimValidationRules, &out.ClaimValidationRules
*out = make([]ClaimValidationRule, len(*in))
copy(*out, *in)
}
if in.UserValidationRules != nil {
in, out := &in.UserValidationRules, &out.UserValidationRules
*out = make([]UserValidationRule, len(*in))
copy(*out, *in)
}
if in.TLS != nil {
in, out := &in.TLS, &out.TLS
*out = new(TLSSpec)
@@ -138,6 +180,11 @@ func (in *JWTAuthenticatorStatus) DeepCopy() *JWTAuthenticatorStatus {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *JWTTokenClaims) DeepCopyInto(out *JWTTokenClaims) {
*out = *in
if in.Extra != nil {
in, out := &in.Extra, &out.Extra
*out = make([]ExtraMapping, len(*in))
copy(*out, *in)
}
return
}
@@ -172,6 +219,22 @@ func (in *TLSSpec) DeepCopy() *TLSSpec {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *UserValidationRule) DeepCopyInto(out *UserValidationRule) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UserValidationRule.
func (in *UserValidationRule) DeepCopy() *UserValidationRule {
if in == nil {
return nil
}
out := new(UserValidationRule)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *WebhookAuthenticator) DeepCopyInto(out *WebhookAuthenticator) {
*out = *in

View File

@@ -58,37 +58,133 @@ spec:
metadata:
type: object
spec:
description: Spec for configuring the authenticator.
description: spec for configuring the authenticator.
properties:
audience:
description: Audience is the required value of the "aud" JWT claim.
description: audience is the required value of the "aud" JWT claim.
minLength: 1
type: string
claimValidationRules:
description: |-
claimValidationRules are rules that are applied to validate token claims to authenticate users.
This is similar to claimValidationRules from Kubernetes AuthenticationConfiguration as documented in
https://kubernetes.io/docs/reference/access-authn-authz/authentication.
This is an advanced configuration option. During an end-user login flow, mistakes in this
configuration will cause the user's login to fail.
items:
description: ClaimValidationRule provides the configuration for
a single claim validation rule.
properties:
claim:
description: |-
claim is the name of a required claim.
Same as --oidc-required-claim flag.
Only string claim keys are supported.
Mutually exclusive with expression and message.
type: string
expression:
description: |-
expression represents the expression which will be evaluated by CEL.
Must produce a boolean.
CEL expressions have access to the contents of the token claims, organized into CEL variable:
- 'claims' is a map of claim names to claim values.
For example, a variable named 'sub' can be accessed as 'claims.sub'.
Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'.
Must return true for the validation to pass.
Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
Mutually exclusive with claim and requiredValue.
type: string
message:
description: |-
message customizes the returned error message when expression returns false.
message is a literal string.
Mutually exclusive with claim and requiredValue.
type: string
requiredValue:
description: |-
requiredValue is the value of a required claim.
Same as --oidc-required-claim flag.
Only string claim values are supported.
If claim is set and requiredValue is not set, the claim must be present with a value set to the empty string.
Mutually exclusive with expression and message.
type: string
type: object
type: array
claims:
description: |-
Claims allows customization of the claims that will be mapped to user identity
claims allows customization of the claims that will be mapped to user identity
for Kubernetes access.
properties:
extra:
description: |-
extra is similar to claimMappings.extra from Kubernetes AuthenticationConfiguration as documented in
https://kubernetes.io/docs/reference/access-authn-authz/authentication. However, note that the
Pinniped Concierge issues client certificates to users for the purpose of authenticating, and
the Kubernetes API server does not have any mechanism for transmitting auth extras via client
certificates. When configured, these extras will appear in client certificates issued by the
Pinniped Supervisor in the x509 Subject field as Organizational Units (OU). However, when this
client certificate is presented to Kubernetes for authentication, Kubernetes will ignore these
extras. This is probably only useful if you are using a custom authenticating proxy in front
of your Kubernetes API server which can translate these OUs into auth extras, as described by
https://kubernetes.io/docs/reference/access-authn-authz/authentication/#authenticating-proxy.
This is an advanced configuration option. During an end-user login flow, each of these CEL expressions
must evaluate to either a string or an array of strings, or else the user's login will fail.
items:
description: ExtraMapping provides the configuration for a single
extra mapping.
properties:
key:
description: |-
key is a string to use as the extra attribute key.
key must be a domain-prefix path (e.g. example.org/foo). All characters before the first "/" must be a valid
subdomain as defined by RFC 1123. All characters trailing the first "/" must
be valid HTTP Path characters as defined by RFC 3986.
key must be lowercase.
Required to be unique.
type: string
valueExpression:
description: |-
valueExpression is a CEL expression to extract extra attribute value.
valueExpression must produce a string or string array value.
"", [], and null values are treated as the extra mapping not being present.
Empty string values contained within a string array are filtered out.
CEL expressions have access to the contents of the token claims, organized into CEL variable:
- 'claims' is a map of claim names to claim values.
For example, a variable named 'sub' can be accessed as 'claims.sub'.
Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'.
Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
type: string
required:
- key
- valueExpression
type: object
type: array
groups:
description: |-
Groups is the name of the claim which should be read to extract the user's
groups is the name of the claim which should be read to extract the user's
group membership from the JWT token. When not specified, it will default to "groups".
type: string
username:
description: |-
Username is the name of the claim which should be read to extract the
username is the name of the claim which should be read to extract the
username from the JWT token. When not specified, it will default to "username".
type: string
type: object
issuer:
description: |-
Issuer is the OIDC issuer URL that will be used to discover public signing keys. Issuer is
issuer is the OIDC issuer URL that will be used to discover public signing keys. Issuer is
also used to validate the "iss" JWT claim.
minLength: 1
pattern: ^https://
type: string
tls:
description: TLS configuration for communicating with the OIDC provider.
description: tls is the configuration for communicating with the OIDC
provider via TLS.
properties:
certificateAuthorityData:
description: X.509 Certificate Authority (base64-encoded PEM bundle).
@@ -128,12 +224,47 @@ spec:
- name
type: object
type: object
userValidationRules:
description: |-
userValidationRules are rules that are applied to final user before completing authentication.
These allow invariants to be applied to incoming identities such as preventing the
use of the system: prefix that is commonly used by Kubernetes components.
The validation rules are logically ANDed together and must all return true for the validation to pass.
This is similar to claimValidationRules from Kubernetes AuthenticationConfiguration as documented in
https://kubernetes.io/docs/reference/access-authn-authz/authentication.
This is an advanced configuration option. During an end-user login flow, mistakes in this
configuration will cause the user's login to fail.
items:
description: UserValidationRule provides the configuration for a
single user info validation rule.
properties:
expression:
description: |-
expression represents the expression which will be evaluated by CEL.
Must return true for the validation to pass.
CEL expressions have access to the contents of UserInfo, organized into CEL variable:
- 'user' - authentication.k8s.io/v1, Kind=UserInfo object
Refer to https://github.com/kubernetes/api/blob/release-1.28/authentication/v1/types.go#L105-L122 for the definition.
API documentation: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#userinfo-v1-authentication-k8s-io
Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
type: string
message:
description: |-
message customizes the returned error message when rule returns false.
message is a literal string.
type: string
required:
- expression
type: object
type: array
required:
- audience
- issuer
type: object
status:
description: Status of the authenticator.
description: status of the authenticator.
properties:
conditions:
description: Represents the observations of the authenticator's current

View File

@@ -60,6 +60,79 @@ certificate bundle. +
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-27-apis-concierge-authentication-v1alpha1-claimvalidationrule"]
==== ClaimValidationRule
ClaimValidationRule provides the configuration for a single claim validation rule.
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-27-apis-concierge-authentication-v1alpha1-jwtauthenticatorspec[$$JWTAuthenticatorSpec$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`claim`* __string__ | claim is the name of a required claim. +
Same as --oidc-required-claim flag. +
Only string claim keys are supported. +
Mutually exclusive with expression and message. +
| *`requiredValue`* __string__ | requiredValue is the value of a required claim. +
Same as --oidc-required-claim flag. +
Only string claim values are supported. +
If claim is set and requiredValue is not set, the claim must be present with a value set to the empty string. +
Mutually exclusive with expression and message. +
| *`expression`* __string__ | expression represents the expression which will be evaluated by CEL. +
Must produce a boolean. +
CEL expressions have access to the contents of the token claims, organized into CEL variable: +
- 'claims' is a map of claim names to claim values. +
For example, a variable named 'sub' can be accessed as 'claims.sub'. +
Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'. +
Must return true for the validation to pass. +
Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ +
Mutually exclusive with claim and requiredValue. +
| *`message`* __string__ | message customizes the returned error message when expression returns false. +
message is a literal string. +
Mutually exclusive with claim and requiredValue. +
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-27-apis-concierge-authentication-v1alpha1-extramapping"]
==== ExtraMapping
ExtraMapping provides the configuration for a single extra mapping.
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-27-apis-concierge-authentication-v1alpha1-jwttokenclaims[$$JWTTokenClaims$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`key`* __string__ | key is a string to use as the extra attribute key. +
key must be a domain-prefix path (e.g. example.org/foo). All characters before the first "/" must be a valid +
subdomain as defined by RFC 1123. All characters trailing the first "/" must +
be valid HTTP Path characters as defined by RFC 3986. +
key must be lowercase. +
Required to be unique. +
| *`valueExpression`* __string__ | valueExpression is a CEL expression to extract extra attribute value. +
valueExpression must produce a string or string array value. +
"", [], and null values are treated as the extra mapping not being present. +
Empty string values contained within a string array are filtered out. +
CEL expressions have access to the contents of the token claims, organized into CEL variable: +
- 'claims' is a map of claim names to claim values. +
For example, a variable named 'sub' can be accessed as 'claims.sub'. +
Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'. +
Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ +
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-27-apis-concierge-authentication-v1alpha1-jwtauthenticator"]
==== JWTAuthenticator
@@ -78,8 +151,8 @@ signature, existence of claims, etc.) and extract the username and groups from t
| Field | Description
| *`metadata`* __link:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#objectmeta-v1-meta[$$ObjectMeta$$]__ | Refer to Kubernetes API documentation for fields of `metadata`.
| *`spec`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-27-apis-concierge-authentication-v1alpha1-jwtauthenticatorspec[$$JWTAuthenticatorSpec$$]__ | Spec for configuring the authenticator. +
| *`status`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-27-apis-concierge-authentication-v1alpha1-jwtauthenticatorstatus[$$JWTAuthenticatorStatus$$]__ | Status of the authenticator. +
| *`spec`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-27-apis-concierge-authentication-v1alpha1-jwtauthenticatorspec[$$JWTAuthenticatorSpec$$]__ | spec for configuring the authenticator. +
| *`status`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-27-apis-concierge-authentication-v1alpha1-jwtauthenticatorstatus[$$JWTAuthenticatorStatus$$]__ | status of the authenticator. +
|===
@@ -100,7 +173,7 @@ signature, existence of claims, etc.) and extract the username and groups from t
[id="{anchor_prefix}-go-pinniped-dev-generated-1-27-apis-concierge-authentication-v1alpha1-jwtauthenticatorspec"]
==== JWTAuthenticatorSpec
Spec for configuring a JWT authenticator.
JWTAuthenticatorSpec is the spec for configuring a JWT authenticator.
.Appears In:
****
@@ -110,19 +183,32 @@ Spec for configuring a JWT authenticator.
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`issuer`* __string__ | Issuer is the OIDC issuer URL that will be used to discover public signing keys. Issuer is +
| *`issuer`* __string__ | issuer is the OIDC issuer URL that will be used to discover public signing keys. Issuer is +
also used to validate the "iss" JWT claim. +
| *`audience`* __string__ | Audience is the required value of the "aud" JWT claim. +
| *`claims`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-27-apis-concierge-authentication-v1alpha1-jwttokenclaims[$$JWTTokenClaims$$]__ | Claims allows customization of the claims that will be mapped to user identity +
| *`audience`* __string__ | audience is the required value of the "aud" JWT claim. +
| *`claims`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-27-apis-concierge-authentication-v1alpha1-jwttokenclaims[$$JWTTokenClaims$$]__ | claims allows customization of the claims that will be mapped to user identity +
for Kubernetes access. +
| *`tls`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-27-apis-concierge-authentication-v1alpha1-tlsspec[$$TLSSpec$$]__ | TLS configuration for communicating with the OIDC provider. +
| *`claimValidationRules`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-27-apis-concierge-authentication-v1alpha1-claimvalidationrule[$$ClaimValidationRule$$] array__ | claimValidationRules are rules that are applied to validate token claims to authenticate users. +
This is similar to claimValidationRules from Kubernetes AuthenticationConfiguration as documented in +
https://kubernetes.io/docs/reference/access-authn-authz/authentication. +
This is an advanced configuration option. During an end-user login flow, mistakes in this +
configuration will cause the user's login to fail. +
| *`userValidationRules`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-27-apis-concierge-authentication-v1alpha1-uservalidationrule[$$UserValidationRule$$] array__ | userValidationRules are rules that are applied to final user before completing authentication. +
These allow invariants to be applied to incoming identities such as preventing the +
use of the system: prefix that is commonly used by Kubernetes components. +
The validation rules are logically ANDed together and must all return true for the validation to pass. +
This is similar to claimValidationRules from Kubernetes AuthenticationConfiguration as documented in +
https://kubernetes.io/docs/reference/access-authn-authz/authentication. +
This is an advanced configuration option. During an end-user login flow, mistakes in this +
configuration will cause the user's login to fail. +
| *`tls`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-27-apis-concierge-authentication-v1alpha1-tlsspec[$$TLSSpec$$]__ | tls is the configuration for communicating with the OIDC provider via TLS. +
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-27-apis-concierge-authentication-v1alpha1-jwtauthenticatorstatus"]
==== JWTAuthenticatorStatus
Status of a JWT authenticator.
JWTAuthenticatorStatus is the status of a JWT authenticator.
.Appears In:
****
@@ -151,10 +237,22 @@ for Kubernetes access.
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`groups`* __string__ | Groups is the name of the claim which should be read to extract the user's +
| *`groups`* __string__ | groups is the name of the claim which should be read to extract the user's +
group membership from the JWT token. When not specified, it will default to "groups". +
| *`username`* __string__ | Username is the name of the claim which should be read to extract the +
| *`username`* __string__ | username is the name of the claim which should be read to extract the +
username from the JWT token. When not specified, it will default to "username". +
| *`extra`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-27-apis-concierge-authentication-v1alpha1-extramapping[$$ExtraMapping$$] array__ | extra is similar to claimMappings.extra from Kubernetes AuthenticationConfiguration as documented in +
https://kubernetes.io/docs/reference/access-authn-authz/authentication. However, note that the +
Pinniped Concierge issues client certificates to users for the purpose of authenticating, and +
the Kubernetes API server does not have any mechanism for transmitting auth extras via client +
certificates. When configured, these extras will appear in client certificates issued by the +
Pinniped Supervisor in the x509 Subject field as Organizational Units (OU). However, when this +
client certificate is presented to Kubernetes for authentication, Kubernetes will ignore these +
extras. This is probably only useful if you are using a custom authenticating proxy in front +
of your Kubernetes API server which can translate these OUs into auth extras, as described by +
https://kubernetes.io/docs/reference/access-authn-authz/authentication/#authenticating-proxy. +
This is an advanced configuration option. During an end-user login flow, each of these CEL expressions +
must evaluate to either a string or an array of strings, or else the user's login will fail. +
|===
@@ -178,6 +276,33 @@ Any changes to the CA bundle in the secret or configmap will be dynamically relo
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-27-apis-concierge-authentication-v1alpha1-uservalidationrule"]
==== UserValidationRule
UserValidationRule provides the configuration for a single user info validation rule.
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-27-apis-concierge-authentication-v1alpha1-jwtauthenticatorspec[$$JWTAuthenticatorSpec$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`expression`* __string__ | expression represents the expression which will be evaluated by CEL. +
Must return true for the validation to pass. +
CEL expressions have access to the contents of UserInfo, organized into CEL variable: +
- 'user' - authentication.k8s.io/v1, Kind=UserInfo object +
Refer to https://github.com/kubernetes/api/blob/release-1.28/authentication/v1/types.go#L105-L122 for the definition. +
API documentation: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#userinfo-v1-authentication-k8s-io +
Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ +
| *`message`* __string__ | message customizes the returned error message when rule returns false. +
message is a literal string. +
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-27-apis-concierge-authentication-v1alpha1-webhookauthenticator"]
==== WebhookAuthenticator

View File

@@ -18,7 +18,7 @@ const (
JWTAuthenticatorPhaseError JWTAuthenticatorPhase = "Error"
)
// Status of a JWT authenticator.
// JWTAuthenticatorStatus is the status of a JWT authenticator.
type JWTAuthenticatorStatus struct {
// Represents the observations of the authenticator's current state.
// +patchMergeKey=type
@@ -26,46 +26,168 @@ type JWTAuthenticatorStatus struct {
// +listType=map
// +listMapKey=type
Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"`
// Phase summarizes the overall status of the JWTAuthenticator.
// +kubebuilder:default=Pending
// +kubebuilder:validation:Enum=Pending;Ready;Error
Phase JWTAuthenticatorPhase `json:"phase,omitempty"`
}
// Spec for configuring a JWT authenticator.
// JWTAuthenticatorSpec is the spec for configuring a JWT authenticator.
type JWTAuthenticatorSpec struct {
// Issuer is the OIDC issuer URL that will be used to discover public signing keys. Issuer is
// issuer is the OIDC issuer URL that will be used to discover public signing keys. Issuer is
// also used to validate the "iss" JWT claim.
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:Pattern=`^https://`
Issuer string `json:"issuer"`
// Audience is the required value of the "aud" JWT claim.
// audience is the required value of the "aud" JWT claim.
// +kubebuilder:validation:MinLength=1
Audience string `json:"audience"`
// Claims allows customization of the claims that will be mapped to user identity
// claims allows customization of the claims that will be mapped to user identity
// for Kubernetes access.
// +optional
Claims JWTTokenClaims `json:"claims"`
// TLS configuration for communicating with the OIDC provider.
// claimValidationRules are rules that are applied to validate token claims to authenticate users.
// This is similar to claimValidationRules from Kubernetes AuthenticationConfiguration as documented in
// https://kubernetes.io/docs/reference/access-authn-authz/authentication.
// This is an advanced configuration option. During an end-user login flow, mistakes in this
// configuration will cause the user's login to fail.
// +optional
ClaimValidationRules []ClaimValidationRule `json:"claimValidationRules,omitempty"`
// userValidationRules are rules that are applied to final user before completing authentication.
// These allow invariants to be applied to incoming identities such as preventing the
// use of the system: prefix that is commonly used by Kubernetes components.
// The validation rules are logically ANDed together and must all return true for the validation to pass.
// This is similar to claimValidationRules from Kubernetes AuthenticationConfiguration as documented in
// https://kubernetes.io/docs/reference/access-authn-authz/authentication.
// This is an advanced configuration option. During an end-user login flow, mistakes in this
// configuration will cause the user's login to fail.
// +optional
UserValidationRules []UserValidationRule `json:"userValidationRules,omitempty"`
// tls is the configuration for communicating with the OIDC provider via TLS.
// +optional
TLS *TLSSpec `json:"tls,omitempty"`
}
// ClaimValidationRule provides the configuration for a single claim validation rule.
type ClaimValidationRule struct {
// claim is the name of a required claim.
// Same as --oidc-required-claim flag.
// Only string claim keys are supported.
// Mutually exclusive with expression and message.
// +optional
Claim string `json:"claim,omitempty"`
// requiredValue is the value of a required claim.
// Same as --oidc-required-claim flag.
// Only string claim values are supported.
// If claim is set and requiredValue is not set, the claim must be present with a value set to the empty string.
// Mutually exclusive with expression and message.
// +optional
RequiredValue string `json:"requiredValue,omitempty"`
// expression represents the expression which will be evaluated by CEL.
// Must produce a boolean.
//
// CEL expressions have access to the contents of the token claims, organized into CEL variable:
// - 'claims' is a map of claim names to claim values.
// For example, a variable named 'sub' can be accessed as 'claims.sub'.
// Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'.
// Must return true for the validation to pass.
//
// Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
//
// Mutually exclusive with claim and requiredValue.
// +optional
Expression string `json:"expression,omitempty"`
// message customizes the returned error message when expression returns false.
// message is a literal string.
// Mutually exclusive with claim and requiredValue.
// +optional
Message string `json:"message,omitempty"`
}
// UserValidationRule provides the configuration for a single user info validation rule.
type UserValidationRule struct {
// expression represents the expression which will be evaluated by CEL.
// Must return true for the validation to pass.
//
// CEL expressions have access to the contents of UserInfo, organized into CEL variable:
// - 'user' - authentication.k8s.io/v1, Kind=UserInfo object
// Refer to https://github.com/kubernetes/api/blob/release-1.28/authentication/v1/types.go#L105-L122 for the definition.
// API documentation: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#userinfo-v1-authentication-k8s-io
//
// Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
//
// +required
Expression string `json:"expression"`
// message customizes the returned error message when rule returns false.
// message is a literal string.
// +optional
Message string `json:"message,omitempty"`
}
// JWTTokenClaims allows customization of the claims that will be mapped to user identity
// for Kubernetes access.
type JWTTokenClaims struct {
// Groups is the name of the claim which should be read to extract the user's
// groups is the name of the claim which should be read to extract the user's
// group membership from the JWT token. When not specified, it will default to "groups".
// +optional
Groups string `json:"groups"`
// Username is the name of the claim which should be read to extract the
// username is the name of the claim which should be read to extract the
// username from the JWT token. When not specified, it will default to "username".
// +optional
Username string `json:"username"`
// extra is similar to claimMappings.extra from Kubernetes AuthenticationConfiguration as documented in
// https://kubernetes.io/docs/reference/access-authn-authz/authentication. However, note that the
// Pinniped Concierge issues client certificates to users for the purpose of authenticating, and
// the Kubernetes API server does not have any mechanism for transmitting auth extras via client
// certificates. When configured, these extras will appear in client certificates issued by the
// Pinniped Supervisor in the x509 Subject field as Organizational Units (OU). However, when this
// client certificate is presented to Kubernetes for authentication, Kubernetes will ignore these
// extras. This is probably only useful if you are using a custom authenticating proxy in front
// of your Kubernetes API server which can translate these OUs into auth extras, as described by
// https://kubernetes.io/docs/reference/access-authn-authz/authentication/#authenticating-proxy.
// This is an advanced configuration option. During an end-user login flow, each of these CEL expressions
// must evaluate to either a string or an array of strings, or else the user's login will fail.
// +optional
Extra []ExtraMapping `json:"extra,omitempty"`
}
// ExtraMapping provides the configuration for a single extra mapping.
type ExtraMapping struct {
// key is a string to use as the extra attribute key.
// key must be a domain-prefix path (e.g. example.org/foo). All characters before the first "/" must be a valid
// subdomain as defined by RFC 1123. All characters trailing the first "/" must
// be valid HTTP Path characters as defined by RFC 3986.
// key must be lowercase.
// Required to be unique.
// +required
Key string `json:"key"`
// valueExpression is a CEL expression to extract extra attribute value.
// valueExpression must produce a string or string array value.
// "", [], and null values are treated as the extra mapping not being present.
// Empty string values contained within a string array are filtered out.
//
// CEL expressions have access to the contents of the token claims, organized into CEL variable:
// - 'claims' is a map of claim names to claim values.
// For example, a variable named 'sub' can be accessed as 'claims.sub'.
// Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'.
//
// Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
//
// +required
ValueExpression string `json:"valueExpression"`
}
// JWTAuthenticator describes the configuration of a JWT authenticator.
@@ -86,14 +208,14 @@ type JWTAuthenticator struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
// Spec for configuring the authenticator.
// spec for configuring the authenticator.
Spec JWTAuthenticatorSpec `json:"spec"`
// Status of the authenticator.
// status of the authenticator.
Status JWTAuthenticatorStatus `json:"status,omitempty"`
}
// List of JWTAuthenticator objects.
// JWTAuthenticatorList is a list of JWTAuthenticator objects.
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type JWTAuthenticatorList struct {
metav1.TypeMeta `json:",inline"`

View File

@@ -29,6 +29,38 @@ func (in *CertificateAuthorityDataSourceSpec) DeepCopy() *CertificateAuthorityDa
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ClaimValidationRule) DeepCopyInto(out *ClaimValidationRule) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClaimValidationRule.
func (in *ClaimValidationRule) DeepCopy() *ClaimValidationRule {
if in == nil {
return nil
}
out := new(ClaimValidationRule)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ExtraMapping) DeepCopyInto(out *ExtraMapping) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExtraMapping.
func (in *ExtraMapping) DeepCopy() *ExtraMapping {
if in == nil {
return nil
}
out := new(ExtraMapping)
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
@@ -93,7 +125,17 @@ func (in *JWTAuthenticatorList) DeepCopyObject() runtime.Object {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *JWTAuthenticatorSpec) DeepCopyInto(out *JWTAuthenticatorSpec) {
*out = *in
out.Claims = in.Claims
in.Claims.DeepCopyInto(&out.Claims)
if in.ClaimValidationRules != nil {
in, out := &in.ClaimValidationRules, &out.ClaimValidationRules
*out = make([]ClaimValidationRule, len(*in))
copy(*out, *in)
}
if in.UserValidationRules != nil {
in, out := &in.UserValidationRules, &out.UserValidationRules
*out = make([]UserValidationRule, len(*in))
copy(*out, *in)
}
if in.TLS != nil {
in, out := &in.TLS, &out.TLS
*out = new(TLSSpec)
@@ -138,6 +180,11 @@ func (in *JWTAuthenticatorStatus) DeepCopy() *JWTAuthenticatorStatus {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *JWTTokenClaims) DeepCopyInto(out *JWTTokenClaims) {
*out = *in
if in.Extra != nil {
in, out := &in.Extra, &out.Extra
*out = make([]ExtraMapping, len(*in))
copy(*out, *in)
}
return
}
@@ -172,6 +219,22 @@ func (in *TLSSpec) DeepCopy() *TLSSpec {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *UserValidationRule) DeepCopyInto(out *UserValidationRule) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UserValidationRule.
func (in *UserValidationRule) DeepCopy() *UserValidationRule {
if in == nil {
return nil
}
out := new(UserValidationRule)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *WebhookAuthenticator) DeepCopyInto(out *WebhookAuthenticator) {
*out = *in

View File

@@ -58,37 +58,133 @@ spec:
metadata:
type: object
spec:
description: Spec for configuring the authenticator.
description: spec for configuring the authenticator.
properties:
audience:
description: Audience is the required value of the "aud" JWT claim.
description: audience is the required value of the "aud" JWT claim.
minLength: 1
type: string
claimValidationRules:
description: |-
claimValidationRules are rules that are applied to validate token claims to authenticate users.
This is similar to claimValidationRules from Kubernetes AuthenticationConfiguration as documented in
https://kubernetes.io/docs/reference/access-authn-authz/authentication.
This is an advanced configuration option. During an end-user login flow, mistakes in this
configuration will cause the user's login to fail.
items:
description: ClaimValidationRule provides the configuration for
a single claim validation rule.
properties:
claim:
description: |-
claim is the name of a required claim.
Same as --oidc-required-claim flag.
Only string claim keys are supported.
Mutually exclusive with expression and message.
type: string
expression:
description: |-
expression represents the expression which will be evaluated by CEL.
Must produce a boolean.
CEL expressions have access to the contents of the token claims, organized into CEL variable:
- 'claims' is a map of claim names to claim values.
For example, a variable named 'sub' can be accessed as 'claims.sub'.
Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'.
Must return true for the validation to pass.
Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
Mutually exclusive with claim and requiredValue.
type: string
message:
description: |-
message customizes the returned error message when expression returns false.
message is a literal string.
Mutually exclusive with claim and requiredValue.
type: string
requiredValue:
description: |-
requiredValue is the value of a required claim.
Same as --oidc-required-claim flag.
Only string claim values are supported.
If claim is set and requiredValue is not set, the claim must be present with a value set to the empty string.
Mutually exclusive with expression and message.
type: string
type: object
type: array
claims:
description: |-
Claims allows customization of the claims that will be mapped to user identity
claims allows customization of the claims that will be mapped to user identity
for Kubernetes access.
properties:
extra:
description: |-
extra is similar to claimMappings.extra from Kubernetes AuthenticationConfiguration as documented in
https://kubernetes.io/docs/reference/access-authn-authz/authentication. However, note that the
Pinniped Concierge issues client certificates to users for the purpose of authenticating, and
the Kubernetes API server does not have any mechanism for transmitting auth extras via client
certificates. When configured, these extras will appear in client certificates issued by the
Pinniped Supervisor in the x509 Subject field as Organizational Units (OU). However, when this
client certificate is presented to Kubernetes for authentication, Kubernetes will ignore these
extras. This is probably only useful if you are using a custom authenticating proxy in front
of your Kubernetes API server which can translate these OUs into auth extras, as described by
https://kubernetes.io/docs/reference/access-authn-authz/authentication/#authenticating-proxy.
This is an advanced configuration option. During an end-user login flow, each of these CEL expressions
must evaluate to either a string or an array of strings, or else the user's login will fail.
items:
description: ExtraMapping provides the configuration for a single
extra mapping.
properties:
key:
description: |-
key is a string to use as the extra attribute key.
key must be a domain-prefix path (e.g. example.org/foo). All characters before the first "/" must be a valid
subdomain as defined by RFC 1123. All characters trailing the first "/" must
be valid HTTP Path characters as defined by RFC 3986.
key must be lowercase.
Required to be unique.
type: string
valueExpression:
description: |-
valueExpression is a CEL expression to extract extra attribute value.
valueExpression must produce a string or string array value.
"", [], and null values are treated as the extra mapping not being present.
Empty string values contained within a string array are filtered out.
CEL expressions have access to the contents of the token claims, organized into CEL variable:
- 'claims' is a map of claim names to claim values.
For example, a variable named 'sub' can be accessed as 'claims.sub'.
Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'.
Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
type: string
required:
- key
- valueExpression
type: object
type: array
groups:
description: |-
Groups is the name of the claim which should be read to extract the user's
groups is the name of the claim which should be read to extract the user's
group membership from the JWT token. When not specified, it will default to "groups".
type: string
username:
description: |-
Username is the name of the claim which should be read to extract the
username is the name of the claim which should be read to extract the
username from the JWT token. When not specified, it will default to "username".
type: string
type: object
issuer:
description: |-
Issuer is the OIDC issuer URL that will be used to discover public signing keys. Issuer is
issuer is the OIDC issuer URL that will be used to discover public signing keys. Issuer is
also used to validate the "iss" JWT claim.
minLength: 1
pattern: ^https://
type: string
tls:
description: TLS configuration for communicating with the OIDC provider.
description: tls is the configuration for communicating with the OIDC
provider via TLS.
properties:
certificateAuthorityData:
description: X.509 Certificate Authority (base64-encoded PEM bundle).
@@ -128,12 +224,47 @@ spec:
- name
type: object
type: object
userValidationRules:
description: |-
userValidationRules are rules that are applied to final user before completing authentication.
These allow invariants to be applied to incoming identities such as preventing the
use of the system: prefix that is commonly used by Kubernetes components.
The validation rules are logically ANDed together and must all return true for the validation to pass.
This is similar to claimValidationRules from Kubernetes AuthenticationConfiguration as documented in
https://kubernetes.io/docs/reference/access-authn-authz/authentication.
This is an advanced configuration option. During an end-user login flow, mistakes in this
configuration will cause the user's login to fail.
items:
description: UserValidationRule provides the configuration for a
single user info validation rule.
properties:
expression:
description: |-
expression represents the expression which will be evaluated by CEL.
Must return true for the validation to pass.
CEL expressions have access to the contents of UserInfo, organized into CEL variable:
- 'user' - authentication.k8s.io/v1, Kind=UserInfo object
Refer to https://github.com/kubernetes/api/blob/release-1.28/authentication/v1/types.go#L105-L122 for the definition.
API documentation: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#userinfo-v1-authentication-k8s-io
Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
type: string
message:
description: |-
message customizes the returned error message when rule returns false.
message is a literal string.
type: string
required:
- expression
type: object
type: array
required:
- audience
- issuer
type: object
status:
description: Status of the authenticator.
description: status of the authenticator.
properties:
conditions:
description: Represents the observations of the authenticator's current

View File

@@ -60,6 +60,79 @@ certificate bundle. +
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-28-apis-concierge-authentication-v1alpha1-claimvalidationrule"]
==== ClaimValidationRule
ClaimValidationRule provides the configuration for a single claim validation rule.
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-28-apis-concierge-authentication-v1alpha1-jwtauthenticatorspec[$$JWTAuthenticatorSpec$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`claim`* __string__ | claim is the name of a required claim. +
Same as --oidc-required-claim flag. +
Only string claim keys are supported. +
Mutually exclusive with expression and message. +
| *`requiredValue`* __string__ | requiredValue is the value of a required claim. +
Same as --oidc-required-claim flag. +
Only string claim values are supported. +
If claim is set and requiredValue is not set, the claim must be present with a value set to the empty string. +
Mutually exclusive with expression and message. +
| *`expression`* __string__ | expression represents the expression which will be evaluated by CEL. +
Must produce a boolean. +
CEL expressions have access to the contents of the token claims, organized into CEL variable: +
- 'claims' is a map of claim names to claim values. +
For example, a variable named 'sub' can be accessed as 'claims.sub'. +
Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'. +
Must return true for the validation to pass. +
Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ +
Mutually exclusive with claim and requiredValue. +
| *`message`* __string__ | message customizes the returned error message when expression returns false. +
message is a literal string. +
Mutually exclusive with claim and requiredValue. +
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-28-apis-concierge-authentication-v1alpha1-extramapping"]
==== ExtraMapping
ExtraMapping provides the configuration for a single extra mapping.
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-28-apis-concierge-authentication-v1alpha1-jwttokenclaims[$$JWTTokenClaims$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`key`* __string__ | key is a string to use as the extra attribute key. +
key must be a domain-prefix path (e.g. example.org/foo). All characters before the first "/" must be a valid +
subdomain as defined by RFC 1123. All characters trailing the first "/" must +
be valid HTTP Path characters as defined by RFC 3986. +
key must be lowercase. +
Required to be unique. +
| *`valueExpression`* __string__ | valueExpression is a CEL expression to extract extra attribute value. +
valueExpression must produce a string or string array value. +
"", [], and null values are treated as the extra mapping not being present. +
Empty string values contained within a string array are filtered out. +
CEL expressions have access to the contents of the token claims, organized into CEL variable: +
- 'claims' is a map of claim names to claim values. +
For example, a variable named 'sub' can be accessed as 'claims.sub'. +
Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'. +
Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ +
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-28-apis-concierge-authentication-v1alpha1-jwtauthenticator"]
==== JWTAuthenticator
@@ -78,8 +151,8 @@ signature, existence of claims, etc.) and extract the username and groups from t
| Field | Description
| *`metadata`* __link:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#objectmeta-v1-meta[$$ObjectMeta$$]__ | Refer to Kubernetes API documentation for fields of `metadata`.
| *`spec`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-28-apis-concierge-authentication-v1alpha1-jwtauthenticatorspec[$$JWTAuthenticatorSpec$$]__ | Spec for configuring the authenticator. +
| *`status`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-28-apis-concierge-authentication-v1alpha1-jwtauthenticatorstatus[$$JWTAuthenticatorStatus$$]__ | Status of the authenticator. +
| *`spec`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-28-apis-concierge-authentication-v1alpha1-jwtauthenticatorspec[$$JWTAuthenticatorSpec$$]__ | spec for configuring the authenticator. +
| *`status`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-28-apis-concierge-authentication-v1alpha1-jwtauthenticatorstatus[$$JWTAuthenticatorStatus$$]__ | status of the authenticator. +
|===
@@ -100,7 +173,7 @@ signature, existence of claims, etc.) and extract the username and groups from t
[id="{anchor_prefix}-go-pinniped-dev-generated-1-28-apis-concierge-authentication-v1alpha1-jwtauthenticatorspec"]
==== JWTAuthenticatorSpec
Spec for configuring a JWT authenticator.
JWTAuthenticatorSpec is the spec for configuring a JWT authenticator.
.Appears In:
****
@@ -110,19 +183,32 @@ Spec for configuring a JWT authenticator.
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`issuer`* __string__ | Issuer is the OIDC issuer URL that will be used to discover public signing keys. Issuer is +
| *`issuer`* __string__ | issuer is the OIDC issuer URL that will be used to discover public signing keys. Issuer is +
also used to validate the "iss" JWT claim. +
| *`audience`* __string__ | Audience is the required value of the "aud" JWT claim. +
| *`claims`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-28-apis-concierge-authentication-v1alpha1-jwttokenclaims[$$JWTTokenClaims$$]__ | Claims allows customization of the claims that will be mapped to user identity +
| *`audience`* __string__ | audience is the required value of the "aud" JWT claim. +
| *`claims`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-28-apis-concierge-authentication-v1alpha1-jwttokenclaims[$$JWTTokenClaims$$]__ | claims allows customization of the claims that will be mapped to user identity +
for Kubernetes access. +
| *`tls`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-28-apis-concierge-authentication-v1alpha1-tlsspec[$$TLSSpec$$]__ | TLS configuration for communicating with the OIDC provider. +
| *`claimValidationRules`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-28-apis-concierge-authentication-v1alpha1-claimvalidationrule[$$ClaimValidationRule$$] array__ | claimValidationRules are rules that are applied to validate token claims to authenticate users. +
This is similar to claimValidationRules from Kubernetes AuthenticationConfiguration as documented in +
https://kubernetes.io/docs/reference/access-authn-authz/authentication. +
This is an advanced configuration option. During an end-user login flow, mistakes in this +
configuration will cause the user's login to fail. +
| *`userValidationRules`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-28-apis-concierge-authentication-v1alpha1-uservalidationrule[$$UserValidationRule$$] array__ | userValidationRules are rules that are applied to final user before completing authentication. +
These allow invariants to be applied to incoming identities such as preventing the +
use of the system: prefix that is commonly used by Kubernetes components. +
The validation rules are logically ANDed together and must all return true for the validation to pass. +
This is similar to claimValidationRules from Kubernetes AuthenticationConfiguration as documented in +
https://kubernetes.io/docs/reference/access-authn-authz/authentication. +
This is an advanced configuration option. During an end-user login flow, mistakes in this +
configuration will cause the user's login to fail. +
| *`tls`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-28-apis-concierge-authentication-v1alpha1-tlsspec[$$TLSSpec$$]__ | tls is the configuration for communicating with the OIDC provider via TLS. +
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-28-apis-concierge-authentication-v1alpha1-jwtauthenticatorstatus"]
==== JWTAuthenticatorStatus
Status of a JWT authenticator.
JWTAuthenticatorStatus is the status of a JWT authenticator.
.Appears In:
****
@@ -151,10 +237,22 @@ for Kubernetes access.
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`groups`* __string__ | Groups is the name of the claim which should be read to extract the user's +
| *`groups`* __string__ | groups is the name of the claim which should be read to extract the user's +
group membership from the JWT token. When not specified, it will default to "groups". +
| *`username`* __string__ | Username is the name of the claim which should be read to extract the +
| *`username`* __string__ | username is the name of the claim which should be read to extract the +
username from the JWT token. When not specified, it will default to "username". +
| *`extra`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-28-apis-concierge-authentication-v1alpha1-extramapping[$$ExtraMapping$$] array__ | extra is similar to claimMappings.extra from Kubernetes AuthenticationConfiguration as documented in +
https://kubernetes.io/docs/reference/access-authn-authz/authentication. However, note that the +
Pinniped Concierge issues client certificates to users for the purpose of authenticating, and +
the Kubernetes API server does not have any mechanism for transmitting auth extras via client +
certificates. When configured, these extras will appear in client certificates issued by the +
Pinniped Supervisor in the x509 Subject field as Organizational Units (OU). However, when this +
client certificate is presented to Kubernetes for authentication, Kubernetes will ignore these +
extras. This is probably only useful if you are using a custom authenticating proxy in front +
of your Kubernetes API server which can translate these OUs into auth extras, as described by +
https://kubernetes.io/docs/reference/access-authn-authz/authentication/#authenticating-proxy. +
This is an advanced configuration option. During an end-user login flow, each of these CEL expressions +
must evaluate to either a string or an array of strings, or else the user's login will fail. +
|===
@@ -178,6 +276,33 @@ Any changes to the CA bundle in the secret or configmap will be dynamically relo
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-28-apis-concierge-authentication-v1alpha1-uservalidationrule"]
==== UserValidationRule
UserValidationRule provides the configuration for a single user info validation rule.
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-28-apis-concierge-authentication-v1alpha1-jwtauthenticatorspec[$$JWTAuthenticatorSpec$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`expression`* __string__ | expression represents the expression which will be evaluated by CEL. +
Must return true for the validation to pass. +
CEL expressions have access to the contents of UserInfo, organized into CEL variable: +
- 'user' - authentication.k8s.io/v1, Kind=UserInfo object +
Refer to https://github.com/kubernetes/api/blob/release-1.28/authentication/v1/types.go#L105-L122 for the definition. +
API documentation: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#userinfo-v1-authentication-k8s-io +
Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ +
| *`message`* __string__ | message customizes the returned error message when rule returns false. +
message is a literal string. +
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-28-apis-concierge-authentication-v1alpha1-webhookauthenticator"]
==== WebhookAuthenticator

View File

@@ -18,7 +18,7 @@ const (
JWTAuthenticatorPhaseError JWTAuthenticatorPhase = "Error"
)
// Status of a JWT authenticator.
// JWTAuthenticatorStatus is the status of a JWT authenticator.
type JWTAuthenticatorStatus struct {
// Represents the observations of the authenticator's current state.
// +patchMergeKey=type
@@ -26,46 +26,168 @@ type JWTAuthenticatorStatus struct {
// +listType=map
// +listMapKey=type
Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"`
// Phase summarizes the overall status of the JWTAuthenticator.
// +kubebuilder:default=Pending
// +kubebuilder:validation:Enum=Pending;Ready;Error
Phase JWTAuthenticatorPhase `json:"phase,omitempty"`
}
// Spec for configuring a JWT authenticator.
// JWTAuthenticatorSpec is the spec for configuring a JWT authenticator.
type JWTAuthenticatorSpec struct {
// Issuer is the OIDC issuer URL that will be used to discover public signing keys. Issuer is
// issuer is the OIDC issuer URL that will be used to discover public signing keys. Issuer is
// also used to validate the "iss" JWT claim.
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:Pattern=`^https://`
Issuer string `json:"issuer"`
// Audience is the required value of the "aud" JWT claim.
// audience is the required value of the "aud" JWT claim.
// +kubebuilder:validation:MinLength=1
Audience string `json:"audience"`
// Claims allows customization of the claims that will be mapped to user identity
// claims allows customization of the claims that will be mapped to user identity
// for Kubernetes access.
// +optional
Claims JWTTokenClaims `json:"claims"`
// TLS configuration for communicating with the OIDC provider.
// claimValidationRules are rules that are applied to validate token claims to authenticate users.
// This is similar to claimValidationRules from Kubernetes AuthenticationConfiguration as documented in
// https://kubernetes.io/docs/reference/access-authn-authz/authentication.
// This is an advanced configuration option. During an end-user login flow, mistakes in this
// configuration will cause the user's login to fail.
// +optional
ClaimValidationRules []ClaimValidationRule `json:"claimValidationRules,omitempty"`
// userValidationRules are rules that are applied to final user before completing authentication.
// These allow invariants to be applied to incoming identities such as preventing the
// use of the system: prefix that is commonly used by Kubernetes components.
// The validation rules are logically ANDed together and must all return true for the validation to pass.
// This is similar to claimValidationRules from Kubernetes AuthenticationConfiguration as documented in
// https://kubernetes.io/docs/reference/access-authn-authz/authentication.
// This is an advanced configuration option. During an end-user login flow, mistakes in this
// configuration will cause the user's login to fail.
// +optional
UserValidationRules []UserValidationRule `json:"userValidationRules,omitempty"`
// tls is the configuration for communicating with the OIDC provider via TLS.
// +optional
TLS *TLSSpec `json:"tls,omitempty"`
}
// ClaimValidationRule provides the configuration for a single claim validation rule.
type ClaimValidationRule struct {
// claim is the name of a required claim.
// Same as --oidc-required-claim flag.
// Only string claim keys are supported.
// Mutually exclusive with expression and message.
// +optional
Claim string `json:"claim,omitempty"`
// requiredValue is the value of a required claim.
// Same as --oidc-required-claim flag.
// Only string claim values are supported.
// If claim is set and requiredValue is not set, the claim must be present with a value set to the empty string.
// Mutually exclusive with expression and message.
// +optional
RequiredValue string `json:"requiredValue,omitempty"`
// expression represents the expression which will be evaluated by CEL.
// Must produce a boolean.
//
// CEL expressions have access to the contents of the token claims, organized into CEL variable:
// - 'claims' is a map of claim names to claim values.
// For example, a variable named 'sub' can be accessed as 'claims.sub'.
// Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'.
// Must return true for the validation to pass.
//
// Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
//
// Mutually exclusive with claim and requiredValue.
// +optional
Expression string `json:"expression,omitempty"`
// message customizes the returned error message when expression returns false.
// message is a literal string.
// Mutually exclusive with claim and requiredValue.
// +optional
Message string `json:"message,omitempty"`
}
// UserValidationRule provides the configuration for a single user info validation rule.
type UserValidationRule struct {
// expression represents the expression which will be evaluated by CEL.
// Must return true for the validation to pass.
//
// CEL expressions have access to the contents of UserInfo, organized into CEL variable:
// - 'user' - authentication.k8s.io/v1, Kind=UserInfo object
// Refer to https://github.com/kubernetes/api/blob/release-1.28/authentication/v1/types.go#L105-L122 for the definition.
// API documentation: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#userinfo-v1-authentication-k8s-io
//
// Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
//
// +required
Expression string `json:"expression"`
// message customizes the returned error message when rule returns false.
// message is a literal string.
// +optional
Message string `json:"message,omitempty"`
}
// JWTTokenClaims allows customization of the claims that will be mapped to user identity
// for Kubernetes access.
type JWTTokenClaims struct {
// Groups is the name of the claim which should be read to extract the user's
// groups is the name of the claim which should be read to extract the user's
// group membership from the JWT token. When not specified, it will default to "groups".
// +optional
Groups string `json:"groups"`
// Username is the name of the claim which should be read to extract the
// username is the name of the claim which should be read to extract the
// username from the JWT token. When not specified, it will default to "username".
// +optional
Username string `json:"username"`
// extra is similar to claimMappings.extra from Kubernetes AuthenticationConfiguration as documented in
// https://kubernetes.io/docs/reference/access-authn-authz/authentication. However, note that the
// Pinniped Concierge issues client certificates to users for the purpose of authenticating, and
// the Kubernetes API server does not have any mechanism for transmitting auth extras via client
// certificates. When configured, these extras will appear in client certificates issued by the
// Pinniped Supervisor in the x509 Subject field as Organizational Units (OU). However, when this
// client certificate is presented to Kubernetes for authentication, Kubernetes will ignore these
// extras. This is probably only useful if you are using a custom authenticating proxy in front
// of your Kubernetes API server which can translate these OUs into auth extras, as described by
// https://kubernetes.io/docs/reference/access-authn-authz/authentication/#authenticating-proxy.
// This is an advanced configuration option. During an end-user login flow, each of these CEL expressions
// must evaluate to either a string or an array of strings, or else the user's login will fail.
// +optional
Extra []ExtraMapping `json:"extra,omitempty"`
}
// ExtraMapping provides the configuration for a single extra mapping.
type ExtraMapping struct {
// key is a string to use as the extra attribute key.
// key must be a domain-prefix path (e.g. example.org/foo). All characters before the first "/" must be a valid
// subdomain as defined by RFC 1123. All characters trailing the first "/" must
// be valid HTTP Path characters as defined by RFC 3986.
// key must be lowercase.
// Required to be unique.
// +required
Key string `json:"key"`
// valueExpression is a CEL expression to extract extra attribute value.
// valueExpression must produce a string or string array value.
// "", [], and null values are treated as the extra mapping not being present.
// Empty string values contained within a string array are filtered out.
//
// CEL expressions have access to the contents of the token claims, organized into CEL variable:
// - 'claims' is a map of claim names to claim values.
// For example, a variable named 'sub' can be accessed as 'claims.sub'.
// Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'.
//
// Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
//
// +required
ValueExpression string `json:"valueExpression"`
}
// JWTAuthenticator describes the configuration of a JWT authenticator.
@@ -86,14 +208,14 @@ type JWTAuthenticator struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
// Spec for configuring the authenticator.
// spec for configuring the authenticator.
Spec JWTAuthenticatorSpec `json:"spec"`
// Status of the authenticator.
// status of the authenticator.
Status JWTAuthenticatorStatus `json:"status,omitempty"`
}
// List of JWTAuthenticator objects.
// JWTAuthenticatorList is a list of JWTAuthenticator objects.
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type JWTAuthenticatorList struct {
metav1.TypeMeta `json:",inline"`

View File

@@ -29,6 +29,38 @@ func (in *CertificateAuthorityDataSourceSpec) DeepCopy() *CertificateAuthorityDa
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ClaimValidationRule) DeepCopyInto(out *ClaimValidationRule) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClaimValidationRule.
func (in *ClaimValidationRule) DeepCopy() *ClaimValidationRule {
if in == nil {
return nil
}
out := new(ClaimValidationRule)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ExtraMapping) DeepCopyInto(out *ExtraMapping) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExtraMapping.
func (in *ExtraMapping) DeepCopy() *ExtraMapping {
if in == nil {
return nil
}
out := new(ExtraMapping)
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
@@ -93,7 +125,17 @@ func (in *JWTAuthenticatorList) DeepCopyObject() runtime.Object {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *JWTAuthenticatorSpec) DeepCopyInto(out *JWTAuthenticatorSpec) {
*out = *in
out.Claims = in.Claims
in.Claims.DeepCopyInto(&out.Claims)
if in.ClaimValidationRules != nil {
in, out := &in.ClaimValidationRules, &out.ClaimValidationRules
*out = make([]ClaimValidationRule, len(*in))
copy(*out, *in)
}
if in.UserValidationRules != nil {
in, out := &in.UserValidationRules, &out.UserValidationRules
*out = make([]UserValidationRule, len(*in))
copy(*out, *in)
}
if in.TLS != nil {
in, out := &in.TLS, &out.TLS
*out = new(TLSSpec)
@@ -138,6 +180,11 @@ func (in *JWTAuthenticatorStatus) DeepCopy() *JWTAuthenticatorStatus {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *JWTTokenClaims) DeepCopyInto(out *JWTTokenClaims) {
*out = *in
if in.Extra != nil {
in, out := &in.Extra, &out.Extra
*out = make([]ExtraMapping, len(*in))
copy(*out, *in)
}
return
}
@@ -172,6 +219,22 @@ func (in *TLSSpec) DeepCopy() *TLSSpec {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *UserValidationRule) DeepCopyInto(out *UserValidationRule) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UserValidationRule.
func (in *UserValidationRule) DeepCopy() *UserValidationRule {
if in == nil {
return nil
}
out := new(UserValidationRule)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *WebhookAuthenticator) DeepCopyInto(out *WebhookAuthenticator) {
*out = *in

View File

@@ -58,37 +58,133 @@ spec:
metadata:
type: object
spec:
description: Spec for configuring the authenticator.
description: spec for configuring the authenticator.
properties:
audience:
description: Audience is the required value of the "aud" JWT claim.
description: audience is the required value of the "aud" JWT claim.
minLength: 1
type: string
claimValidationRules:
description: |-
claimValidationRules are rules that are applied to validate token claims to authenticate users.
This is similar to claimValidationRules from Kubernetes AuthenticationConfiguration as documented in
https://kubernetes.io/docs/reference/access-authn-authz/authentication.
This is an advanced configuration option. During an end-user login flow, mistakes in this
configuration will cause the user's login to fail.
items:
description: ClaimValidationRule provides the configuration for
a single claim validation rule.
properties:
claim:
description: |-
claim is the name of a required claim.
Same as --oidc-required-claim flag.
Only string claim keys are supported.
Mutually exclusive with expression and message.
type: string
expression:
description: |-
expression represents the expression which will be evaluated by CEL.
Must produce a boolean.
CEL expressions have access to the contents of the token claims, organized into CEL variable:
- 'claims' is a map of claim names to claim values.
For example, a variable named 'sub' can be accessed as 'claims.sub'.
Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'.
Must return true for the validation to pass.
Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
Mutually exclusive with claim and requiredValue.
type: string
message:
description: |-
message customizes the returned error message when expression returns false.
message is a literal string.
Mutually exclusive with claim and requiredValue.
type: string
requiredValue:
description: |-
requiredValue is the value of a required claim.
Same as --oidc-required-claim flag.
Only string claim values are supported.
If claim is set and requiredValue is not set, the claim must be present with a value set to the empty string.
Mutually exclusive with expression and message.
type: string
type: object
type: array
claims:
description: |-
Claims allows customization of the claims that will be mapped to user identity
claims allows customization of the claims that will be mapped to user identity
for Kubernetes access.
properties:
extra:
description: |-
extra is similar to claimMappings.extra from Kubernetes AuthenticationConfiguration as documented in
https://kubernetes.io/docs/reference/access-authn-authz/authentication. However, note that the
Pinniped Concierge issues client certificates to users for the purpose of authenticating, and
the Kubernetes API server does not have any mechanism for transmitting auth extras via client
certificates. When configured, these extras will appear in client certificates issued by the
Pinniped Supervisor in the x509 Subject field as Organizational Units (OU). However, when this
client certificate is presented to Kubernetes for authentication, Kubernetes will ignore these
extras. This is probably only useful if you are using a custom authenticating proxy in front
of your Kubernetes API server which can translate these OUs into auth extras, as described by
https://kubernetes.io/docs/reference/access-authn-authz/authentication/#authenticating-proxy.
This is an advanced configuration option. During an end-user login flow, each of these CEL expressions
must evaluate to either a string or an array of strings, or else the user's login will fail.
items:
description: ExtraMapping provides the configuration for a single
extra mapping.
properties:
key:
description: |-
key is a string to use as the extra attribute key.
key must be a domain-prefix path (e.g. example.org/foo). All characters before the first "/" must be a valid
subdomain as defined by RFC 1123. All characters trailing the first "/" must
be valid HTTP Path characters as defined by RFC 3986.
key must be lowercase.
Required to be unique.
type: string
valueExpression:
description: |-
valueExpression is a CEL expression to extract extra attribute value.
valueExpression must produce a string or string array value.
"", [], and null values are treated as the extra mapping not being present.
Empty string values contained within a string array are filtered out.
CEL expressions have access to the contents of the token claims, organized into CEL variable:
- 'claims' is a map of claim names to claim values.
For example, a variable named 'sub' can be accessed as 'claims.sub'.
Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'.
Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
type: string
required:
- key
- valueExpression
type: object
type: array
groups:
description: |-
Groups is the name of the claim which should be read to extract the user's
groups is the name of the claim which should be read to extract the user's
group membership from the JWT token. When not specified, it will default to "groups".
type: string
username:
description: |-
Username is the name of the claim which should be read to extract the
username is the name of the claim which should be read to extract the
username from the JWT token. When not specified, it will default to "username".
type: string
type: object
issuer:
description: |-
Issuer is the OIDC issuer URL that will be used to discover public signing keys. Issuer is
issuer is the OIDC issuer URL that will be used to discover public signing keys. Issuer is
also used to validate the "iss" JWT claim.
minLength: 1
pattern: ^https://
type: string
tls:
description: TLS configuration for communicating with the OIDC provider.
description: tls is the configuration for communicating with the OIDC
provider via TLS.
properties:
certificateAuthorityData:
description: X.509 Certificate Authority (base64-encoded PEM bundle).
@@ -128,12 +224,47 @@ spec:
- name
type: object
type: object
userValidationRules:
description: |-
userValidationRules are rules that are applied to final user before completing authentication.
These allow invariants to be applied to incoming identities such as preventing the
use of the system: prefix that is commonly used by Kubernetes components.
The validation rules are logically ANDed together and must all return true for the validation to pass.
This is similar to claimValidationRules from Kubernetes AuthenticationConfiguration as documented in
https://kubernetes.io/docs/reference/access-authn-authz/authentication.
This is an advanced configuration option. During an end-user login flow, mistakes in this
configuration will cause the user's login to fail.
items:
description: UserValidationRule provides the configuration for a
single user info validation rule.
properties:
expression:
description: |-
expression represents the expression which will be evaluated by CEL.
Must return true for the validation to pass.
CEL expressions have access to the contents of UserInfo, organized into CEL variable:
- 'user' - authentication.k8s.io/v1, Kind=UserInfo object
Refer to https://github.com/kubernetes/api/blob/release-1.28/authentication/v1/types.go#L105-L122 for the definition.
API documentation: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#userinfo-v1-authentication-k8s-io
Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
type: string
message:
description: |-
message customizes the returned error message when rule returns false.
message is a literal string.
type: string
required:
- expression
type: object
type: array
required:
- audience
- issuer
type: object
status:
description: Status of the authenticator.
description: status of the authenticator.
properties:
conditions:
description: Represents the observations of the authenticator's current

View File

@@ -60,6 +60,79 @@ certificate bundle. +
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-29-apis-concierge-authentication-v1alpha1-claimvalidationrule"]
==== ClaimValidationRule
ClaimValidationRule provides the configuration for a single claim validation rule.
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-29-apis-concierge-authentication-v1alpha1-jwtauthenticatorspec[$$JWTAuthenticatorSpec$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`claim`* __string__ | claim is the name of a required claim. +
Same as --oidc-required-claim flag. +
Only string claim keys are supported. +
Mutually exclusive with expression and message. +
| *`requiredValue`* __string__ | requiredValue is the value of a required claim. +
Same as --oidc-required-claim flag. +
Only string claim values are supported. +
If claim is set and requiredValue is not set, the claim must be present with a value set to the empty string. +
Mutually exclusive with expression and message. +
| *`expression`* __string__ | expression represents the expression which will be evaluated by CEL. +
Must produce a boolean. +
CEL expressions have access to the contents of the token claims, organized into CEL variable: +
- 'claims' is a map of claim names to claim values. +
For example, a variable named 'sub' can be accessed as 'claims.sub'. +
Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'. +
Must return true for the validation to pass. +
Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ +
Mutually exclusive with claim and requiredValue. +
| *`message`* __string__ | message customizes the returned error message when expression returns false. +
message is a literal string. +
Mutually exclusive with claim and requiredValue. +
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-29-apis-concierge-authentication-v1alpha1-extramapping"]
==== ExtraMapping
ExtraMapping provides the configuration for a single extra mapping.
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-29-apis-concierge-authentication-v1alpha1-jwttokenclaims[$$JWTTokenClaims$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`key`* __string__ | key is a string to use as the extra attribute key. +
key must be a domain-prefix path (e.g. example.org/foo). All characters before the first "/" must be a valid +
subdomain as defined by RFC 1123. All characters trailing the first "/" must +
be valid HTTP Path characters as defined by RFC 3986. +
key must be lowercase. +
Required to be unique. +
| *`valueExpression`* __string__ | valueExpression is a CEL expression to extract extra attribute value. +
valueExpression must produce a string or string array value. +
"", [], and null values are treated as the extra mapping not being present. +
Empty string values contained within a string array are filtered out. +
CEL expressions have access to the contents of the token claims, organized into CEL variable: +
- 'claims' is a map of claim names to claim values. +
For example, a variable named 'sub' can be accessed as 'claims.sub'. +
Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'. +
Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ +
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-29-apis-concierge-authentication-v1alpha1-jwtauthenticator"]
==== JWTAuthenticator
@@ -78,8 +151,8 @@ signature, existence of claims, etc.) and extract the username and groups from t
| Field | Description
| *`metadata`* __link:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#objectmeta-v1-meta[$$ObjectMeta$$]__ | Refer to Kubernetes API documentation for fields of `metadata`.
| *`spec`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-29-apis-concierge-authentication-v1alpha1-jwtauthenticatorspec[$$JWTAuthenticatorSpec$$]__ | Spec for configuring the authenticator. +
| *`status`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-29-apis-concierge-authentication-v1alpha1-jwtauthenticatorstatus[$$JWTAuthenticatorStatus$$]__ | Status of the authenticator. +
| *`spec`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-29-apis-concierge-authentication-v1alpha1-jwtauthenticatorspec[$$JWTAuthenticatorSpec$$]__ | spec for configuring the authenticator. +
| *`status`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-29-apis-concierge-authentication-v1alpha1-jwtauthenticatorstatus[$$JWTAuthenticatorStatus$$]__ | status of the authenticator. +
|===
@@ -100,7 +173,7 @@ signature, existence of claims, etc.) and extract the username and groups from t
[id="{anchor_prefix}-go-pinniped-dev-generated-1-29-apis-concierge-authentication-v1alpha1-jwtauthenticatorspec"]
==== JWTAuthenticatorSpec
Spec for configuring a JWT authenticator.
JWTAuthenticatorSpec is the spec for configuring a JWT authenticator.
.Appears In:
****
@@ -110,19 +183,32 @@ Spec for configuring a JWT authenticator.
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`issuer`* __string__ | Issuer is the OIDC issuer URL that will be used to discover public signing keys. Issuer is +
| *`issuer`* __string__ | issuer is the OIDC issuer URL that will be used to discover public signing keys. Issuer is +
also used to validate the "iss" JWT claim. +
| *`audience`* __string__ | Audience is the required value of the "aud" JWT claim. +
| *`claims`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-29-apis-concierge-authentication-v1alpha1-jwttokenclaims[$$JWTTokenClaims$$]__ | Claims allows customization of the claims that will be mapped to user identity +
| *`audience`* __string__ | audience is the required value of the "aud" JWT claim. +
| *`claims`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-29-apis-concierge-authentication-v1alpha1-jwttokenclaims[$$JWTTokenClaims$$]__ | claims allows customization of the claims that will be mapped to user identity +
for Kubernetes access. +
| *`tls`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-29-apis-concierge-authentication-v1alpha1-tlsspec[$$TLSSpec$$]__ | TLS configuration for communicating with the OIDC provider. +
| *`claimValidationRules`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-29-apis-concierge-authentication-v1alpha1-claimvalidationrule[$$ClaimValidationRule$$] array__ | claimValidationRules are rules that are applied to validate token claims to authenticate users. +
This is similar to claimValidationRules from Kubernetes AuthenticationConfiguration as documented in +
https://kubernetes.io/docs/reference/access-authn-authz/authentication. +
This is an advanced configuration option. During an end-user login flow, mistakes in this +
configuration will cause the user's login to fail. +
| *`userValidationRules`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-29-apis-concierge-authentication-v1alpha1-uservalidationrule[$$UserValidationRule$$] array__ | userValidationRules are rules that are applied to final user before completing authentication. +
These allow invariants to be applied to incoming identities such as preventing the +
use of the system: prefix that is commonly used by Kubernetes components. +
The validation rules are logically ANDed together and must all return true for the validation to pass. +
This is similar to claimValidationRules from Kubernetes AuthenticationConfiguration as documented in +
https://kubernetes.io/docs/reference/access-authn-authz/authentication. +
This is an advanced configuration option. During an end-user login flow, mistakes in this +
configuration will cause the user's login to fail. +
| *`tls`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-29-apis-concierge-authentication-v1alpha1-tlsspec[$$TLSSpec$$]__ | tls is the configuration for communicating with the OIDC provider via TLS. +
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-29-apis-concierge-authentication-v1alpha1-jwtauthenticatorstatus"]
==== JWTAuthenticatorStatus
Status of a JWT authenticator.
JWTAuthenticatorStatus is the status of a JWT authenticator.
.Appears In:
****
@@ -151,10 +237,22 @@ for Kubernetes access.
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`groups`* __string__ | Groups is the name of the claim which should be read to extract the user's +
| *`groups`* __string__ | groups is the name of the claim which should be read to extract the user's +
group membership from the JWT token. When not specified, it will default to "groups". +
| *`username`* __string__ | Username is the name of the claim which should be read to extract the +
| *`username`* __string__ | username is the name of the claim which should be read to extract the +
username from the JWT token. When not specified, it will default to "username". +
| *`extra`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-29-apis-concierge-authentication-v1alpha1-extramapping[$$ExtraMapping$$] array__ | extra is similar to claimMappings.extra from Kubernetes AuthenticationConfiguration as documented in +
https://kubernetes.io/docs/reference/access-authn-authz/authentication. However, note that the +
Pinniped Concierge issues client certificates to users for the purpose of authenticating, and +
the Kubernetes API server does not have any mechanism for transmitting auth extras via client +
certificates. When configured, these extras will appear in client certificates issued by the +
Pinniped Supervisor in the x509 Subject field as Organizational Units (OU). However, when this +
client certificate is presented to Kubernetes for authentication, Kubernetes will ignore these +
extras. This is probably only useful if you are using a custom authenticating proxy in front +
of your Kubernetes API server which can translate these OUs into auth extras, as described by +
https://kubernetes.io/docs/reference/access-authn-authz/authentication/#authenticating-proxy. +
This is an advanced configuration option. During an end-user login flow, each of these CEL expressions +
must evaluate to either a string or an array of strings, or else the user's login will fail. +
|===
@@ -178,6 +276,33 @@ Any changes to the CA bundle in the secret or configmap will be dynamically relo
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-29-apis-concierge-authentication-v1alpha1-uservalidationrule"]
==== UserValidationRule
UserValidationRule provides the configuration for a single user info validation rule.
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-29-apis-concierge-authentication-v1alpha1-jwtauthenticatorspec[$$JWTAuthenticatorSpec$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`expression`* __string__ | expression represents the expression which will be evaluated by CEL. +
Must return true for the validation to pass. +
CEL expressions have access to the contents of UserInfo, organized into CEL variable: +
- 'user' - authentication.k8s.io/v1, Kind=UserInfo object +
Refer to https://github.com/kubernetes/api/blob/release-1.28/authentication/v1/types.go#L105-L122 for the definition. +
API documentation: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#userinfo-v1-authentication-k8s-io +
Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ +
| *`message`* __string__ | message customizes the returned error message when rule returns false. +
message is a literal string. +
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-29-apis-concierge-authentication-v1alpha1-webhookauthenticator"]
==== WebhookAuthenticator

View File

@@ -18,7 +18,7 @@ const (
JWTAuthenticatorPhaseError JWTAuthenticatorPhase = "Error"
)
// Status of a JWT authenticator.
// JWTAuthenticatorStatus is the status of a JWT authenticator.
type JWTAuthenticatorStatus struct {
// Represents the observations of the authenticator's current state.
// +patchMergeKey=type
@@ -26,46 +26,168 @@ type JWTAuthenticatorStatus struct {
// +listType=map
// +listMapKey=type
Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"`
// Phase summarizes the overall status of the JWTAuthenticator.
// +kubebuilder:default=Pending
// +kubebuilder:validation:Enum=Pending;Ready;Error
Phase JWTAuthenticatorPhase `json:"phase,omitempty"`
}
// Spec for configuring a JWT authenticator.
// JWTAuthenticatorSpec is the spec for configuring a JWT authenticator.
type JWTAuthenticatorSpec struct {
// Issuer is the OIDC issuer URL that will be used to discover public signing keys. Issuer is
// issuer is the OIDC issuer URL that will be used to discover public signing keys. Issuer is
// also used to validate the "iss" JWT claim.
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:Pattern=`^https://`
Issuer string `json:"issuer"`
// Audience is the required value of the "aud" JWT claim.
// audience is the required value of the "aud" JWT claim.
// +kubebuilder:validation:MinLength=1
Audience string `json:"audience"`
// Claims allows customization of the claims that will be mapped to user identity
// claims allows customization of the claims that will be mapped to user identity
// for Kubernetes access.
// +optional
Claims JWTTokenClaims `json:"claims"`
// TLS configuration for communicating with the OIDC provider.
// claimValidationRules are rules that are applied to validate token claims to authenticate users.
// This is similar to claimValidationRules from Kubernetes AuthenticationConfiguration as documented in
// https://kubernetes.io/docs/reference/access-authn-authz/authentication.
// This is an advanced configuration option. During an end-user login flow, mistakes in this
// configuration will cause the user's login to fail.
// +optional
ClaimValidationRules []ClaimValidationRule `json:"claimValidationRules,omitempty"`
// userValidationRules are rules that are applied to final user before completing authentication.
// These allow invariants to be applied to incoming identities such as preventing the
// use of the system: prefix that is commonly used by Kubernetes components.
// The validation rules are logically ANDed together and must all return true for the validation to pass.
// This is similar to claimValidationRules from Kubernetes AuthenticationConfiguration as documented in
// https://kubernetes.io/docs/reference/access-authn-authz/authentication.
// This is an advanced configuration option. During an end-user login flow, mistakes in this
// configuration will cause the user's login to fail.
// +optional
UserValidationRules []UserValidationRule `json:"userValidationRules,omitempty"`
// tls is the configuration for communicating with the OIDC provider via TLS.
// +optional
TLS *TLSSpec `json:"tls,omitempty"`
}
// ClaimValidationRule provides the configuration for a single claim validation rule.
type ClaimValidationRule struct {
// claim is the name of a required claim.
// Same as --oidc-required-claim flag.
// Only string claim keys are supported.
// Mutually exclusive with expression and message.
// +optional
Claim string `json:"claim,omitempty"`
// requiredValue is the value of a required claim.
// Same as --oidc-required-claim flag.
// Only string claim values are supported.
// If claim is set and requiredValue is not set, the claim must be present with a value set to the empty string.
// Mutually exclusive with expression and message.
// +optional
RequiredValue string `json:"requiredValue,omitempty"`
// expression represents the expression which will be evaluated by CEL.
// Must produce a boolean.
//
// CEL expressions have access to the contents of the token claims, organized into CEL variable:
// - 'claims' is a map of claim names to claim values.
// For example, a variable named 'sub' can be accessed as 'claims.sub'.
// Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'.
// Must return true for the validation to pass.
//
// Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
//
// Mutually exclusive with claim and requiredValue.
// +optional
Expression string `json:"expression,omitempty"`
// message customizes the returned error message when expression returns false.
// message is a literal string.
// Mutually exclusive with claim and requiredValue.
// +optional
Message string `json:"message,omitempty"`
}
// UserValidationRule provides the configuration for a single user info validation rule.
type UserValidationRule struct {
// expression represents the expression which will be evaluated by CEL.
// Must return true for the validation to pass.
//
// CEL expressions have access to the contents of UserInfo, organized into CEL variable:
// - 'user' - authentication.k8s.io/v1, Kind=UserInfo object
// Refer to https://github.com/kubernetes/api/blob/release-1.28/authentication/v1/types.go#L105-L122 for the definition.
// API documentation: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#userinfo-v1-authentication-k8s-io
//
// Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
//
// +required
Expression string `json:"expression"`
// message customizes the returned error message when rule returns false.
// message is a literal string.
// +optional
Message string `json:"message,omitempty"`
}
// JWTTokenClaims allows customization of the claims that will be mapped to user identity
// for Kubernetes access.
type JWTTokenClaims struct {
// Groups is the name of the claim which should be read to extract the user's
// groups is the name of the claim which should be read to extract the user's
// group membership from the JWT token. When not specified, it will default to "groups".
// +optional
Groups string `json:"groups"`
// Username is the name of the claim which should be read to extract the
// username is the name of the claim which should be read to extract the
// username from the JWT token. When not specified, it will default to "username".
// +optional
Username string `json:"username"`
// extra is similar to claimMappings.extra from Kubernetes AuthenticationConfiguration as documented in
// https://kubernetes.io/docs/reference/access-authn-authz/authentication. However, note that the
// Pinniped Concierge issues client certificates to users for the purpose of authenticating, and
// the Kubernetes API server does not have any mechanism for transmitting auth extras via client
// certificates. When configured, these extras will appear in client certificates issued by the
// Pinniped Supervisor in the x509 Subject field as Organizational Units (OU). However, when this
// client certificate is presented to Kubernetes for authentication, Kubernetes will ignore these
// extras. This is probably only useful if you are using a custom authenticating proxy in front
// of your Kubernetes API server which can translate these OUs into auth extras, as described by
// https://kubernetes.io/docs/reference/access-authn-authz/authentication/#authenticating-proxy.
// This is an advanced configuration option. During an end-user login flow, each of these CEL expressions
// must evaluate to either a string or an array of strings, or else the user's login will fail.
// +optional
Extra []ExtraMapping `json:"extra,omitempty"`
}
// ExtraMapping provides the configuration for a single extra mapping.
type ExtraMapping struct {
// key is a string to use as the extra attribute key.
// key must be a domain-prefix path (e.g. example.org/foo). All characters before the first "/" must be a valid
// subdomain as defined by RFC 1123. All characters trailing the first "/" must
// be valid HTTP Path characters as defined by RFC 3986.
// key must be lowercase.
// Required to be unique.
// +required
Key string `json:"key"`
// valueExpression is a CEL expression to extract extra attribute value.
// valueExpression must produce a string or string array value.
// "", [], and null values are treated as the extra mapping not being present.
// Empty string values contained within a string array are filtered out.
//
// CEL expressions have access to the contents of the token claims, organized into CEL variable:
// - 'claims' is a map of claim names to claim values.
// For example, a variable named 'sub' can be accessed as 'claims.sub'.
// Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'.
//
// Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
//
// +required
ValueExpression string `json:"valueExpression"`
}
// JWTAuthenticator describes the configuration of a JWT authenticator.
@@ -86,14 +208,14 @@ type JWTAuthenticator struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
// Spec for configuring the authenticator.
// spec for configuring the authenticator.
Spec JWTAuthenticatorSpec `json:"spec"`
// Status of the authenticator.
// status of the authenticator.
Status JWTAuthenticatorStatus `json:"status,omitempty"`
}
// List of JWTAuthenticator objects.
// JWTAuthenticatorList is a list of JWTAuthenticator objects.
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type JWTAuthenticatorList struct {
metav1.TypeMeta `json:",inline"`

View File

@@ -29,6 +29,38 @@ func (in *CertificateAuthorityDataSourceSpec) DeepCopy() *CertificateAuthorityDa
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ClaimValidationRule) DeepCopyInto(out *ClaimValidationRule) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClaimValidationRule.
func (in *ClaimValidationRule) DeepCopy() *ClaimValidationRule {
if in == nil {
return nil
}
out := new(ClaimValidationRule)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ExtraMapping) DeepCopyInto(out *ExtraMapping) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExtraMapping.
func (in *ExtraMapping) DeepCopy() *ExtraMapping {
if in == nil {
return nil
}
out := new(ExtraMapping)
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
@@ -93,7 +125,17 @@ func (in *JWTAuthenticatorList) DeepCopyObject() runtime.Object {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *JWTAuthenticatorSpec) DeepCopyInto(out *JWTAuthenticatorSpec) {
*out = *in
out.Claims = in.Claims
in.Claims.DeepCopyInto(&out.Claims)
if in.ClaimValidationRules != nil {
in, out := &in.ClaimValidationRules, &out.ClaimValidationRules
*out = make([]ClaimValidationRule, len(*in))
copy(*out, *in)
}
if in.UserValidationRules != nil {
in, out := &in.UserValidationRules, &out.UserValidationRules
*out = make([]UserValidationRule, len(*in))
copy(*out, *in)
}
if in.TLS != nil {
in, out := &in.TLS, &out.TLS
*out = new(TLSSpec)
@@ -138,6 +180,11 @@ func (in *JWTAuthenticatorStatus) DeepCopy() *JWTAuthenticatorStatus {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *JWTTokenClaims) DeepCopyInto(out *JWTTokenClaims) {
*out = *in
if in.Extra != nil {
in, out := &in.Extra, &out.Extra
*out = make([]ExtraMapping, len(*in))
copy(*out, *in)
}
return
}
@@ -172,6 +219,22 @@ func (in *TLSSpec) DeepCopy() *TLSSpec {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *UserValidationRule) DeepCopyInto(out *UserValidationRule) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UserValidationRule.
func (in *UserValidationRule) DeepCopy() *UserValidationRule {
if in == nil {
return nil
}
out := new(UserValidationRule)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *WebhookAuthenticator) DeepCopyInto(out *WebhookAuthenticator) {
*out = *in

View File

@@ -58,37 +58,133 @@ spec:
metadata:
type: object
spec:
description: Spec for configuring the authenticator.
description: spec for configuring the authenticator.
properties:
audience:
description: Audience is the required value of the "aud" JWT claim.
description: audience is the required value of the "aud" JWT claim.
minLength: 1
type: string
claimValidationRules:
description: |-
claimValidationRules are rules that are applied to validate token claims to authenticate users.
This is similar to claimValidationRules from Kubernetes AuthenticationConfiguration as documented in
https://kubernetes.io/docs/reference/access-authn-authz/authentication.
This is an advanced configuration option. During an end-user login flow, mistakes in this
configuration will cause the user's login to fail.
items:
description: ClaimValidationRule provides the configuration for
a single claim validation rule.
properties:
claim:
description: |-
claim is the name of a required claim.
Same as --oidc-required-claim flag.
Only string claim keys are supported.
Mutually exclusive with expression and message.
type: string
expression:
description: |-
expression represents the expression which will be evaluated by CEL.
Must produce a boolean.
CEL expressions have access to the contents of the token claims, organized into CEL variable:
- 'claims' is a map of claim names to claim values.
For example, a variable named 'sub' can be accessed as 'claims.sub'.
Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'.
Must return true for the validation to pass.
Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
Mutually exclusive with claim and requiredValue.
type: string
message:
description: |-
message customizes the returned error message when expression returns false.
message is a literal string.
Mutually exclusive with claim and requiredValue.
type: string
requiredValue:
description: |-
requiredValue is the value of a required claim.
Same as --oidc-required-claim flag.
Only string claim values are supported.
If claim is set and requiredValue is not set, the claim must be present with a value set to the empty string.
Mutually exclusive with expression and message.
type: string
type: object
type: array
claims:
description: |-
Claims allows customization of the claims that will be mapped to user identity
claims allows customization of the claims that will be mapped to user identity
for Kubernetes access.
properties:
extra:
description: |-
extra is similar to claimMappings.extra from Kubernetes AuthenticationConfiguration as documented in
https://kubernetes.io/docs/reference/access-authn-authz/authentication. However, note that the
Pinniped Concierge issues client certificates to users for the purpose of authenticating, and
the Kubernetes API server does not have any mechanism for transmitting auth extras via client
certificates. When configured, these extras will appear in client certificates issued by the
Pinniped Supervisor in the x509 Subject field as Organizational Units (OU). However, when this
client certificate is presented to Kubernetes for authentication, Kubernetes will ignore these
extras. This is probably only useful if you are using a custom authenticating proxy in front
of your Kubernetes API server which can translate these OUs into auth extras, as described by
https://kubernetes.io/docs/reference/access-authn-authz/authentication/#authenticating-proxy.
This is an advanced configuration option. During an end-user login flow, each of these CEL expressions
must evaluate to either a string or an array of strings, or else the user's login will fail.
items:
description: ExtraMapping provides the configuration for a single
extra mapping.
properties:
key:
description: |-
key is a string to use as the extra attribute key.
key must be a domain-prefix path (e.g. example.org/foo). All characters before the first "/" must be a valid
subdomain as defined by RFC 1123. All characters trailing the first "/" must
be valid HTTP Path characters as defined by RFC 3986.
key must be lowercase.
Required to be unique.
type: string
valueExpression:
description: |-
valueExpression is a CEL expression to extract extra attribute value.
valueExpression must produce a string or string array value.
"", [], and null values are treated as the extra mapping not being present.
Empty string values contained within a string array are filtered out.
CEL expressions have access to the contents of the token claims, organized into CEL variable:
- 'claims' is a map of claim names to claim values.
For example, a variable named 'sub' can be accessed as 'claims.sub'.
Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'.
Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
type: string
required:
- key
- valueExpression
type: object
type: array
groups:
description: |-
Groups is the name of the claim which should be read to extract the user's
groups is the name of the claim which should be read to extract the user's
group membership from the JWT token. When not specified, it will default to "groups".
type: string
username:
description: |-
Username is the name of the claim which should be read to extract the
username is the name of the claim which should be read to extract the
username from the JWT token. When not specified, it will default to "username".
type: string
type: object
issuer:
description: |-
Issuer is the OIDC issuer URL that will be used to discover public signing keys. Issuer is
issuer is the OIDC issuer URL that will be used to discover public signing keys. Issuer is
also used to validate the "iss" JWT claim.
minLength: 1
pattern: ^https://
type: string
tls:
description: TLS configuration for communicating with the OIDC provider.
description: tls is the configuration for communicating with the OIDC
provider via TLS.
properties:
certificateAuthorityData:
description: X.509 Certificate Authority (base64-encoded PEM bundle).
@@ -128,12 +224,47 @@ spec:
- name
type: object
type: object
userValidationRules:
description: |-
userValidationRules are rules that are applied to final user before completing authentication.
These allow invariants to be applied to incoming identities such as preventing the
use of the system: prefix that is commonly used by Kubernetes components.
The validation rules are logically ANDed together and must all return true for the validation to pass.
This is similar to claimValidationRules from Kubernetes AuthenticationConfiguration as documented in
https://kubernetes.io/docs/reference/access-authn-authz/authentication.
This is an advanced configuration option. During an end-user login flow, mistakes in this
configuration will cause the user's login to fail.
items:
description: UserValidationRule provides the configuration for a
single user info validation rule.
properties:
expression:
description: |-
expression represents the expression which will be evaluated by CEL.
Must return true for the validation to pass.
CEL expressions have access to the contents of UserInfo, organized into CEL variable:
- 'user' - authentication.k8s.io/v1, Kind=UserInfo object
Refer to https://github.com/kubernetes/api/blob/release-1.28/authentication/v1/types.go#L105-L122 for the definition.
API documentation: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#userinfo-v1-authentication-k8s-io
Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
type: string
message:
description: |-
message customizes the returned error message when rule returns false.
message is a literal string.
type: string
required:
- expression
type: object
type: array
required:
- audience
- issuer
type: object
status:
description: Status of the authenticator.
description: status of the authenticator.
properties:
conditions:
description: Represents the observations of the authenticator's current

View File

@@ -60,6 +60,79 @@ certificate bundle. +
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-concierge-authentication-v1alpha1-claimvalidationrule"]
==== ClaimValidationRule
ClaimValidationRule provides the configuration for a single claim validation rule.
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-concierge-authentication-v1alpha1-jwtauthenticatorspec[$$JWTAuthenticatorSpec$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`claim`* __string__ | claim is the name of a required claim. +
Same as --oidc-required-claim flag. +
Only string claim keys are supported. +
Mutually exclusive with expression and message. +
| *`requiredValue`* __string__ | requiredValue is the value of a required claim. +
Same as --oidc-required-claim flag. +
Only string claim values are supported. +
If claim is set and requiredValue is not set, the claim must be present with a value set to the empty string. +
Mutually exclusive with expression and message. +
| *`expression`* __string__ | expression represents the expression which will be evaluated by CEL. +
Must produce a boolean. +
CEL expressions have access to the contents of the token claims, organized into CEL variable: +
- 'claims' is a map of claim names to claim values. +
For example, a variable named 'sub' can be accessed as 'claims.sub'. +
Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'. +
Must return true for the validation to pass. +
Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ +
Mutually exclusive with claim and requiredValue. +
| *`message`* __string__ | message customizes the returned error message when expression returns false. +
message is a literal string. +
Mutually exclusive with claim and requiredValue. +
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-concierge-authentication-v1alpha1-extramapping"]
==== ExtraMapping
ExtraMapping provides the configuration for a single extra mapping.
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-concierge-authentication-v1alpha1-jwttokenclaims[$$JWTTokenClaims$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`key`* __string__ | key is a string to use as the extra attribute key. +
key must be a domain-prefix path (e.g. example.org/foo). All characters before the first "/" must be a valid +
subdomain as defined by RFC 1123. All characters trailing the first "/" must +
be valid HTTP Path characters as defined by RFC 3986. +
key must be lowercase. +
Required to be unique. +
| *`valueExpression`* __string__ | valueExpression is a CEL expression to extract extra attribute value. +
valueExpression must produce a string or string array value. +
"", [], and null values are treated as the extra mapping not being present. +
Empty string values contained within a string array are filtered out. +
CEL expressions have access to the contents of the token claims, organized into CEL variable: +
- 'claims' is a map of claim names to claim values. +
For example, a variable named 'sub' can be accessed as 'claims.sub'. +
Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'. +
Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ +
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-concierge-authentication-v1alpha1-jwtauthenticator"]
==== JWTAuthenticator
@@ -78,8 +151,8 @@ signature, existence of claims, etc.) and extract the username and groups from t
| Field | Description
| *`metadata`* __link:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.3/#objectmeta-v1-meta[$$ObjectMeta$$]__ | Refer to Kubernetes API documentation for fields of `metadata`.
| *`spec`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-concierge-authentication-v1alpha1-jwtauthenticatorspec[$$JWTAuthenticatorSpec$$]__ | Spec for configuring the authenticator. +
| *`status`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-concierge-authentication-v1alpha1-jwtauthenticatorstatus[$$JWTAuthenticatorStatus$$]__ | Status of the authenticator. +
| *`spec`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-concierge-authentication-v1alpha1-jwtauthenticatorspec[$$JWTAuthenticatorSpec$$]__ | spec for configuring the authenticator. +
| *`status`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-concierge-authentication-v1alpha1-jwtauthenticatorstatus[$$JWTAuthenticatorStatus$$]__ | status of the authenticator. +
|===
@@ -100,7 +173,7 @@ signature, existence of claims, etc.) and extract the username and groups from t
[id="{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-concierge-authentication-v1alpha1-jwtauthenticatorspec"]
==== JWTAuthenticatorSpec
Spec for configuring a JWT authenticator.
JWTAuthenticatorSpec is the spec for configuring a JWT authenticator.
.Appears In:
****
@@ -110,19 +183,32 @@ Spec for configuring a JWT authenticator.
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`issuer`* __string__ | Issuer is the OIDC issuer URL that will be used to discover public signing keys. Issuer is +
| *`issuer`* __string__ | issuer is the OIDC issuer URL that will be used to discover public signing keys. Issuer is +
also used to validate the "iss" JWT claim. +
| *`audience`* __string__ | Audience is the required value of the "aud" JWT claim. +
| *`claims`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-concierge-authentication-v1alpha1-jwttokenclaims[$$JWTTokenClaims$$]__ | Claims allows customization of the claims that will be mapped to user identity +
| *`audience`* __string__ | audience is the required value of the "aud" JWT claim. +
| *`claims`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-concierge-authentication-v1alpha1-jwttokenclaims[$$JWTTokenClaims$$]__ | claims allows customization of the claims that will be mapped to user identity +
for Kubernetes access. +
| *`tls`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-concierge-authentication-v1alpha1-tlsspec[$$TLSSpec$$]__ | TLS configuration for communicating with the OIDC provider. +
| *`claimValidationRules`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-concierge-authentication-v1alpha1-claimvalidationrule[$$ClaimValidationRule$$] array__ | claimValidationRules are rules that are applied to validate token claims to authenticate users. +
This is similar to claimValidationRules from Kubernetes AuthenticationConfiguration as documented in +
https://kubernetes.io/docs/reference/access-authn-authz/authentication. +
This is an advanced configuration option. During an end-user login flow, mistakes in this +
configuration will cause the user's login to fail. +
| *`userValidationRules`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-concierge-authentication-v1alpha1-uservalidationrule[$$UserValidationRule$$] array__ | userValidationRules are rules that are applied to final user before completing authentication. +
These allow invariants to be applied to incoming identities such as preventing the +
use of the system: prefix that is commonly used by Kubernetes components. +
The validation rules are logically ANDed together and must all return true for the validation to pass. +
This is similar to claimValidationRules from Kubernetes AuthenticationConfiguration as documented in +
https://kubernetes.io/docs/reference/access-authn-authz/authentication. +
This is an advanced configuration option. During an end-user login flow, mistakes in this +
configuration will cause the user's login to fail. +
| *`tls`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-concierge-authentication-v1alpha1-tlsspec[$$TLSSpec$$]__ | tls is the configuration for communicating with the OIDC provider via TLS. +
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-concierge-authentication-v1alpha1-jwtauthenticatorstatus"]
==== JWTAuthenticatorStatus
Status of a JWT authenticator.
JWTAuthenticatorStatus is the status of a JWT authenticator.
.Appears In:
****
@@ -151,10 +237,22 @@ for Kubernetes access.
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`groups`* __string__ | Groups is the name of the claim which should be read to extract the user's +
| *`groups`* __string__ | groups is the name of the claim which should be read to extract the user's +
group membership from the JWT token. When not specified, it will default to "groups". +
| *`username`* __string__ | Username is the name of the claim which should be read to extract the +
| *`username`* __string__ | username is the name of the claim which should be read to extract the +
username from the JWT token. When not specified, it will default to "username". +
| *`extra`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-concierge-authentication-v1alpha1-extramapping[$$ExtraMapping$$] array__ | extra is similar to claimMappings.extra from Kubernetes AuthenticationConfiguration as documented in +
https://kubernetes.io/docs/reference/access-authn-authz/authentication. However, note that the +
Pinniped Concierge issues client certificates to users for the purpose of authenticating, and +
the Kubernetes API server does not have any mechanism for transmitting auth extras via client +
certificates. When configured, these extras will appear in client certificates issued by the +
Pinniped Supervisor in the x509 Subject field as Organizational Units (OU). However, when this +
client certificate is presented to Kubernetes for authentication, Kubernetes will ignore these +
extras. This is probably only useful if you are using a custom authenticating proxy in front +
of your Kubernetes API server which can translate these OUs into auth extras, as described by +
https://kubernetes.io/docs/reference/access-authn-authz/authentication/#authenticating-proxy. +
This is an advanced configuration option. During an end-user login flow, each of these CEL expressions +
must evaluate to either a string or an array of strings, or else the user's login will fail. +
|===
@@ -178,6 +276,33 @@ Any changes to the CA bundle in the secret or configmap will be dynamically relo
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-concierge-authentication-v1alpha1-uservalidationrule"]
==== UserValidationRule
UserValidationRule provides the configuration for a single user info validation rule.
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-concierge-authentication-v1alpha1-jwtauthenticatorspec[$$JWTAuthenticatorSpec$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`expression`* __string__ | expression represents the expression which will be evaluated by CEL. +
Must return true for the validation to pass. +
CEL expressions have access to the contents of UserInfo, organized into CEL variable: +
- 'user' - authentication.k8s.io/v1, Kind=UserInfo object +
Refer to https://github.com/kubernetes/api/blob/release-1.28/authentication/v1/types.go#L105-L122 for the definition. +
API documentation: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#userinfo-v1-authentication-k8s-io +
Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ +
| *`message`* __string__ | message customizes the returned error message when rule returns false. +
message is a literal string. +
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-30-apis-concierge-authentication-v1alpha1-webhookauthenticator"]
==== WebhookAuthenticator

View File

@@ -18,7 +18,7 @@ const (
JWTAuthenticatorPhaseError JWTAuthenticatorPhase = "Error"
)
// Status of a JWT authenticator.
// JWTAuthenticatorStatus is the status of a JWT authenticator.
type JWTAuthenticatorStatus struct {
// Represents the observations of the authenticator's current state.
// +patchMergeKey=type
@@ -26,46 +26,168 @@ type JWTAuthenticatorStatus struct {
// +listType=map
// +listMapKey=type
Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"`
// Phase summarizes the overall status of the JWTAuthenticator.
// +kubebuilder:default=Pending
// +kubebuilder:validation:Enum=Pending;Ready;Error
Phase JWTAuthenticatorPhase `json:"phase,omitempty"`
}
// Spec for configuring a JWT authenticator.
// JWTAuthenticatorSpec is the spec for configuring a JWT authenticator.
type JWTAuthenticatorSpec struct {
// Issuer is the OIDC issuer URL that will be used to discover public signing keys. Issuer is
// issuer is the OIDC issuer URL that will be used to discover public signing keys. Issuer is
// also used to validate the "iss" JWT claim.
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:Pattern=`^https://`
Issuer string `json:"issuer"`
// Audience is the required value of the "aud" JWT claim.
// audience is the required value of the "aud" JWT claim.
// +kubebuilder:validation:MinLength=1
Audience string `json:"audience"`
// Claims allows customization of the claims that will be mapped to user identity
// claims allows customization of the claims that will be mapped to user identity
// for Kubernetes access.
// +optional
Claims JWTTokenClaims `json:"claims"`
// TLS configuration for communicating with the OIDC provider.
// claimValidationRules are rules that are applied to validate token claims to authenticate users.
// This is similar to claimValidationRules from Kubernetes AuthenticationConfiguration as documented in
// https://kubernetes.io/docs/reference/access-authn-authz/authentication.
// This is an advanced configuration option. During an end-user login flow, mistakes in this
// configuration will cause the user's login to fail.
// +optional
ClaimValidationRules []ClaimValidationRule `json:"claimValidationRules,omitempty"`
// userValidationRules are rules that are applied to final user before completing authentication.
// These allow invariants to be applied to incoming identities such as preventing the
// use of the system: prefix that is commonly used by Kubernetes components.
// The validation rules are logically ANDed together and must all return true for the validation to pass.
// This is similar to claimValidationRules from Kubernetes AuthenticationConfiguration as documented in
// https://kubernetes.io/docs/reference/access-authn-authz/authentication.
// This is an advanced configuration option. During an end-user login flow, mistakes in this
// configuration will cause the user's login to fail.
// +optional
UserValidationRules []UserValidationRule `json:"userValidationRules,omitempty"`
// tls is the configuration for communicating with the OIDC provider via TLS.
// +optional
TLS *TLSSpec `json:"tls,omitempty"`
}
// ClaimValidationRule provides the configuration for a single claim validation rule.
type ClaimValidationRule struct {
// claim is the name of a required claim.
// Same as --oidc-required-claim flag.
// Only string claim keys are supported.
// Mutually exclusive with expression and message.
// +optional
Claim string `json:"claim,omitempty"`
// requiredValue is the value of a required claim.
// Same as --oidc-required-claim flag.
// Only string claim values are supported.
// If claim is set and requiredValue is not set, the claim must be present with a value set to the empty string.
// Mutually exclusive with expression and message.
// +optional
RequiredValue string `json:"requiredValue,omitempty"`
// expression represents the expression which will be evaluated by CEL.
// Must produce a boolean.
//
// CEL expressions have access to the contents of the token claims, organized into CEL variable:
// - 'claims' is a map of claim names to claim values.
// For example, a variable named 'sub' can be accessed as 'claims.sub'.
// Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'.
// Must return true for the validation to pass.
//
// Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
//
// Mutually exclusive with claim and requiredValue.
// +optional
Expression string `json:"expression,omitempty"`
// message customizes the returned error message when expression returns false.
// message is a literal string.
// Mutually exclusive with claim and requiredValue.
// +optional
Message string `json:"message,omitempty"`
}
// UserValidationRule provides the configuration for a single user info validation rule.
type UserValidationRule struct {
// expression represents the expression which will be evaluated by CEL.
// Must return true for the validation to pass.
//
// CEL expressions have access to the contents of UserInfo, organized into CEL variable:
// - 'user' - authentication.k8s.io/v1, Kind=UserInfo object
// Refer to https://github.com/kubernetes/api/blob/release-1.28/authentication/v1/types.go#L105-L122 for the definition.
// API documentation: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#userinfo-v1-authentication-k8s-io
//
// Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
//
// +required
Expression string `json:"expression"`
// message customizes the returned error message when rule returns false.
// message is a literal string.
// +optional
Message string `json:"message,omitempty"`
}
// JWTTokenClaims allows customization of the claims that will be mapped to user identity
// for Kubernetes access.
type JWTTokenClaims struct {
// Groups is the name of the claim which should be read to extract the user's
// groups is the name of the claim which should be read to extract the user's
// group membership from the JWT token. When not specified, it will default to "groups".
// +optional
Groups string `json:"groups"`
// Username is the name of the claim which should be read to extract the
// username is the name of the claim which should be read to extract the
// username from the JWT token. When not specified, it will default to "username".
// +optional
Username string `json:"username"`
// extra is similar to claimMappings.extra from Kubernetes AuthenticationConfiguration as documented in
// https://kubernetes.io/docs/reference/access-authn-authz/authentication. However, note that the
// Pinniped Concierge issues client certificates to users for the purpose of authenticating, and
// the Kubernetes API server does not have any mechanism for transmitting auth extras via client
// certificates. When configured, these extras will appear in client certificates issued by the
// Pinniped Supervisor in the x509 Subject field as Organizational Units (OU). However, when this
// client certificate is presented to Kubernetes for authentication, Kubernetes will ignore these
// extras. This is probably only useful if you are using a custom authenticating proxy in front
// of your Kubernetes API server which can translate these OUs into auth extras, as described by
// https://kubernetes.io/docs/reference/access-authn-authz/authentication/#authenticating-proxy.
// This is an advanced configuration option. During an end-user login flow, each of these CEL expressions
// must evaluate to either a string or an array of strings, or else the user's login will fail.
// +optional
Extra []ExtraMapping `json:"extra,omitempty"`
}
// ExtraMapping provides the configuration for a single extra mapping.
type ExtraMapping struct {
// key is a string to use as the extra attribute key.
// key must be a domain-prefix path (e.g. example.org/foo). All characters before the first "/" must be a valid
// subdomain as defined by RFC 1123. All characters trailing the first "/" must
// be valid HTTP Path characters as defined by RFC 3986.
// key must be lowercase.
// Required to be unique.
// +required
Key string `json:"key"`
// valueExpression is a CEL expression to extract extra attribute value.
// valueExpression must produce a string or string array value.
// "", [], and null values are treated as the extra mapping not being present.
// Empty string values contained within a string array are filtered out.
//
// CEL expressions have access to the contents of the token claims, organized into CEL variable:
// - 'claims' is a map of claim names to claim values.
// For example, a variable named 'sub' can be accessed as 'claims.sub'.
// Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'.
//
// Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
//
// +required
ValueExpression string `json:"valueExpression"`
}
// JWTAuthenticator describes the configuration of a JWT authenticator.
@@ -86,14 +208,14 @@ type JWTAuthenticator struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
// Spec for configuring the authenticator.
// spec for configuring the authenticator.
Spec JWTAuthenticatorSpec `json:"spec"`
// Status of the authenticator.
// status of the authenticator.
Status JWTAuthenticatorStatus `json:"status,omitempty"`
}
// List of JWTAuthenticator objects.
// JWTAuthenticatorList is a list of JWTAuthenticator objects.
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type JWTAuthenticatorList struct {
metav1.TypeMeta `json:",inline"`

View File

@@ -29,6 +29,38 @@ func (in *CertificateAuthorityDataSourceSpec) DeepCopy() *CertificateAuthorityDa
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ClaimValidationRule) DeepCopyInto(out *ClaimValidationRule) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClaimValidationRule.
func (in *ClaimValidationRule) DeepCopy() *ClaimValidationRule {
if in == nil {
return nil
}
out := new(ClaimValidationRule)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ExtraMapping) DeepCopyInto(out *ExtraMapping) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExtraMapping.
func (in *ExtraMapping) DeepCopy() *ExtraMapping {
if in == nil {
return nil
}
out := new(ExtraMapping)
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
@@ -93,7 +125,17 @@ func (in *JWTAuthenticatorList) DeepCopyObject() runtime.Object {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *JWTAuthenticatorSpec) DeepCopyInto(out *JWTAuthenticatorSpec) {
*out = *in
out.Claims = in.Claims
in.Claims.DeepCopyInto(&out.Claims)
if in.ClaimValidationRules != nil {
in, out := &in.ClaimValidationRules, &out.ClaimValidationRules
*out = make([]ClaimValidationRule, len(*in))
copy(*out, *in)
}
if in.UserValidationRules != nil {
in, out := &in.UserValidationRules, &out.UserValidationRules
*out = make([]UserValidationRule, len(*in))
copy(*out, *in)
}
if in.TLS != nil {
in, out := &in.TLS, &out.TLS
*out = new(TLSSpec)
@@ -138,6 +180,11 @@ func (in *JWTAuthenticatorStatus) DeepCopy() *JWTAuthenticatorStatus {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *JWTTokenClaims) DeepCopyInto(out *JWTTokenClaims) {
*out = *in
if in.Extra != nil {
in, out := &in.Extra, &out.Extra
*out = make([]ExtraMapping, len(*in))
copy(*out, *in)
}
return
}
@@ -172,6 +219,22 @@ func (in *TLSSpec) DeepCopy() *TLSSpec {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *UserValidationRule) DeepCopyInto(out *UserValidationRule) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UserValidationRule.
func (in *UserValidationRule) DeepCopy() *UserValidationRule {
if in == nil {
return nil
}
out := new(UserValidationRule)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *WebhookAuthenticator) DeepCopyInto(out *WebhookAuthenticator) {
*out = *in

View File

@@ -58,37 +58,133 @@ spec:
metadata:
type: object
spec:
description: Spec for configuring the authenticator.
description: spec for configuring the authenticator.
properties:
audience:
description: Audience is the required value of the "aud" JWT claim.
description: audience is the required value of the "aud" JWT claim.
minLength: 1
type: string
claimValidationRules:
description: |-
claimValidationRules are rules that are applied to validate token claims to authenticate users.
This is similar to claimValidationRules from Kubernetes AuthenticationConfiguration as documented in
https://kubernetes.io/docs/reference/access-authn-authz/authentication.
This is an advanced configuration option. During an end-user login flow, mistakes in this
configuration will cause the user's login to fail.
items:
description: ClaimValidationRule provides the configuration for
a single claim validation rule.
properties:
claim:
description: |-
claim is the name of a required claim.
Same as --oidc-required-claim flag.
Only string claim keys are supported.
Mutually exclusive with expression and message.
type: string
expression:
description: |-
expression represents the expression which will be evaluated by CEL.
Must produce a boolean.
CEL expressions have access to the contents of the token claims, organized into CEL variable:
- 'claims' is a map of claim names to claim values.
For example, a variable named 'sub' can be accessed as 'claims.sub'.
Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'.
Must return true for the validation to pass.
Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
Mutually exclusive with claim and requiredValue.
type: string
message:
description: |-
message customizes the returned error message when expression returns false.
message is a literal string.
Mutually exclusive with claim and requiredValue.
type: string
requiredValue:
description: |-
requiredValue is the value of a required claim.
Same as --oidc-required-claim flag.
Only string claim values are supported.
If claim is set and requiredValue is not set, the claim must be present with a value set to the empty string.
Mutually exclusive with expression and message.
type: string
type: object
type: array
claims:
description: |-
Claims allows customization of the claims that will be mapped to user identity
claims allows customization of the claims that will be mapped to user identity
for Kubernetes access.
properties:
extra:
description: |-
extra is similar to claimMappings.extra from Kubernetes AuthenticationConfiguration as documented in
https://kubernetes.io/docs/reference/access-authn-authz/authentication. However, note that the
Pinniped Concierge issues client certificates to users for the purpose of authenticating, and
the Kubernetes API server does not have any mechanism for transmitting auth extras via client
certificates. When configured, these extras will appear in client certificates issued by the
Pinniped Supervisor in the x509 Subject field as Organizational Units (OU). However, when this
client certificate is presented to Kubernetes for authentication, Kubernetes will ignore these
extras. This is probably only useful if you are using a custom authenticating proxy in front
of your Kubernetes API server which can translate these OUs into auth extras, as described by
https://kubernetes.io/docs/reference/access-authn-authz/authentication/#authenticating-proxy.
This is an advanced configuration option. During an end-user login flow, each of these CEL expressions
must evaluate to either a string or an array of strings, or else the user's login will fail.
items:
description: ExtraMapping provides the configuration for a single
extra mapping.
properties:
key:
description: |-
key is a string to use as the extra attribute key.
key must be a domain-prefix path (e.g. example.org/foo). All characters before the first "/" must be a valid
subdomain as defined by RFC 1123. All characters trailing the first "/" must
be valid HTTP Path characters as defined by RFC 3986.
key must be lowercase.
Required to be unique.
type: string
valueExpression:
description: |-
valueExpression is a CEL expression to extract extra attribute value.
valueExpression must produce a string or string array value.
"", [], and null values are treated as the extra mapping not being present.
Empty string values contained within a string array are filtered out.
CEL expressions have access to the contents of the token claims, organized into CEL variable:
- 'claims' is a map of claim names to claim values.
For example, a variable named 'sub' can be accessed as 'claims.sub'.
Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'.
Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
type: string
required:
- key
- valueExpression
type: object
type: array
groups:
description: |-
Groups is the name of the claim which should be read to extract the user's
groups is the name of the claim which should be read to extract the user's
group membership from the JWT token. When not specified, it will default to "groups".
type: string
username:
description: |-
Username is the name of the claim which should be read to extract the
username is the name of the claim which should be read to extract the
username from the JWT token. When not specified, it will default to "username".
type: string
type: object
issuer:
description: |-
Issuer is the OIDC issuer URL that will be used to discover public signing keys. Issuer is
issuer is the OIDC issuer URL that will be used to discover public signing keys. Issuer is
also used to validate the "iss" JWT claim.
minLength: 1
pattern: ^https://
type: string
tls:
description: TLS configuration for communicating with the OIDC provider.
description: tls is the configuration for communicating with the OIDC
provider via TLS.
properties:
certificateAuthorityData:
description: X.509 Certificate Authority (base64-encoded PEM bundle).
@@ -128,12 +224,47 @@ spec:
- name
type: object
type: object
userValidationRules:
description: |-
userValidationRules are rules that are applied to final user before completing authentication.
These allow invariants to be applied to incoming identities such as preventing the
use of the system: prefix that is commonly used by Kubernetes components.
The validation rules are logically ANDed together and must all return true for the validation to pass.
This is similar to claimValidationRules from Kubernetes AuthenticationConfiguration as documented in
https://kubernetes.io/docs/reference/access-authn-authz/authentication.
This is an advanced configuration option. During an end-user login flow, mistakes in this
configuration will cause the user's login to fail.
items:
description: UserValidationRule provides the configuration for a
single user info validation rule.
properties:
expression:
description: |-
expression represents the expression which will be evaluated by CEL.
Must return true for the validation to pass.
CEL expressions have access to the contents of UserInfo, organized into CEL variable:
- 'user' - authentication.k8s.io/v1, Kind=UserInfo object
Refer to https://github.com/kubernetes/api/blob/release-1.28/authentication/v1/types.go#L105-L122 for the definition.
API documentation: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#userinfo-v1-authentication-k8s-io
Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
type: string
message:
description: |-
message customizes the returned error message when rule returns false.
message is a literal string.
type: string
required:
- expression
type: object
type: array
required:
- audience
- issuer
type: object
status:
description: Status of the authenticator.
description: status of the authenticator.
properties:
conditions:
description: Represents the observations of the authenticator's current

View File

@@ -60,6 +60,79 @@ certificate bundle. +
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-31-apis-concierge-authentication-v1alpha1-claimvalidationrule"]
==== ClaimValidationRule
ClaimValidationRule provides the configuration for a single claim validation rule.
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-31-apis-concierge-authentication-v1alpha1-jwtauthenticatorspec[$$JWTAuthenticatorSpec$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`claim`* __string__ | claim is the name of a required claim. +
Same as --oidc-required-claim flag. +
Only string claim keys are supported. +
Mutually exclusive with expression and message. +
| *`requiredValue`* __string__ | requiredValue is the value of a required claim. +
Same as --oidc-required-claim flag. +
Only string claim values are supported. +
If claim is set and requiredValue is not set, the claim must be present with a value set to the empty string. +
Mutually exclusive with expression and message. +
| *`expression`* __string__ | expression represents the expression which will be evaluated by CEL. +
Must produce a boolean. +
CEL expressions have access to the contents of the token claims, organized into CEL variable: +
- 'claims' is a map of claim names to claim values. +
For example, a variable named 'sub' can be accessed as 'claims.sub'. +
Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'. +
Must return true for the validation to pass. +
Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ +
Mutually exclusive with claim and requiredValue. +
| *`message`* __string__ | message customizes the returned error message when expression returns false. +
message is a literal string. +
Mutually exclusive with claim and requiredValue. +
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-31-apis-concierge-authentication-v1alpha1-extramapping"]
==== ExtraMapping
ExtraMapping provides the configuration for a single extra mapping.
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-31-apis-concierge-authentication-v1alpha1-jwttokenclaims[$$JWTTokenClaims$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`key`* __string__ | key is a string to use as the extra attribute key. +
key must be a domain-prefix path (e.g. example.org/foo). All characters before the first "/" must be a valid +
subdomain as defined by RFC 1123. All characters trailing the first "/" must +
be valid HTTP Path characters as defined by RFC 3986. +
key must be lowercase. +
Required to be unique. +
| *`valueExpression`* __string__ | valueExpression is a CEL expression to extract extra attribute value. +
valueExpression must produce a string or string array value. +
"", [], and null values are treated as the extra mapping not being present. +
Empty string values contained within a string array are filtered out. +
CEL expressions have access to the contents of the token claims, organized into CEL variable: +
- 'claims' is a map of claim names to claim values. +
For example, a variable named 'sub' can be accessed as 'claims.sub'. +
Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'. +
Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ +
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-31-apis-concierge-authentication-v1alpha1-jwtauthenticator"]
==== JWTAuthenticator
@@ -78,8 +151,8 @@ signature, existence of claims, etc.) and extract the username and groups from t
| Field | Description
| *`metadata`* __link:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#objectmeta-v1-meta[$$ObjectMeta$$]__ | Refer to Kubernetes API documentation for fields of `metadata`.
| *`spec`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-31-apis-concierge-authentication-v1alpha1-jwtauthenticatorspec[$$JWTAuthenticatorSpec$$]__ | Spec for configuring the authenticator. +
| *`status`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-31-apis-concierge-authentication-v1alpha1-jwtauthenticatorstatus[$$JWTAuthenticatorStatus$$]__ | Status of the authenticator. +
| *`spec`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-31-apis-concierge-authentication-v1alpha1-jwtauthenticatorspec[$$JWTAuthenticatorSpec$$]__ | spec for configuring the authenticator. +
| *`status`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-31-apis-concierge-authentication-v1alpha1-jwtauthenticatorstatus[$$JWTAuthenticatorStatus$$]__ | status of the authenticator. +
|===
@@ -100,7 +173,7 @@ signature, existence of claims, etc.) and extract the username and groups from t
[id="{anchor_prefix}-go-pinniped-dev-generated-1-31-apis-concierge-authentication-v1alpha1-jwtauthenticatorspec"]
==== JWTAuthenticatorSpec
Spec for configuring a JWT authenticator.
JWTAuthenticatorSpec is the spec for configuring a JWT authenticator.
.Appears In:
****
@@ -110,19 +183,32 @@ Spec for configuring a JWT authenticator.
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`issuer`* __string__ | Issuer is the OIDC issuer URL that will be used to discover public signing keys. Issuer is +
| *`issuer`* __string__ | issuer is the OIDC issuer URL that will be used to discover public signing keys. Issuer is +
also used to validate the "iss" JWT claim. +
| *`audience`* __string__ | Audience is the required value of the "aud" JWT claim. +
| *`claims`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-31-apis-concierge-authentication-v1alpha1-jwttokenclaims[$$JWTTokenClaims$$]__ | Claims allows customization of the claims that will be mapped to user identity +
| *`audience`* __string__ | audience is the required value of the "aud" JWT claim. +
| *`claims`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-31-apis-concierge-authentication-v1alpha1-jwttokenclaims[$$JWTTokenClaims$$]__ | claims allows customization of the claims that will be mapped to user identity +
for Kubernetes access. +
| *`tls`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-31-apis-concierge-authentication-v1alpha1-tlsspec[$$TLSSpec$$]__ | TLS configuration for communicating with the OIDC provider. +
| *`claimValidationRules`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-31-apis-concierge-authentication-v1alpha1-claimvalidationrule[$$ClaimValidationRule$$] array__ | claimValidationRules are rules that are applied to validate token claims to authenticate users. +
This is similar to claimValidationRules from Kubernetes AuthenticationConfiguration as documented in +
https://kubernetes.io/docs/reference/access-authn-authz/authentication. +
This is an advanced configuration option. During an end-user login flow, mistakes in this +
configuration will cause the user's login to fail. +
| *`userValidationRules`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-31-apis-concierge-authentication-v1alpha1-uservalidationrule[$$UserValidationRule$$] array__ | userValidationRules are rules that are applied to final user before completing authentication. +
These allow invariants to be applied to incoming identities such as preventing the +
use of the system: prefix that is commonly used by Kubernetes components. +
The validation rules are logically ANDed together and must all return true for the validation to pass. +
This is similar to claimValidationRules from Kubernetes AuthenticationConfiguration as documented in +
https://kubernetes.io/docs/reference/access-authn-authz/authentication. +
This is an advanced configuration option. During an end-user login flow, mistakes in this +
configuration will cause the user's login to fail. +
| *`tls`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-31-apis-concierge-authentication-v1alpha1-tlsspec[$$TLSSpec$$]__ | tls is the configuration for communicating with the OIDC provider via TLS. +
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-31-apis-concierge-authentication-v1alpha1-jwtauthenticatorstatus"]
==== JWTAuthenticatorStatus
Status of a JWT authenticator.
JWTAuthenticatorStatus is the status of a JWT authenticator.
.Appears In:
****
@@ -151,10 +237,22 @@ for Kubernetes access.
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`groups`* __string__ | Groups is the name of the claim which should be read to extract the user's +
| *`groups`* __string__ | groups is the name of the claim which should be read to extract the user's +
group membership from the JWT token. When not specified, it will default to "groups". +
| *`username`* __string__ | Username is the name of the claim which should be read to extract the +
| *`username`* __string__ | username is the name of the claim which should be read to extract the +
username from the JWT token. When not specified, it will default to "username". +
| *`extra`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-31-apis-concierge-authentication-v1alpha1-extramapping[$$ExtraMapping$$] array__ | extra is similar to claimMappings.extra from Kubernetes AuthenticationConfiguration as documented in +
https://kubernetes.io/docs/reference/access-authn-authz/authentication. However, note that the +
Pinniped Concierge issues client certificates to users for the purpose of authenticating, and +
the Kubernetes API server does not have any mechanism for transmitting auth extras via client +
certificates. When configured, these extras will appear in client certificates issued by the +
Pinniped Supervisor in the x509 Subject field as Organizational Units (OU). However, when this +
client certificate is presented to Kubernetes for authentication, Kubernetes will ignore these +
extras. This is probably only useful if you are using a custom authenticating proxy in front +
of your Kubernetes API server which can translate these OUs into auth extras, as described by +
https://kubernetes.io/docs/reference/access-authn-authz/authentication/#authenticating-proxy. +
This is an advanced configuration option. During an end-user login flow, each of these CEL expressions +
must evaluate to either a string or an array of strings, or else the user's login will fail. +
|===
@@ -178,6 +276,33 @@ Any changes to the CA bundle in the secret or configmap will be dynamically relo
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-31-apis-concierge-authentication-v1alpha1-uservalidationrule"]
==== UserValidationRule
UserValidationRule provides the configuration for a single user info validation rule.
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-31-apis-concierge-authentication-v1alpha1-jwtauthenticatorspec[$$JWTAuthenticatorSpec$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`expression`* __string__ | expression represents the expression which will be evaluated by CEL. +
Must return true for the validation to pass. +
CEL expressions have access to the contents of UserInfo, organized into CEL variable: +
- 'user' - authentication.k8s.io/v1, Kind=UserInfo object +
Refer to https://github.com/kubernetes/api/blob/release-1.28/authentication/v1/types.go#L105-L122 for the definition. +
API documentation: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#userinfo-v1-authentication-k8s-io +
Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ +
| *`message`* __string__ | message customizes the returned error message when rule returns false. +
message is a literal string. +
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-31-apis-concierge-authentication-v1alpha1-webhookauthenticator"]
==== WebhookAuthenticator

View File

@@ -18,7 +18,7 @@ const (
JWTAuthenticatorPhaseError JWTAuthenticatorPhase = "Error"
)
// Status of a JWT authenticator.
// JWTAuthenticatorStatus is the status of a JWT authenticator.
type JWTAuthenticatorStatus struct {
// Represents the observations of the authenticator's current state.
// +patchMergeKey=type
@@ -26,46 +26,168 @@ type JWTAuthenticatorStatus struct {
// +listType=map
// +listMapKey=type
Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"`
// Phase summarizes the overall status of the JWTAuthenticator.
// +kubebuilder:default=Pending
// +kubebuilder:validation:Enum=Pending;Ready;Error
Phase JWTAuthenticatorPhase `json:"phase,omitempty"`
}
// Spec for configuring a JWT authenticator.
// JWTAuthenticatorSpec is the spec for configuring a JWT authenticator.
type JWTAuthenticatorSpec struct {
// Issuer is the OIDC issuer URL that will be used to discover public signing keys. Issuer is
// issuer is the OIDC issuer URL that will be used to discover public signing keys. Issuer is
// also used to validate the "iss" JWT claim.
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:Pattern=`^https://`
Issuer string `json:"issuer"`
// Audience is the required value of the "aud" JWT claim.
// audience is the required value of the "aud" JWT claim.
// +kubebuilder:validation:MinLength=1
Audience string `json:"audience"`
// Claims allows customization of the claims that will be mapped to user identity
// claims allows customization of the claims that will be mapped to user identity
// for Kubernetes access.
// +optional
Claims JWTTokenClaims `json:"claims"`
// TLS configuration for communicating with the OIDC provider.
// claimValidationRules are rules that are applied to validate token claims to authenticate users.
// This is similar to claimValidationRules from Kubernetes AuthenticationConfiguration as documented in
// https://kubernetes.io/docs/reference/access-authn-authz/authentication.
// This is an advanced configuration option. During an end-user login flow, mistakes in this
// configuration will cause the user's login to fail.
// +optional
ClaimValidationRules []ClaimValidationRule `json:"claimValidationRules,omitempty"`
// userValidationRules are rules that are applied to final user before completing authentication.
// These allow invariants to be applied to incoming identities such as preventing the
// use of the system: prefix that is commonly used by Kubernetes components.
// The validation rules are logically ANDed together and must all return true for the validation to pass.
// This is similar to claimValidationRules from Kubernetes AuthenticationConfiguration as documented in
// https://kubernetes.io/docs/reference/access-authn-authz/authentication.
// This is an advanced configuration option. During an end-user login flow, mistakes in this
// configuration will cause the user's login to fail.
// +optional
UserValidationRules []UserValidationRule `json:"userValidationRules,omitempty"`
// tls is the configuration for communicating with the OIDC provider via TLS.
// +optional
TLS *TLSSpec `json:"tls,omitempty"`
}
// ClaimValidationRule provides the configuration for a single claim validation rule.
type ClaimValidationRule struct {
// claim is the name of a required claim.
// Same as --oidc-required-claim flag.
// Only string claim keys are supported.
// Mutually exclusive with expression and message.
// +optional
Claim string `json:"claim,omitempty"`
// requiredValue is the value of a required claim.
// Same as --oidc-required-claim flag.
// Only string claim values are supported.
// If claim is set and requiredValue is not set, the claim must be present with a value set to the empty string.
// Mutually exclusive with expression and message.
// +optional
RequiredValue string `json:"requiredValue,omitempty"`
// expression represents the expression which will be evaluated by CEL.
// Must produce a boolean.
//
// CEL expressions have access to the contents of the token claims, organized into CEL variable:
// - 'claims' is a map of claim names to claim values.
// For example, a variable named 'sub' can be accessed as 'claims.sub'.
// Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'.
// Must return true for the validation to pass.
//
// Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
//
// Mutually exclusive with claim and requiredValue.
// +optional
Expression string `json:"expression,omitempty"`
// message customizes the returned error message when expression returns false.
// message is a literal string.
// Mutually exclusive with claim and requiredValue.
// +optional
Message string `json:"message,omitempty"`
}
// UserValidationRule provides the configuration for a single user info validation rule.
type UserValidationRule struct {
// expression represents the expression which will be evaluated by CEL.
// Must return true for the validation to pass.
//
// CEL expressions have access to the contents of UserInfo, organized into CEL variable:
// - 'user' - authentication.k8s.io/v1, Kind=UserInfo object
// Refer to https://github.com/kubernetes/api/blob/release-1.28/authentication/v1/types.go#L105-L122 for the definition.
// API documentation: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#userinfo-v1-authentication-k8s-io
//
// Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
//
// +required
Expression string `json:"expression"`
// message customizes the returned error message when rule returns false.
// message is a literal string.
// +optional
Message string `json:"message,omitempty"`
}
// JWTTokenClaims allows customization of the claims that will be mapped to user identity
// for Kubernetes access.
type JWTTokenClaims struct {
// Groups is the name of the claim which should be read to extract the user's
// groups is the name of the claim which should be read to extract the user's
// group membership from the JWT token. When not specified, it will default to "groups".
// +optional
Groups string `json:"groups"`
// Username is the name of the claim which should be read to extract the
// username is the name of the claim which should be read to extract the
// username from the JWT token. When not specified, it will default to "username".
// +optional
Username string `json:"username"`
// extra is similar to claimMappings.extra from Kubernetes AuthenticationConfiguration as documented in
// https://kubernetes.io/docs/reference/access-authn-authz/authentication. However, note that the
// Pinniped Concierge issues client certificates to users for the purpose of authenticating, and
// the Kubernetes API server does not have any mechanism for transmitting auth extras via client
// certificates. When configured, these extras will appear in client certificates issued by the
// Pinniped Supervisor in the x509 Subject field as Organizational Units (OU). However, when this
// client certificate is presented to Kubernetes for authentication, Kubernetes will ignore these
// extras. This is probably only useful if you are using a custom authenticating proxy in front
// of your Kubernetes API server which can translate these OUs into auth extras, as described by
// https://kubernetes.io/docs/reference/access-authn-authz/authentication/#authenticating-proxy.
// This is an advanced configuration option. During an end-user login flow, each of these CEL expressions
// must evaluate to either a string or an array of strings, or else the user's login will fail.
// +optional
Extra []ExtraMapping `json:"extra,omitempty"`
}
// ExtraMapping provides the configuration for a single extra mapping.
type ExtraMapping struct {
// key is a string to use as the extra attribute key.
// key must be a domain-prefix path (e.g. example.org/foo). All characters before the first "/" must be a valid
// subdomain as defined by RFC 1123. All characters trailing the first "/" must
// be valid HTTP Path characters as defined by RFC 3986.
// key must be lowercase.
// Required to be unique.
// +required
Key string `json:"key"`
// valueExpression is a CEL expression to extract extra attribute value.
// valueExpression must produce a string or string array value.
// "", [], and null values are treated as the extra mapping not being present.
// Empty string values contained within a string array are filtered out.
//
// CEL expressions have access to the contents of the token claims, organized into CEL variable:
// - 'claims' is a map of claim names to claim values.
// For example, a variable named 'sub' can be accessed as 'claims.sub'.
// Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'.
//
// Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
//
// +required
ValueExpression string `json:"valueExpression"`
}
// JWTAuthenticator describes the configuration of a JWT authenticator.
@@ -86,14 +208,14 @@ type JWTAuthenticator struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
// Spec for configuring the authenticator.
// spec for configuring the authenticator.
Spec JWTAuthenticatorSpec `json:"spec"`
// Status of the authenticator.
// status of the authenticator.
Status JWTAuthenticatorStatus `json:"status,omitempty"`
}
// List of JWTAuthenticator objects.
// JWTAuthenticatorList is a list of JWTAuthenticator objects.
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type JWTAuthenticatorList struct {
metav1.TypeMeta `json:",inline"`

View File

@@ -29,6 +29,38 @@ func (in *CertificateAuthorityDataSourceSpec) DeepCopy() *CertificateAuthorityDa
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ClaimValidationRule) DeepCopyInto(out *ClaimValidationRule) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClaimValidationRule.
func (in *ClaimValidationRule) DeepCopy() *ClaimValidationRule {
if in == nil {
return nil
}
out := new(ClaimValidationRule)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ExtraMapping) DeepCopyInto(out *ExtraMapping) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExtraMapping.
func (in *ExtraMapping) DeepCopy() *ExtraMapping {
if in == nil {
return nil
}
out := new(ExtraMapping)
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
@@ -93,7 +125,17 @@ func (in *JWTAuthenticatorList) DeepCopyObject() runtime.Object {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *JWTAuthenticatorSpec) DeepCopyInto(out *JWTAuthenticatorSpec) {
*out = *in
out.Claims = in.Claims
in.Claims.DeepCopyInto(&out.Claims)
if in.ClaimValidationRules != nil {
in, out := &in.ClaimValidationRules, &out.ClaimValidationRules
*out = make([]ClaimValidationRule, len(*in))
copy(*out, *in)
}
if in.UserValidationRules != nil {
in, out := &in.UserValidationRules, &out.UserValidationRules
*out = make([]UserValidationRule, len(*in))
copy(*out, *in)
}
if in.TLS != nil {
in, out := &in.TLS, &out.TLS
*out = new(TLSSpec)
@@ -138,6 +180,11 @@ func (in *JWTAuthenticatorStatus) DeepCopy() *JWTAuthenticatorStatus {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *JWTTokenClaims) DeepCopyInto(out *JWTTokenClaims) {
*out = *in
if in.Extra != nil {
in, out := &in.Extra, &out.Extra
*out = make([]ExtraMapping, len(*in))
copy(*out, *in)
}
return
}
@@ -172,6 +219,22 @@ func (in *TLSSpec) DeepCopy() *TLSSpec {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *UserValidationRule) DeepCopyInto(out *UserValidationRule) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UserValidationRule.
func (in *UserValidationRule) DeepCopy() *UserValidationRule {
if in == nil {
return nil
}
out := new(UserValidationRule)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *WebhookAuthenticator) DeepCopyInto(out *WebhookAuthenticator) {
*out = *in

View File

@@ -58,37 +58,133 @@ spec:
metadata:
type: object
spec:
description: Spec for configuring the authenticator.
description: spec for configuring the authenticator.
properties:
audience:
description: Audience is the required value of the "aud" JWT claim.
description: audience is the required value of the "aud" JWT claim.
minLength: 1
type: string
claimValidationRules:
description: |-
claimValidationRules are rules that are applied to validate token claims to authenticate users.
This is similar to claimValidationRules from Kubernetes AuthenticationConfiguration as documented in
https://kubernetes.io/docs/reference/access-authn-authz/authentication.
This is an advanced configuration option. During an end-user login flow, mistakes in this
configuration will cause the user's login to fail.
items:
description: ClaimValidationRule provides the configuration for
a single claim validation rule.
properties:
claim:
description: |-
claim is the name of a required claim.
Same as --oidc-required-claim flag.
Only string claim keys are supported.
Mutually exclusive with expression and message.
type: string
expression:
description: |-
expression represents the expression which will be evaluated by CEL.
Must produce a boolean.
CEL expressions have access to the contents of the token claims, organized into CEL variable:
- 'claims' is a map of claim names to claim values.
For example, a variable named 'sub' can be accessed as 'claims.sub'.
Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'.
Must return true for the validation to pass.
Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
Mutually exclusive with claim and requiredValue.
type: string
message:
description: |-
message customizes the returned error message when expression returns false.
message is a literal string.
Mutually exclusive with claim and requiredValue.
type: string
requiredValue:
description: |-
requiredValue is the value of a required claim.
Same as --oidc-required-claim flag.
Only string claim values are supported.
If claim is set and requiredValue is not set, the claim must be present with a value set to the empty string.
Mutually exclusive with expression and message.
type: string
type: object
type: array
claims:
description: |-
Claims allows customization of the claims that will be mapped to user identity
claims allows customization of the claims that will be mapped to user identity
for Kubernetes access.
properties:
extra:
description: |-
extra is similar to claimMappings.extra from Kubernetes AuthenticationConfiguration as documented in
https://kubernetes.io/docs/reference/access-authn-authz/authentication. However, note that the
Pinniped Concierge issues client certificates to users for the purpose of authenticating, and
the Kubernetes API server does not have any mechanism for transmitting auth extras via client
certificates. When configured, these extras will appear in client certificates issued by the
Pinniped Supervisor in the x509 Subject field as Organizational Units (OU). However, when this
client certificate is presented to Kubernetes for authentication, Kubernetes will ignore these
extras. This is probably only useful if you are using a custom authenticating proxy in front
of your Kubernetes API server which can translate these OUs into auth extras, as described by
https://kubernetes.io/docs/reference/access-authn-authz/authentication/#authenticating-proxy.
This is an advanced configuration option. During an end-user login flow, each of these CEL expressions
must evaluate to either a string or an array of strings, or else the user's login will fail.
items:
description: ExtraMapping provides the configuration for a single
extra mapping.
properties:
key:
description: |-
key is a string to use as the extra attribute key.
key must be a domain-prefix path (e.g. example.org/foo). All characters before the first "/" must be a valid
subdomain as defined by RFC 1123. All characters trailing the first "/" must
be valid HTTP Path characters as defined by RFC 3986.
key must be lowercase.
Required to be unique.
type: string
valueExpression:
description: |-
valueExpression is a CEL expression to extract extra attribute value.
valueExpression must produce a string or string array value.
"", [], and null values are treated as the extra mapping not being present.
Empty string values contained within a string array are filtered out.
CEL expressions have access to the contents of the token claims, organized into CEL variable:
- 'claims' is a map of claim names to claim values.
For example, a variable named 'sub' can be accessed as 'claims.sub'.
Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'.
Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
type: string
required:
- key
- valueExpression
type: object
type: array
groups:
description: |-
Groups is the name of the claim which should be read to extract the user's
groups is the name of the claim which should be read to extract the user's
group membership from the JWT token. When not specified, it will default to "groups".
type: string
username:
description: |-
Username is the name of the claim which should be read to extract the
username is the name of the claim which should be read to extract the
username from the JWT token. When not specified, it will default to "username".
type: string
type: object
issuer:
description: |-
Issuer is the OIDC issuer URL that will be used to discover public signing keys. Issuer is
issuer is the OIDC issuer URL that will be used to discover public signing keys. Issuer is
also used to validate the "iss" JWT claim.
minLength: 1
pattern: ^https://
type: string
tls:
description: TLS configuration for communicating with the OIDC provider.
description: tls is the configuration for communicating with the OIDC
provider via TLS.
properties:
certificateAuthorityData:
description: X.509 Certificate Authority (base64-encoded PEM bundle).
@@ -128,12 +224,47 @@ spec:
- name
type: object
type: object
userValidationRules:
description: |-
userValidationRules are rules that are applied to final user before completing authentication.
These allow invariants to be applied to incoming identities such as preventing the
use of the system: prefix that is commonly used by Kubernetes components.
The validation rules are logically ANDed together and must all return true for the validation to pass.
This is similar to claimValidationRules from Kubernetes AuthenticationConfiguration as documented in
https://kubernetes.io/docs/reference/access-authn-authz/authentication.
This is an advanced configuration option. During an end-user login flow, mistakes in this
configuration will cause the user's login to fail.
items:
description: UserValidationRule provides the configuration for a
single user info validation rule.
properties:
expression:
description: |-
expression represents the expression which will be evaluated by CEL.
Must return true for the validation to pass.
CEL expressions have access to the contents of UserInfo, organized into CEL variable:
- 'user' - authentication.k8s.io/v1, Kind=UserInfo object
Refer to https://github.com/kubernetes/api/blob/release-1.28/authentication/v1/types.go#L105-L122 for the definition.
API documentation: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#userinfo-v1-authentication-k8s-io
Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
type: string
message:
description: |-
message customizes the returned error message when rule returns false.
message is a literal string.
type: string
required:
- expression
type: object
type: array
required:
- audience
- issuer
type: object
status:
description: Status of the authenticator.
description: status of the authenticator.
properties:
conditions:
description: Represents the observations of the authenticator's current

View File

@@ -60,6 +60,79 @@ certificate bundle. +
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-32-apis-concierge-authentication-v1alpha1-claimvalidationrule"]
==== ClaimValidationRule
ClaimValidationRule provides the configuration for a single claim validation rule.
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-32-apis-concierge-authentication-v1alpha1-jwtauthenticatorspec[$$JWTAuthenticatorSpec$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`claim`* __string__ | claim is the name of a required claim. +
Same as --oidc-required-claim flag. +
Only string claim keys are supported. +
Mutually exclusive with expression and message. +
| *`requiredValue`* __string__ | requiredValue is the value of a required claim. +
Same as --oidc-required-claim flag. +
Only string claim values are supported. +
If claim is set and requiredValue is not set, the claim must be present with a value set to the empty string. +
Mutually exclusive with expression and message. +
| *`expression`* __string__ | expression represents the expression which will be evaluated by CEL. +
Must produce a boolean. +
CEL expressions have access to the contents of the token claims, organized into CEL variable: +
- 'claims' is a map of claim names to claim values. +
For example, a variable named 'sub' can be accessed as 'claims.sub'. +
Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'. +
Must return true for the validation to pass. +
Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ +
Mutually exclusive with claim and requiredValue. +
| *`message`* __string__ | message customizes the returned error message when expression returns false. +
message is a literal string. +
Mutually exclusive with claim and requiredValue. +
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-32-apis-concierge-authentication-v1alpha1-extramapping"]
==== ExtraMapping
ExtraMapping provides the configuration for a single extra mapping.
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-32-apis-concierge-authentication-v1alpha1-jwttokenclaims[$$JWTTokenClaims$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`key`* __string__ | key is a string to use as the extra attribute key. +
key must be a domain-prefix path (e.g. example.org/foo). All characters before the first "/" must be a valid +
subdomain as defined by RFC 1123. All characters trailing the first "/" must +
be valid HTTP Path characters as defined by RFC 3986. +
key must be lowercase. +
Required to be unique. +
| *`valueExpression`* __string__ | valueExpression is a CEL expression to extract extra attribute value. +
valueExpression must produce a string or string array value. +
"", [], and null values are treated as the extra mapping not being present. +
Empty string values contained within a string array are filtered out. +
CEL expressions have access to the contents of the token claims, organized into CEL variable: +
- 'claims' is a map of claim names to claim values. +
For example, a variable named 'sub' can be accessed as 'claims.sub'. +
Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'. +
Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ +
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-32-apis-concierge-authentication-v1alpha1-jwtauthenticator"]
==== JWTAuthenticator
@@ -78,8 +151,8 @@ signature, existence of claims, etc.) and extract the username and groups from t
| Field | Description
| *`metadata`* __link:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.32/#objectmeta-v1-meta[$$ObjectMeta$$]__ | Refer to Kubernetes API documentation for fields of `metadata`.
| *`spec`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-32-apis-concierge-authentication-v1alpha1-jwtauthenticatorspec[$$JWTAuthenticatorSpec$$]__ | Spec for configuring the authenticator. +
| *`status`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-32-apis-concierge-authentication-v1alpha1-jwtauthenticatorstatus[$$JWTAuthenticatorStatus$$]__ | Status of the authenticator. +
| *`spec`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-32-apis-concierge-authentication-v1alpha1-jwtauthenticatorspec[$$JWTAuthenticatorSpec$$]__ | spec for configuring the authenticator. +
| *`status`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-32-apis-concierge-authentication-v1alpha1-jwtauthenticatorstatus[$$JWTAuthenticatorStatus$$]__ | status of the authenticator. +
|===
@@ -100,7 +173,7 @@ signature, existence of claims, etc.) and extract the username and groups from t
[id="{anchor_prefix}-go-pinniped-dev-generated-1-32-apis-concierge-authentication-v1alpha1-jwtauthenticatorspec"]
==== JWTAuthenticatorSpec
Spec for configuring a JWT authenticator.
JWTAuthenticatorSpec is the spec for configuring a JWT authenticator.
.Appears In:
****
@@ -110,19 +183,32 @@ Spec for configuring a JWT authenticator.
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`issuer`* __string__ | Issuer is the OIDC issuer URL that will be used to discover public signing keys. Issuer is +
| *`issuer`* __string__ | issuer is the OIDC issuer URL that will be used to discover public signing keys. Issuer is +
also used to validate the "iss" JWT claim. +
| *`audience`* __string__ | Audience is the required value of the "aud" JWT claim. +
| *`claims`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-32-apis-concierge-authentication-v1alpha1-jwttokenclaims[$$JWTTokenClaims$$]__ | Claims allows customization of the claims that will be mapped to user identity +
| *`audience`* __string__ | audience is the required value of the "aud" JWT claim. +
| *`claims`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-32-apis-concierge-authentication-v1alpha1-jwttokenclaims[$$JWTTokenClaims$$]__ | claims allows customization of the claims that will be mapped to user identity +
for Kubernetes access. +
| *`tls`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-32-apis-concierge-authentication-v1alpha1-tlsspec[$$TLSSpec$$]__ | TLS configuration for communicating with the OIDC provider. +
| *`claimValidationRules`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-32-apis-concierge-authentication-v1alpha1-claimvalidationrule[$$ClaimValidationRule$$] array__ | claimValidationRules are rules that are applied to validate token claims to authenticate users. +
This is similar to claimValidationRules from Kubernetes AuthenticationConfiguration as documented in +
https://kubernetes.io/docs/reference/access-authn-authz/authentication. +
This is an advanced configuration option. During an end-user login flow, mistakes in this +
configuration will cause the user's login to fail. +
| *`userValidationRules`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-32-apis-concierge-authentication-v1alpha1-uservalidationrule[$$UserValidationRule$$] array__ | userValidationRules are rules that are applied to final user before completing authentication. +
These allow invariants to be applied to incoming identities such as preventing the +
use of the system: prefix that is commonly used by Kubernetes components. +
The validation rules are logically ANDed together and must all return true for the validation to pass. +
This is similar to claimValidationRules from Kubernetes AuthenticationConfiguration as documented in +
https://kubernetes.io/docs/reference/access-authn-authz/authentication. +
This is an advanced configuration option. During an end-user login flow, mistakes in this +
configuration will cause the user's login to fail. +
| *`tls`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-32-apis-concierge-authentication-v1alpha1-tlsspec[$$TLSSpec$$]__ | tls is the configuration for communicating with the OIDC provider via TLS. +
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-32-apis-concierge-authentication-v1alpha1-jwtauthenticatorstatus"]
==== JWTAuthenticatorStatus
Status of a JWT authenticator.
JWTAuthenticatorStatus is the status of a JWT authenticator.
.Appears In:
****
@@ -151,10 +237,22 @@ for Kubernetes access.
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`groups`* __string__ | Groups is the name of the claim which should be read to extract the user's +
| *`groups`* __string__ | groups is the name of the claim which should be read to extract the user's +
group membership from the JWT token. When not specified, it will default to "groups". +
| *`username`* __string__ | Username is the name of the claim which should be read to extract the +
| *`username`* __string__ | username is the name of the claim which should be read to extract the +
username from the JWT token. When not specified, it will default to "username". +
| *`extra`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-32-apis-concierge-authentication-v1alpha1-extramapping[$$ExtraMapping$$] array__ | extra is similar to claimMappings.extra from Kubernetes AuthenticationConfiguration as documented in +
https://kubernetes.io/docs/reference/access-authn-authz/authentication. However, note that the +
Pinniped Concierge issues client certificates to users for the purpose of authenticating, and +
the Kubernetes API server does not have any mechanism for transmitting auth extras via client +
certificates. When configured, these extras will appear in client certificates issued by the +
Pinniped Supervisor in the x509 Subject field as Organizational Units (OU). However, when this +
client certificate is presented to Kubernetes for authentication, Kubernetes will ignore these +
extras. This is probably only useful if you are using a custom authenticating proxy in front +
of your Kubernetes API server which can translate these OUs into auth extras, as described by +
https://kubernetes.io/docs/reference/access-authn-authz/authentication/#authenticating-proxy. +
This is an advanced configuration option. During an end-user login flow, each of these CEL expressions +
must evaluate to either a string or an array of strings, or else the user's login will fail. +
|===
@@ -178,6 +276,33 @@ Any changes to the CA bundle in the secret or configmap will be dynamically relo
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-32-apis-concierge-authentication-v1alpha1-uservalidationrule"]
==== UserValidationRule
UserValidationRule provides the configuration for a single user info validation rule.
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-32-apis-concierge-authentication-v1alpha1-jwtauthenticatorspec[$$JWTAuthenticatorSpec$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`expression`* __string__ | expression represents the expression which will be evaluated by CEL. +
Must return true for the validation to pass. +
CEL expressions have access to the contents of UserInfo, organized into CEL variable: +
- 'user' - authentication.k8s.io/v1, Kind=UserInfo object +
Refer to https://github.com/kubernetes/api/blob/release-1.28/authentication/v1/types.go#L105-L122 for the definition. +
API documentation: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#userinfo-v1-authentication-k8s-io +
Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ +
| *`message`* __string__ | message customizes the returned error message when rule returns false. +
message is a literal string. +
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-32-apis-concierge-authentication-v1alpha1-webhookauthenticator"]
==== WebhookAuthenticator

View File

@@ -18,7 +18,7 @@ const (
JWTAuthenticatorPhaseError JWTAuthenticatorPhase = "Error"
)
// Status of a JWT authenticator.
// JWTAuthenticatorStatus is the status of a JWT authenticator.
type JWTAuthenticatorStatus struct {
// Represents the observations of the authenticator's current state.
// +patchMergeKey=type
@@ -26,46 +26,168 @@ type JWTAuthenticatorStatus struct {
// +listType=map
// +listMapKey=type
Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"`
// Phase summarizes the overall status of the JWTAuthenticator.
// +kubebuilder:default=Pending
// +kubebuilder:validation:Enum=Pending;Ready;Error
Phase JWTAuthenticatorPhase `json:"phase,omitempty"`
}
// Spec for configuring a JWT authenticator.
// JWTAuthenticatorSpec is the spec for configuring a JWT authenticator.
type JWTAuthenticatorSpec struct {
// Issuer is the OIDC issuer URL that will be used to discover public signing keys. Issuer is
// issuer is the OIDC issuer URL that will be used to discover public signing keys. Issuer is
// also used to validate the "iss" JWT claim.
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:Pattern=`^https://`
Issuer string `json:"issuer"`
// Audience is the required value of the "aud" JWT claim.
// audience is the required value of the "aud" JWT claim.
// +kubebuilder:validation:MinLength=1
Audience string `json:"audience"`
// Claims allows customization of the claims that will be mapped to user identity
// claims allows customization of the claims that will be mapped to user identity
// for Kubernetes access.
// +optional
Claims JWTTokenClaims `json:"claims"`
// TLS configuration for communicating with the OIDC provider.
// claimValidationRules are rules that are applied to validate token claims to authenticate users.
// This is similar to claimValidationRules from Kubernetes AuthenticationConfiguration as documented in
// https://kubernetes.io/docs/reference/access-authn-authz/authentication.
// This is an advanced configuration option. During an end-user login flow, mistakes in this
// configuration will cause the user's login to fail.
// +optional
ClaimValidationRules []ClaimValidationRule `json:"claimValidationRules,omitempty"`
// userValidationRules are rules that are applied to final user before completing authentication.
// These allow invariants to be applied to incoming identities such as preventing the
// use of the system: prefix that is commonly used by Kubernetes components.
// The validation rules are logically ANDed together and must all return true for the validation to pass.
// This is similar to claimValidationRules from Kubernetes AuthenticationConfiguration as documented in
// https://kubernetes.io/docs/reference/access-authn-authz/authentication.
// This is an advanced configuration option. During an end-user login flow, mistakes in this
// configuration will cause the user's login to fail.
// +optional
UserValidationRules []UserValidationRule `json:"userValidationRules,omitempty"`
// tls is the configuration for communicating with the OIDC provider via TLS.
// +optional
TLS *TLSSpec `json:"tls,omitempty"`
}
// ClaimValidationRule provides the configuration for a single claim validation rule.
type ClaimValidationRule struct {
// claim is the name of a required claim.
// Same as --oidc-required-claim flag.
// Only string claim keys are supported.
// Mutually exclusive with expression and message.
// +optional
Claim string `json:"claim,omitempty"`
// requiredValue is the value of a required claim.
// Same as --oidc-required-claim flag.
// Only string claim values are supported.
// If claim is set and requiredValue is not set, the claim must be present with a value set to the empty string.
// Mutually exclusive with expression and message.
// +optional
RequiredValue string `json:"requiredValue,omitempty"`
// expression represents the expression which will be evaluated by CEL.
// Must produce a boolean.
//
// CEL expressions have access to the contents of the token claims, organized into CEL variable:
// - 'claims' is a map of claim names to claim values.
// For example, a variable named 'sub' can be accessed as 'claims.sub'.
// Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'.
// Must return true for the validation to pass.
//
// Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
//
// Mutually exclusive with claim and requiredValue.
// +optional
Expression string `json:"expression,omitempty"`
// message customizes the returned error message when expression returns false.
// message is a literal string.
// Mutually exclusive with claim and requiredValue.
// +optional
Message string `json:"message,omitempty"`
}
// UserValidationRule provides the configuration for a single user info validation rule.
type UserValidationRule struct {
// expression represents the expression which will be evaluated by CEL.
// Must return true for the validation to pass.
//
// CEL expressions have access to the contents of UserInfo, organized into CEL variable:
// - 'user' - authentication.k8s.io/v1, Kind=UserInfo object
// Refer to https://github.com/kubernetes/api/blob/release-1.28/authentication/v1/types.go#L105-L122 for the definition.
// API documentation: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#userinfo-v1-authentication-k8s-io
//
// Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
//
// +required
Expression string `json:"expression"`
// message customizes the returned error message when rule returns false.
// message is a literal string.
// +optional
Message string `json:"message,omitempty"`
}
// JWTTokenClaims allows customization of the claims that will be mapped to user identity
// for Kubernetes access.
type JWTTokenClaims struct {
// Groups is the name of the claim which should be read to extract the user's
// groups is the name of the claim which should be read to extract the user's
// group membership from the JWT token. When not specified, it will default to "groups".
// +optional
Groups string `json:"groups"`
// Username is the name of the claim which should be read to extract the
// username is the name of the claim which should be read to extract the
// username from the JWT token. When not specified, it will default to "username".
// +optional
Username string `json:"username"`
// extra is similar to claimMappings.extra from Kubernetes AuthenticationConfiguration as documented in
// https://kubernetes.io/docs/reference/access-authn-authz/authentication. However, note that the
// Pinniped Concierge issues client certificates to users for the purpose of authenticating, and
// the Kubernetes API server does not have any mechanism for transmitting auth extras via client
// certificates. When configured, these extras will appear in client certificates issued by the
// Pinniped Supervisor in the x509 Subject field as Organizational Units (OU). However, when this
// client certificate is presented to Kubernetes for authentication, Kubernetes will ignore these
// extras. This is probably only useful if you are using a custom authenticating proxy in front
// of your Kubernetes API server which can translate these OUs into auth extras, as described by
// https://kubernetes.io/docs/reference/access-authn-authz/authentication/#authenticating-proxy.
// This is an advanced configuration option. During an end-user login flow, each of these CEL expressions
// must evaluate to either a string or an array of strings, or else the user's login will fail.
// +optional
Extra []ExtraMapping `json:"extra,omitempty"`
}
// ExtraMapping provides the configuration for a single extra mapping.
type ExtraMapping struct {
// key is a string to use as the extra attribute key.
// key must be a domain-prefix path (e.g. example.org/foo). All characters before the first "/" must be a valid
// subdomain as defined by RFC 1123. All characters trailing the first "/" must
// be valid HTTP Path characters as defined by RFC 3986.
// key must be lowercase.
// Required to be unique.
// +required
Key string `json:"key"`
// valueExpression is a CEL expression to extract extra attribute value.
// valueExpression must produce a string or string array value.
// "", [], and null values are treated as the extra mapping not being present.
// Empty string values contained within a string array are filtered out.
//
// CEL expressions have access to the contents of the token claims, organized into CEL variable:
// - 'claims' is a map of claim names to claim values.
// For example, a variable named 'sub' can be accessed as 'claims.sub'.
// Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'.
//
// Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
//
// +required
ValueExpression string `json:"valueExpression"`
}
// JWTAuthenticator describes the configuration of a JWT authenticator.
@@ -86,14 +208,14 @@ type JWTAuthenticator struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
// Spec for configuring the authenticator.
// spec for configuring the authenticator.
Spec JWTAuthenticatorSpec `json:"spec"`
// Status of the authenticator.
// status of the authenticator.
Status JWTAuthenticatorStatus `json:"status,omitempty"`
}
// List of JWTAuthenticator objects.
// JWTAuthenticatorList is a list of JWTAuthenticator objects.
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type JWTAuthenticatorList struct {
metav1.TypeMeta `json:",inline"`

View File

@@ -29,6 +29,38 @@ func (in *CertificateAuthorityDataSourceSpec) DeepCopy() *CertificateAuthorityDa
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ClaimValidationRule) DeepCopyInto(out *ClaimValidationRule) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClaimValidationRule.
func (in *ClaimValidationRule) DeepCopy() *ClaimValidationRule {
if in == nil {
return nil
}
out := new(ClaimValidationRule)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ExtraMapping) DeepCopyInto(out *ExtraMapping) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExtraMapping.
func (in *ExtraMapping) DeepCopy() *ExtraMapping {
if in == nil {
return nil
}
out := new(ExtraMapping)
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
@@ -93,7 +125,17 @@ func (in *JWTAuthenticatorList) DeepCopyObject() runtime.Object {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *JWTAuthenticatorSpec) DeepCopyInto(out *JWTAuthenticatorSpec) {
*out = *in
out.Claims = in.Claims
in.Claims.DeepCopyInto(&out.Claims)
if in.ClaimValidationRules != nil {
in, out := &in.ClaimValidationRules, &out.ClaimValidationRules
*out = make([]ClaimValidationRule, len(*in))
copy(*out, *in)
}
if in.UserValidationRules != nil {
in, out := &in.UserValidationRules, &out.UserValidationRules
*out = make([]UserValidationRule, len(*in))
copy(*out, *in)
}
if in.TLS != nil {
in, out := &in.TLS, &out.TLS
*out = new(TLSSpec)
@@ -138,6 +180,11 @@ func (in *JWTAuthenticatorStatus) DeepCopy() *JWTAuthenticatorStatus {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *JWTTokenClaims) DeepCopyInto(out *JWTTokenClaims) {
*out = *in
if in.Extra != nil {
in, out := &in.Extra, &out.Extra
*out = make([]ExtraMapping, len(*in))
copy(*out, *in)
}
return
}
@@ -172,6 +219,22 @@ func (in *TLSSpec) DeepCopy() *TLSSpec {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *UserValidationRule) DeepCopyInto(out *UserValidationRule) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UserValidationRule.
func (in *UserValidationRule) DeepCopy() *UserValidationRule {
if in == nil {
return nil
}
out := new(UserValidationRule)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *WebhookAuthenticator) DeepCopyInto(out *WebhookAuthenticator) {
*out = *in

View File

@@ -58,37 +58,133 @@ spec:
metadata:
type: object
spec:
description: Spec for configuring the authenticator.
description: spec for configuring the authenticator.
properties:
audience:
description: Audience is the required value of the "aud" JWT claim.
description: audience is the required value of the "aud" JWT claim.
minLength: 1
type: string
claimValidationRules:
description: |-
claimValidationRules are rules that are applied to validate token claims to authenticate users.
This is similar to claimValidationRules from Kubernetes AuthenticationConfiguration as documented in
https://kubernetes.io/docs/reference/access-authn-authz/authentication.
This is an advanced configuration option. During an end-user login flow, mistakes in this
configuration will cause the user's login to fail.
items:
description: ClaimValidationRule provides the configuration for
a single claim validation rule.
properties:
claim:
description: |-
claim is the name of a required claim.
Same as --oidc-required-claim flag.
Only string claim keys are supported.
Mutually exclusive with expression and message.
type: string
expression:
description: |-
expression represents the expression which will be evaluated by CEL.
Must produce a boolean.
CEL expressions have access to the contents of the token claims, organized into CEL variable:
- 'claims' is a map of claim names to claim values.
For example, a variable named 'sub' can be accessed as 'claims.sub'.
Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'.
Must return true for the validation to pass.
Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
Mutually exclusive with claim and requiredValue.
type: string
message:
description: |-
message customizes the returned error message when expression returns false.
message is a literal string.
Mutually exclusive with claim and requiredValue.
type: string
requiredValue:
description: |-
requiredValue is the value of a required claim.
Same as --oidc-required-claim flag.
Only string claim values are supported.
If claim is set and requiredValue is not set, the claim must be present with a value set to the empty string.
Mutually exclusive with expression and message.
type: string
type: object
type: array
claims:
description: |-
Claims allows customization of the claims that will be mapped to user identity
claims allows customization of the claims that will be mapped to user identity
for Kubernetes access.
properties:
extra:
description: |-
extra is similar to claimMappings.extra from Kubernetes AuthenticationConfiguration as documented in
https://kubernetes.io/docs/reference/access-authn-authz/authentication. However, note that the
Pinniped Concierge issues client certificates to users for the purpose of authenticating, and
the Kubernetes API server does not have any mechanism for transmitting auth extras via client
certificates. When configured, these extras will appear in client certificates issued by the
Pinniped Supervisor in the x509 Subject field as Organizational Units (OU). However, when this
client certificate is presented to Kubernetes for authentication, Kubernetes will ignore these
extras. This is probably only useful if you are using a custom authenticating proxy in front
of your Kubernetes API server which can translate these OUs into auth extras, as described by
https://kubernetes.io/docs/reference/access-authn-authz/authentication/#authenticating-proxy.
This is an advanced configuration option. During an end-user login flow, each of these CEL expressions
must evaluate to either a string or an array of strings, or else the user's login will fail.
items:
description: ExtraMapping provides the configuration for a single
extra mapping.
properties:
key:
description: |-
key is a string to use as the extra attribute key.
key must be a domain-prefix path (e.g. example.org/foo). All characters before the first "/" must be a valid
subdomain as defined by RFC 1123. All characters trailing the first "/" must
be valid HTTP Path characters as defined by RFC 3986.
key must be lowercase.
Required to be unique.
type: string
valueExpression:
description: |-
valueExpression is a CEL expression to extract extra attribute value.
valueExpression must produce a string or string array value.
"", [], and null values are treated as the extra mapping not being present.
Empty string values contained within a string array are filtered out.
CEL expressions have access to the contents of the token claims, organized into CEL variable:
- 'claims' is a map of claim names to claim values.
For example, a variable named 'sub' can be accessed as 'claims.sub'.
Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'.
Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
type: string
required:
- key
- valueExpression
type: object
type: array
groups:
description: |-
Groups is the name of the claim which should be read to extract the user's
groups is the name of the claim which should be read to extract the user's
group membership from the JWT token. When not specified, it will default to "groups".
type: string
username:
description: |-
Username is the name of the claim which should be read to extract the
username is the name of the claim which should be read to extract the
username from the JWT token. When not specified, it will default to "username".
type: string
type: object
issuer:
description: |-
Issuer is the OIDC issuer URL that will be used to discover public signing keys. Issuer is
issuer is the OIDC issuer URL that will be used to discover public signing keys. Issuer is
also used to validate the "iss" JWT claim.
minLength: 1
pattern: ^https://
type: string
tls:
description: TLS configuration for communicating with the OIDC provider.
description: tls is the configuration for communicating with the OIDC
provider via TLS.
properties:
certificateAuthorityData:
description: X.509 Certificate Authority (base64-encoded PEM bundle).
@@ -128,12 +224,47 @@ spec:
- name
type: object
type: object
userValidationRules:
description: |-
userValidationRules are rules that are applied to final user before completing authentication.
These allow invariants to be applied to incoming identities such as preventing the
use of the system: prefix that is commonly used by Kubernetes components.
The validation rules are logically ANDed together and must all return true for the validation to pass.
This is similar to claimValidationRules from Kubernetes AuthenticationConfiguration as documented in
https://kubernetes.io/docs/reference/access-authn-authz/authentication.
This is an advanced configuration option. During an end-user login flow, mistakes in this
configuration will cause the user's login to fail.
items:
description: UserValidationRule provides the configuration for a
single user info validation rule.
properties:
expression:
description: |-
expression represents the expression which will be evaluated by CEL.
Must return true for the validation to pass.
CEL expressions have access to the contents of UserInfo, organized into CEL variable:
- 'user' - authentication.k8s.io/v1, Kind=UserInfo object
Refer to https://github.com/kubernetes/api/blob/release-1.28/authentication/v1/types.go#L105-L122 for the definition.
API documentation: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#userinfo-v1-authentication-k8s-io
Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
type: string
message:
description: |-
message customizes the returned error message when rule returns false.
message is a literal string.
type: string
required:
- expression
type: object
type: array
required:
- audience
- issuer
type: object
status:
description: Status of the authenticator.
description: status of the authenticator.
properties:
conditions:
description: Represents the observations of the authenticator's current

View File

@@ -60,6 +60,79 @@ certificate bundle. +
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-33-apis-concierge-authentication-v1alpha1-claimvalidationrule"]
==== ClaimValidationRule
ClaimValidationRule provides the configuration for a single claim validation rule.
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-33-apis-concierge-authentication-v1alpha1-jwtauthenticatorspec[$$JWTAuthenticatorSpec$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`claim`* __string__ | claim is the name of a required claim. +
Same as --oidc-required-claim flag. +
Only string claim keys are supported. +
Mutually exclusive with expression and message. +
| *`requiredValue`* __string__ | requiredValue is the value of a required claim. +
Same as --oidc-required-claim flag. +
Only string claim values are supported. +
If claim is set and requiredValue is not set, the claim must be present with a value set to the empty string. +
Mutually exclusive with expression and message. +
| *`expression`* __string__ | expression represents the expression which will be evaluated by CEL. +
Must produce a boolean. +
CEL expressions have access to the contents of the token claims, organized into CEL variable: +
- 'claims' is a map of claim names to claim values. +
For example, a variable named 'sub' can be accessed as 'claims.sub'. +
Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'. +
Must return true for the validation to pass. +
Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ +
Mutually exclusive with claim and requiredValue. +
| *`message`* __string__ | message customizes the returned error message when expression returns false. +
message is a literal string. +
Mutually exclusive with claim and requiredValue. +
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-33-apis-concierge-authentication-v1alpha1-extramapping"]
==== ExtraMapping
ExtraMapping provides the configuration for a single extra mapping.
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-33-apis-concierge-authentication-v1alpha1-jwttokenclaims[$$JWTTokenClaims$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`key`* __string__ | key is a string to use as the extra attribute key. +
key must be a domain-prefix path (e.g. example.org/foo). All characters before the first "/" must be a valid +
subdomain as defined by RFC 1123. All characters trailing the first "/" must +
be valid HTTP Path characters as defined by RFC 3986. +
key must be lowercase. +
Required to be unique. +
| *`valueExpression`* __string__ | valueExpression is a CEL expression to extract extra attribute value. +
valueExpression must produce a string or string array value. +
"", [], and null values are treated as the extra mapping not being present. +
Empty string values contained within a string array are filtered out. +
CEL expressions have access to the contents of the token claims, organized into CEL variable: +
- 'claims' is a map of claim names to claim values. +
For example, a variable named 'sub' can be accessed as 'claims.sub'. +
Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'. +
Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ +
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-33-apis-concierge-authentication-v1alpha1-jwtauthenticator"]
==== JWTAuthenticator
@@ -78,8 +151,8 @@ signature, existence of claims, etc.) and extract the username and groups from t
| Field | Description
| *`metadata`* __link:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.33/#objectmeta-v1-meta[$$ObjectMeta$$]__ | Refer to Kubernetes API documentation for fields of `metadata`.
| *`spec`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-33-apis-concierge-authentication-v1alpha1-jwtauthenticatorspec[$$JWTAuthenticatorSpec$$]__ | Spec for configuring the authenticator. +
| *`status`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-33-apis-concierge-authentication-v1alpha1-jwtauthenticatorstatus[$$JWTAuthenticatorStatus$$]__ | Status of the authenticator. +
| *`spec`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-33-apis-concierge-authentication-v1alpha1-jwtauthenticatorspec[$$JWTAuthenticatorSpec$$]__ | spec for configuring the authenticator. +
| *`status`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-33-apis-concierge-authentication-v1alpha1-jwtauthenticatorstatus[$$JWTAuthenticatorStatus$$]__ | status of the authenticator. +
|===
@@ -100,7 +173,7 @@ signature, existence of claims, etc.) and extract the username and groups from t
[id="{anchor_prefix}-go-pinniped-dev-generated-1-33-apis-concierge-authentication-v1alpha1-jwtauthenticatorspec"]
==== JWTAuthenticatorSpec
Spec for configuring a JWT authenticator.
JWTAuthenticatorSpec is the spec for configuring a JWT authenticator.
.Appears In:
****
@@ -110,19 +183,32 @@ Spec for configuring a JWT authenticator.
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`issuer`* __string__ | Issuer is the OIDC issuer URL that will be used to discover public signing keys. Issuer is +
| *`issuer`* __string__ | issuer is the OIDC issuer URL that will be used to discover public signing keys. Issuer is +
also used to validate the "iss" JWT claim. +
| *`audience`* __string__ | Audience is the required value of the "aud" JWT claim. +
| *`claims`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-33-apis-concierge-authentication-v1alpha1-jwttokenclaims[$$JWTTokenClaims$$]__ | Claims allows customization of the claims that will be mapped to user identity +
| *`audience`* __string__ | audience is the required value of the "aud" JWT claim. +
| *`claims`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-33-apis-concierge-authentication-v1alpha1-jwttokenclaims[$$JWTTokenClaims$$]__ | claims allows customization of the claims that will be mapped to user identity +
for Kubernetes access. +
| *`tls`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-33-apis-concierge-authentication-v1alpha1-tlsspec[$$TLSSpec$$]__ | TLS configuration for communicating with the OIDC provider. +
| *`claimValidationRules`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-33-apis-concierge-authentication-v1alpha1-claimvalidationrule[$$ClaimValidationRule$$] array__ | claimValidationRules are rules that are applied to validate token claims to authenticate users. +
This is similar to claimValidationRules from Kubernetes AuthenticationConfiguration as documented in +
https://kubernetes.io/docs/reference/access-authn-authz/authentication. +
This is an advanced configuration option. During an end-user login flow, mistakes in this +
configuration will cause the user's login to fail. +
| *`userValidationRules`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-33-apis-concierge-authentication-v1alpha1-uservalidationrule[$$UserValidationRule$$] array__ | userValidationRules are rules that are applied to final user before completing authentication. +
These allow invariants to be applied to incoming identities such as preventing the +
use of the system: prefix that is commonly used by Kubernetes components. +
The validation rules are logically ANDed together and must all return true for the validation to pass. +
This is similar to claimValidationRules from Kubernetes AuthenticationConfiguration as documented in +
https://kubernetes.io/docs/reference/access-authn-authz/authentication. +
This is an advanced configuration option. During an end-user login flow, mistakes in this +
configuration will cause the user's login to fail. +
| *`tls`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-33-apis-concierge-authentication-v1alpha1-tlsspec[$$TLSSpec$$]__ | tls is the configuration for communicating with the OIDC provider via TLS. +
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-33-apis-concierge-authentication-v1alpha1-jwtauthenticatorstatus"]
==== JWTAuthenticatorStatus
Status of a JWT authenticator.
JWTAuthenticatorStatus is the status of a JWT authenticator.
.Appears In:
****
@@ -151,10 +237,22 @@ for Kubernetes access.
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`groups`* __string__ | Groups is the name of the claim which should be read to extract the user's +
| *`groups`* __string__ | groups is the name of the claim which should be read to extract the user's +
group membership from the JWT token. When not specified, it will default to "groups". +
| *`username`* __string__ | Username is the name of the claim which should be read to extract the +
| *`username`* __string__ | username is the name of the claim which should be read to extract the +
username from the JWT token. When not specified, it will default to "username". +
| *`extra`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-33-apis-concierge-authentication-v1alpha1-extramapping[$$ExtraMapping$$] array__ | extra is similar to claimMappings.extra from Kubernetes AuthenticationConfiguration as documented in +
https://kubernetes.io/docs/reference/access-authn-authz/authentication. However, note that the +
Pinniped Concierge issues client certificates to users for the purpose of authenticating, and +
the Kubernetes API server does not have any mechanism for transmitting auth extras via client +
certificates. When configured, these extras will appear in client certificates issued by the +
Pinniped Supervisor in the x509 Subject field as Organizational Units (OU). However, when this +
client certificate is presented to Kubernetes for authentication, Kubernetes will ignore these +
extras. This is probably only useful if you are using a custom authenticating proxy in front +
of your Kubernetes API server which can translate these OUs into auth extras, as described by +
https://kubernetes.io/docs/reference/access-authn-authz/authentication/#authenticating-proxy. +
This is an advanced configuration option. During an end-user login flow, each of these CEL expressions +
must evaluate to either a string or an array of strings, or else the user's login will fail. +
|===
@@ -178,6 +276,33 @@ Any changes to the CA bundle in the secret or configmap will be dynamically relo
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-33-apis-concierge-authentication-v1alpha1-uservalidationrule"]
==== UserValidationRule
UserValidationRule provides the configuration for a single user info validation rule.
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-33-apis-concierge-authentication-v1alpha1-jwtauthenticatorspec[$$JWTAuthenticatorSpec$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`expression`* __string__ | expression represents the expression which will be evaluated by CEL. +
Must return true for the validation to pass. +
CEL expressions have access to the contents of UserInfo, organized into CEL variable: +
- 'user' - authentication.k8s.io/v1, Kind=UserInfo object +
Refer to https://github.com/kubernetes/api/blob/release-1.28/authentication/v1/types.go#L105-L122 for the definition. +
API documentation: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#userinfo-v1-authentication-k8s-io +
Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ +
| *`message`* __string__ | message customizes the returned error message when rule returns false. +
message is a literal string. +
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-33-apis-concierge-authentication-v1alpha1-webhookauthenticator"]
==== WebhookAuthenticator

View File

@@ -18,7 +18,7 @@ const (
JWTAuthenticatorPhaseError JWTAuthenticatorPhase = "Error"
)
// Status of a JWT authenticator.
// JWTAuthenticatorStatus is the status of a JWT authenticator.
type JWTAuthenticatorStatus struct {
// Represents the observations of the authenticator's current state.
// +patchMergeKey=type
@@ -26,46 +26,168 @@ type JWTAuthenticatorStatus struct {
// +listType=map
// +listMapKey=type
Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"`
// Phase summarizes the overall status of the JWTAuthenticator.
// +kubebuilder:default=Pending
// +kubebuilder:validation:Enum=Pending;Ready;Error
Phase JWTAuthenticatorPhase `json:"phase,omitempty"`
}
// Spec for configuring a JWT authenticator.
// JWTAuthenticatorSpec is the spec for configuring a JWT authenticator.
type JWTAuthenticatorSpec struct {
// Issuer is the OIDC issuer URL that will be used to discover public signing keys. Issuer is
// issuer is the OIDC issuer URL that will be used to discover public signing keys. Issuer is
// also used to validate the "iss" JWT claim.
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:Pattern=`^https://`
Issuer string `json:"issuer"`
// Audience is the required value of the "aud" JWT claim.
// audience is the required value of the "aud" JWT claim.
// +kubebuilder:validation:MinLength=1
Audience string `json:"audience"`
// Claims allows customization of the claims that will be mapped to user identity
// claims allows customization of the claims that will be mapped to user identity
// for Kubernetes access.
// +optional
Claims JWTTokenClaims `json:"claims"`
// TLS configuration for communicating with the OIDC provider.
// claimValidationRules are rules that are applied to validate token claims to authenticate users.
// This is similar to claimValidationRules from Kubernetes AuthenticationConfiguration as documented in
// https://kubernetes.io/docs/reference/access-authn-authz/authentication.
// This is an advanced configuration option. During an end-user login flow, mistakes in this
// configuration will cause the user's login to fail.
// +optional
ClaimValidationRules []ClaimValidationRule `json:"claimValidationRules,omitempty"`
// userValidationRules are rules that are applied to final user before completing authentication.
// These allow invariants to be applied to incoming identities such as preventing the
// use of the system: prefix that is commonly used by Kubernetes components.
// The validation rules are logically ANDed together and must all return true for the validation to pass.
// This is similar to claimValidationRules from Kubernetes AuthenticationConfiguration as documented in
// https://kubernetes.io/docs/reference/access-authn-authz/authentication.
// This is an advanced configuration option. During an end-user login flow, mistakes in this
// configuration will cause the user's login to fail.
// +optional
UserValidationRules []UserValidationRule `json:"userValidationRules,omitempty"`
// tls is the configuration for communicating with the OIDC provider via TLS.
// +optional
TLS *TLSSpec `json:"tls,omitempty"`
}
// ClaimValidationRule provides the configuration for a single claim validation rule.
type ClaimValidationRule struct {
// claim is the name of a required claim.
// Same as --oidc-required-claim flag.
// Only string claim keys are supported.
// Mutually exclusive with expression and message.
// +optional
Claim string `json:"claim,omitempty"`
// requiredValue is the value of a required claim.
// Same as --oidc-required-claim flag.
// Only string claim values are supported.
// If claim is set and requiredValue is not set, the claim must be present with a value set to the empty string.
// Mutually exclusive with expression and message.
// +optional
RequiredValue string `json:"requiredValue,omitempty"`
// expression represents the expression which will be evaluated by CEL.
// Must produce a boolean.
//
// CEL expressions have access to the contents of the token claims, organized into CEL variable:
// - 'claims' is a map of claim names to claim values.
// For example, a variable named 'sub' can be accessed as 'claims.sub'.
// Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'.
// Must return true for the validation to pass.
//
// Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
//
// Mutually exclusive with claim and requiredValue.
// +optional
Expression string `json:"expression,omitempty"`
// message customizes the returned error message when expression returns false.
// message is a literal string.
// Mutually exclusive with claim and requiredValue.
// +optional
Message string `json:"message,omitempty"`
}
// UserValidationRule provides the configuration for a single user info validation rule.
type UserValidationRule struct {
// expression represents the expression which will be evaluated by CEL.
// Must return true for the validation to pass.
//
// CEL expressions have access to the contents of UserInfo, organized into CEL variable:
// - 'user' - authentication.k8s.io/v1, Kind=UserInfo object
// Refer to https://github.com/kubernetes/api/blob/release-1.28/authentication/v1/types.go#L105-L122 for the definition.
// API documentation: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#userinfo-v1-authentication-k8s-io
//
// Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
//
// +required
Expression string `json:"expression"`
// message customizes the returned error message when rule returns false.
// message is a literal string.
// +optional
Message string `json:"message,omitempty"`
}
// JWTTokenClaims allows customization of the claims that will be mapped to user identity
// for Kubernetes access.
type JWTTokenClaims struct {
// Groups is the name of the claim which should be read to extract the user's
// groups is the name of the claim which should be read to extract the user's
// group membership from the JWT token. When not specified, it will default to "groups".
// +optional
Groups string `json:"groups"`
// Username is the name of the claim which should be read to extract the
// username is the name of the claim which should be read to extract the
// username from the JWT token. When not specified, it will default to "username".
// +optional
Username string `json:"username"`
// extra is similar to claimMappings.extra from Kubernetes AuthenticationConfiguration as documented in
// https://kubernetes.io/docs/reference/access-authn-authz/authentication. However, note that the
// Pinniped Concierge issues client certificates to users for the purpose of authenticating, and
// the Kubernetes API server does not have any mechanism for transmitting auth extras via client
// certificates. When configured, these extras will appear in client certificates issued by the
// Pinniped Supervisor in the x509 Subject field as Organizational Units (OU). However, when this
// client certificate is presented to Kubernetes for authentication, Kubernetes will ignore these
// extras. This is probably only useful if you are using a custom authenticating proxy in front
// of your Kubernetes API server which can translate these OUs into auth extras, as described by
// https://kubernetes.io/docs/reference/access-authn-authz/authentication/#authenticating-proxy.
// This is an advanced configuration option. During an end-user login flow, each of these CEL expressions
// must evaluate to either a string or an array of strings, or else the user's login will fail.
// +optional
Extra []ExtraMapping `json:"extra,omitempty"`
}
// ExtraMapping provides the configuration for a single extra mapping.
type ExtraMapping struct {
// key is a string to use as the extra attribute key.
// key must be a domain-prefix path (e.g. example.org/foo). All characters before the first "/" must be a valid
// subdomain as defined by RFC 1123. All characters trailing the first "/" must
// be valid HTTP Path characters as defined by RFC 3986.
// key must be lowercase.
// Required to be unique.
// +required
Key string `json:"key"`
// valueExpression is a CEL expression to extract extra attribute value.
// valueExpression must produce a string or string array value.
// "", [], and null values are treated as the extra mapping not being present.
// Empty string values contained within a string array are filtered out.
//
// CEL expressions have access to the contents of the token claims, organized into CEL variable:
// - 'claims' is a map of claim names to claim values.
// For example, a variable named 'sub' can be accessed as 'claims.sub'.
// Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'.
//
// Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
//
// +required
ValueExpression string `json:"valueExpression"`
}
// JWTAuthenticator describes the configuration of a JWT authenticator.
@@ -86,14 +208,14 @@ type JWTAuthenticator struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
// Spec for configuring the authenticator.
// spec for configuring the authenticator.
Spec JWTAuthenticatorSpec `json:"spec"`
// Status of the authenticator.
// status of the authenticator.
Status JWTAuthenticatorStatus `json:"status,omitempty"`
}
// List of JWTAuthenticator objects.
// JWTAuthenticatorList is a list of JWTAuthenticator objects.
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type JWTAuthenticatorList struct {
metav1.TypeMeta `json:",inline"`

View File

@@ -29,6 +29,38 @@ func (in *CertificateAuthorityDataSourceSpec) DeepCopy() *CertificateAuthorityDa
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ClaimValidationRule) DeepCopyInto(out *ClaimValidationRule) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClaimValidationRule.
func (in *ClaimValidationRule) DeepCopy() *ClaimValidationRule {
if in == nil {
return nil
}
out := new(ClaimValidationRule)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ExtraMapping) DeepCopyInto(out *ExtraMapping) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExtraMapping.
func (in *ExtraMapping) DeepCopy() *ExtraMapping {
if in == nil {
return nil
}
out := new(ExtraMapping)
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
@@ -93,7 +125,17 @@ func (in *JWTAuthenticatorList) DeepCopyObject() runtime.Object {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *JWTAuthenticatorSpec) DeepCopyInto(out *JWTAuthenticatorSpec) {
*out = *in
out.Claims = in.Claims
in.Claims.DeepCopyInto(&out.Claims)
if in.ClaimValidationRules != nil {
in, out := &in.ClaimValidationRules, &out.ClaimValidationRules
*out = make([]ClaimValidationRule, len(*in))
copy(*out, *in)
}
if in.UserValidationRules != nil {
in, out := &in.UserValidationRules, &out.UserValidationRules
*out = make([]UserValidationRule, len(*in))
copy(*out, *in)
}
if in.TLS != nil {
in, out := &in.TLS, &out.TLS
*out = new(TLSSpec)
@@ -138,6 +180,11 @@ func (in *JWTAuthenticatorStatus) DeepCopy() *JWTAuthenticatorStatus {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *JWTTokenClaims) DeepCopyInto(out *JWTTokenClaims) {
*out = *in
if in.Extra != nil {
in, out := &in.Extra, &out.Extra
*out = make([]ExtraMapping, len(*in))
copy(*out, *in)
}
return
}
@@ -172,6 +219,22 @@ func (in *TLSSpec) DeepCopy() *TLSSpec {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *UserValidationRule) DeepCopyInto(out *UserValidationRule) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UserValidationRule.
func (in *UserValidationRule) DeepCopy() *UserValidationRule {
if in == nil {
return nil
}
out := new(UserValidationRule)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *WebhookAuthenticator) DeepCopyInto(out *WebhookAuthenticator) {
*out = *in

View File

@@ -58,37 +58,133 @@ spec:
metadata:
type: object
spec:
description: Spec for configuring the authenticator.
description: spec for configuring the authenticator.
properties:
audience:
description: Audience is the required value of the "aud" JWT claim.
description: audience is the required value of the "aud" JWT claim.
minLength: 1
type: string
claimValidationRules:
description: |-
claimValidationRules are rules that are applied to validate token claims to authenticate users.
This is similar to claimValidationRules from Kubernetes AuthenticationConfiguration as documented in
https://kubernetes.io/docs/reference/access-authn-authz/authentication.
This is an advanced configuration option. During an end-user login flow, mistakes in this
configuration will cause the user's login to fail.
items:
description: ClaimValidationRule provides the configuration for
a single claim validation rule.
properties:
claim:
description: |-
claim is the name of a required claim.
Same as --oidc-required-claim flag.
Only string claim keys are supported.
Mutually exclusive with expression and message.
type: string
expression:
description: |-
expression represents the expression which will be evaluated by CEL.
Must produce a boolean.
CEL expressions have access to the contents of the token claims, organized into CEL variable:
- 'claims' is a map of claim names to claim values.
For example, a variable named 'sub' can be accessed as 'claims.sub'.
Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'.
Must return true for the validation to pass.
Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
Mutually exclusive with claim and requiredValue.
type: string
message:
description: |-
message customizes the returned error message when expression returns false.
message is a literal string.
Mutually exclusive with claim and requiredValue.
type: string
requiredValue:
description: |-
requiredValue is the value of a required claim.
Same as --oidc-required-claim flag.
Only string claim values are supported.
If claim is set and requiredValue is not set, the claim must be present with a value set to the empty string.
Mutually exclusive with expression and message.
type: string
type: object
type: array
claims:
description: |-
Claims allows customization of the claims that will be mapped to user identity
claims allows customization of the claims that will be mapped to user identity
for Kubernetes access.
properties:
extra:
description: |-
extra is similar to claimMappings.extra from Kubernetes AuthenticationConfiguration as documented in
https://kubernetes.io/docs/reference/access-authn-authz/authentication. However, note that the
Pinniped Concierge issues client certificates to users for the purpose of authenticating, and
the Kubernetes API server does not have any mechanism for transmitting auth extras via client
certificates. When configured, these extras will appear in client certificates issued by the
Pinniped Supervisor in the x509 Subject field as Organizational Units (OU). However, when this
client certificate is presented to Kubernetes for authentication, Kubernetes will ignore these
extras. This is probably only useful if you are using a custom authenticating proxy in front
of your Kubernetes API server which can translate these OUs into auth extras, as described by
https://kubernetes.io/docs/reference/access-authn-authz/authentication/#authenticating-proxy.
This is an advanced configuration option. During an end-user login flow, each of these CEL expressions
must evaluate to either a string or an array of strings, or else the user's login will fail.
items:
description: ExtraMapping provides the configuration for a single
extra mapping.
properties:
key:
description: |-
key is a string to use as the extra attribute key.
key must be a domain-prefix path (e.g. example.org/foo). All characters before the first "/" must be a valid
subdomain as defined by RFC 1123. All characters trailing the first "/" must
be valid HTTP Path characters as defined by RFC 3986.
key must be lowercase.
Required to be unique.
type: string
valueExpression:
description: |-
valueExpression is a CEL expression to extract extra attribute value.
valueExpression must produce a string or string array value.
"", [], and null values are treated as the extra mapping not being present.
Empty string values contained within a string array are filtered out.
CEL expressions have access to the contents of the token claims, organized into CEL variable:
- 'claims' is a map of claim names to claim values.
For example, a variable named 'sub' can be accessed as 'claims.sub'.
Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'.
Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
type: string
required:
- key
- valueExpression
type: object
type: array
groups:
description: |-
Groups is the name of the claim which should be read to extract the user's
groups is the name of the claim which should be read to extract the user's
group membership from the JWT token. When not specified, it will default to "groups".
type: string
username:
description: |-
Username is the name of the claim which should be read to extract the
username is the name of the claim which should be read to extract the
username from the JWT token. When not specified, it will default to "username".
type: string
type: object
issuer:
description: |-
Issuer is the OIDC issuer URL that will be used to discover public signing keys. Issuer is
issuer is the OIDC issuer URL that will be used to discover public signing keys. Issuer is
also used to validate the "iss" JWT claim.
minLength: 1
pattern: ^https://
type: string
tls:
description: TLS configuration for communicating with the OIDC provider.
description: tls is the configuration for communicating with the OIDC
provider via TLS.
properties:
certificateAuthorityData:
description: X.509 Certificate Authority (base64-encoded PEM bundle).
@@ -128,12 +224,47 @@ spec:
- name
type: object
type: object
userValidationRules:
description: |-
userValidationRules are rules that are applied to final user before completing authentication.
These allow invariants to be applied to incoming identities such as preventing the
use of the system: prefix that is commonly used by Kubernetes components.
The validation rules are logically ANDed together and must all return true for the validation to pass.
This is similar to claimValidationRules from Kubernetes AuthenticationConfiguration as documented in
https://kubernetes.io/docs/reference/access-authn-authz/authentication.
This is an advanced configuration option. During an end-user login flow, mistakes in this
configuration will cause the user's login to fail.
items:
description: UserValidationRule provides the configuration for a
single user info validation rule.
properties:
expression:
description: |-
expression represents the expression which will be evaluated by CEL.
Must return true for the validation to pass.
CEL expressions have access to the contents of UserInfo, organized into CEL variable:
- 'user' - authentication.k8s.io/v1, Kind=UserInfo object
Refer to https://github.com/kubernetes/api/blob/release-1.28/authentication/v1/types.go#L105-L122 for the definition.
API documentation: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#userinfo-v1-authentication-k8s-io
Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
type: string
message:
description: |-
message customizes the returned error message when rule returns false.
message is a literal string.
type: string
required:
- expression
type: object
type: array
required:
- audience
- issuer
type: object
status:
description: Status of the authenticator.
description: status of the authenticator.
properties:
conditions:
description: Represents the observations of the authenticator's current

View File

@@ -60,6 +60,79 @@ certificate bundle. +
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-33-apis-concierge-authentication-v1alpha1-claimvalidationrule"]
==== ClaimValidationRule
ClaimValidationRule provides the configuration for a single claim validation rule.
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-33-apis-concierge-authentication-v1alpha1-jwtauthenticatorspec[$$JWTAuthenticatorSpec$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`claim`* __string__ | claim is the name of a required claim. +
Same as --oidc-required-claim flag. +
Only string claim keys are supported. +
Mutually exclusive with expression and message. +
| *`requiredValue`* __string__ | requiredValue is the value of a required claim. +
Same as --oidc-required-claim flag. +
Only string claim values are supported. +
If claim is set and requiredValue is not set, the claim must be present with a value set to the empty string. +
Mutually exclusive with expression and message. +
| *`expression`* __string__ | expression represents the expression which will be evaluated by CEL. +
Must produce a boolean. +
CEL expressions have access to the contents of the token claims, organized into CEL variable: +
- 'claims' is a map of claim names to claim values. +
For example, a variable named 'sub' can be accessed as 'claims.sub'. +
Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'. +
Must return true for the validation to pass. +
Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ +
Mutually exclusive with claim and requiredValue. +
| *`message`* __string__ | message customizes the returned error message when expression returns false. +
message is a literal string. +
Mutually exclusive with claim and requiredValue. +
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-33-apis-concierge-authentication-v1alpha1-extramapping"]
==== ExtraMapping
ExtraMapping provides the configuration for a single extra mapping.
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-33-apis-concierge-authentication-v1alpha1-jwttokenclaims[$$JWTTokenClaims$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`key`* __string__ | key is a string to use as the extra attribute key. +
key must be a domain-prefix path (e.g. example.org/foo). All characters before the first "/" must be a valid +
subdomain as defined by RFC 1123. All characters trailing the first "/" must +
be valid HTTP Path characters as defined by RFC 3986. +
key must be lowercase. +
Required to be unique. +
| *`valueExpression`* __string__ | valueExpression is a CEL expression to extract extra attribute value. +
valueExpression must produce a string or string array value. +
"", [], and null values are treated as the extra mapping not being present. +
Empty string values contained within a string array are filtered out. +
CEL expressions have access to the contents of the token claims, organized into CEL variable: +
- 'claims' is a map of claim names to claim values. +
For example, a variable named 'sub' can be accessed as 'claims.sub'. +
Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'. +
Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ +
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-33-apis-concierge-authentication-v1alpha1-jwtauthenticator"]
==== JWTAuthenticator
@@ -78,8 +151,8 @@ signature, existence of claims, etc.) and extract the username and groups from t
| Field | Description
| *`metadata`* __link:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.33/#objectmeta-v1-meta[$$ObjectMeta$$]__ | Refer to Kubernetes API documentation for fields of `metadata`.
| *`spec`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-33-apis-concierge-authentication-v1alpha1-jwtauthenticatorspec[$$JWTAuthenticatorSpec$$]__ | Spec for configuring the authenticator. +
| *`status`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-33-apis-concierge-authentication-v1alpha1-jwtauthenticatorstatus[$$JWTAuthenticatorStatus$$]__ | Status of the authenticator. +
| *`spec`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-33-apis-concierge-authentication-v1alpha1-jwtauthenticatorspec[$$JWTAuthenticatorSpec$$]__ | spec for configuring the authenticator. +
| *`status`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-33-apis-concierge-authentication-v1alpha1-jwtauthenticatorstatus[$$JWTAuthenticatorStatus$$]__ | status of the authenticator. +
|===
@@ -100,7 +173,7 @@ signature, existence of claims, etc.) and extract the username and groups from t
[id="{anchor_prefix}-go-pinniped-dev-generated-1-33-apis-concierge-authentication-v1alpha1-jwtauthenticatorspec"]
==== JWTAuthenticatorSpec
Spec for configuring a JWT authenticator.
JWTAuthenticatorSpec is the spec for configuring a JWT authenticator.
.Appears In:
****
@@ -110,19 +183,32 @@ Spec for configuring a JWT authenticator.
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`issuer`* __string__ | Issuer is the OIDC issuer URL that will be used to discover public signing keys. Issuer is +
| *`issuer`* __string__ | issuer is the OIDC issuer URL that will be used to discover public signing keys. Issuer is +
also used to validate the "iss" JWT claim. +
| *`audience`* __string__ | Audience is the required value of the "aud" JWT claim. +
| *`claims`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-33-apis-concierge-authentication-v1alpha1-jwttokenclaims[$$JWTTokenClaims$$]__ | Claims allows customization of the claims that will be mapped to user identity +
| *`audience`* __string__ | audience is the required value of the "aud" JWT claim. +
| *`claims`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-33-apis-concierge-authentication-v1alpha1-jwttokenclaims[$$JWTTokenClaims$$]__ | claims allows customization of the claims that will be mapped to user identity +
for Kubernetes access. +
| *`tls`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-33-apis-concierge-authentication-v1alpha1-tlsspec[$$TLSSpec$$]__ | TLS configuration for communicating with the OIDC provider. +
| *`claimValidationRules`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-33-apis-concierge-authentication-v1alpha1-claimvalidationrule[$$ClaimValidationRule$$] array__ | claimValidationRules are rules that are applied to validate token claims to authenticate users. +
This is similar to claimValidationRules from Kubernetes AuthenticationConfiguration as documented in +
https://kubernetes.io/docs/reference/access-authn-authz/authentication. +
This is an advanced configuration option. During an end-user login flow, mistakes in this +
configuration will cause the user's login to fail. +
| *`userValidationRules`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-33-apis-concierge-authentication-v1alpha1-uservalidationrule[$$UserValidationRule$$] array__ | userValidationRules are rules that are applied to final user before completing authentication. +
These allow invariants to be applied to incoming identities such as preventing the +
use of the system: prefix that is commonly used by Kubernetes components. +
The validation rules are logically ANDed together and must all return true for the validation to pass. +
This is similar to claimValidationRules from Kubernetes AuthenticationConfiguration as documented in +
https://kubernetes.io/docs/reference/access-authn-authz/authentication. +
This is an advanced configuration option. During an end-user login flow, mistakes in this +
configuration will cause the user's login to fail. +
| *`tls`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-33-apis-concierge-authentication-v1alpha1-tlsspec[$$TLSSpec$$]__ | tls is the configuration for communicating with the OIDC provider via TLS. +
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-33-apis-concierge-authentication-v1alpha1-jwtauthenticatorstatus"]
==== JWTAuthenticatorStatus
Status of a JWT authenticator.
JWTAuthenticatorStatus is the status of a JWT authenticator.
.Appears In:
****
@@ -151,10 +237,22 @@ for Kubernetes access.
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`groups`* __string__ | Groups is the name of the claim which should be read to extract the user's +
| *`groups`* __string__ | groups is the name of the claim which should be read to extract the user's +
group membership from the JWT token. When not specified, it will default to "groups". +
| *`username`* __string__ | Username is the name of the claim which should be read to extract the +
| *`username`* __string__ | username is the name of the claim which should be read to extract the +
username from the JWT token. When not specified, it will default to "username". +
| *`extra`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-33-apis-concierge-authentication-v1alpha1-extramapping[$$ExtraMapping$$] array__ | extra is similar to claimMappings.extra from Kubernetes AuthenticationConfiguration as documented in +
https://kubernetes.io/docs/reference/access-authn-authz/authentication. However, note that the +
Pinniped Concierge issues client certificates to users for the purpose of authenticating, and +
the Kubernetes API server does not have any mechanism for transmitting auth extras via client +
certificates. When configured, these extras will appear in client certificates issued by the +
Pinniped Supervisor in the x509 Subject field as Organizational Units (OU). However, when this +
client certificate is presented to Kubernetes for authentication, Kubernetes will ignore these +
extras. This is probably only useful if you are using a custom authenticating proxy in front +
of your Kubernetes API server which can translate these OUs into auth extras, as described by +
https://kubernetes.io/docs/reference/access-authn-authz/authentication/#authenticating-proxy. +
This is an advanced configuration option. During an end-user login flow, each of these CEL expressions +
must evaluate to either a string or an array of strings, or else the user's login will fail. +
|===
@@ -178,6 +276,33 @@ Any changes to the CA bundle in the secret or configmap will be dynamically relo
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-33-apis-concierge-authentication-v1alpha1-uservalidationrule"]
==== UserValidationRule
UserValidationRule provides the configuration for a single user info validation rule.
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-33-apis-concierge-authentication-v1alpha1-jwtauthenticatorspec[$$JWTAuthenticatorSpec$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`expression`* __string__ | expression represents the expression which will be evaluated by CEL. +
Must return true for the validation to pass. +
CEL expressions have access to the contents of UserInfo, organized into CEL variable: +
- 'user' - authentication.k8s.io/v1, Kind=UserInfo object +
Refer to https://github.com/kubernetes/api/blob/release-1.28/authentication/v1/types.go#L105-L122 for the definition. +
API documentation: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#userinfo-v1-authentication-k8s-io +
Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ +
| *`message`* __string__ | message customizes the returned error message when rule returns false. +
message is a literal string. +
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-33-apis-concierge-authentication-v1alpha1-webhookauthenticator"]
==== WebhookAuthenticator

View File

@@ -18,7 +18,7 @@ const (
JWTAuthenticatorPhaseError JWTAuthenticatorPhase = "Error"
)
// Status of a JWT authenticator.
// JWTAuthenticatorStatus is the status of a JWT authenticator.
type JWTAuthenticatorStatus struct {
// Represents the observations of the authenticator's current state.
// +patchMergeKey=type
@@ -26,46 +26,168 @@ type JWTAuthenticatorStatus struct {
// +listType=map
// +listMapKey=type
Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"`
// Phase summarizes the overall status of the JWTAuthenticator.
// +kubebuilder:default=Pending
// +kubebuilder:validation:Enum=Pending;Ready;Error
Phase JWTAuthenticatorPhase `json:"phase,omitempty"`
}
// Spec for configuring a JWT authenticator.
// JWTAuthenticatorSpec is the spec for configuring a JWT authenticator.
type JWTAuthenticatorSpec struct {
// Issuer is the OIDC issuer URL that will be used to discover public signing keys. Issuer is
// issuer is the OIDC issuer URL that will be used to discover public signing keys. Issuer is
// also used to validate the "iss" JWT claim.
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:Pattern=`^https://`
Issuer string `json:"issuer"`
// Audience is the required value of the "aud" JWT claim.
// audience is the required value of the "aud" JWT claim.
// +kubebuilder:validation:MinLength=1
Audience string `json:"audience"`
// Claims allows customization of the claims that will be mapped to user identity
// claims allows customization of the claims that will be mapped to user identity
// for Kubernetes access.
// +optional
Claims JWTTokenClaims `json:"claims"`
// TLS configuration for communicating with the OIDC provider.
// claimValidationRules are rules that are applied to validate token claims to authenticate users.
// This is similar to claimValidationRules from Kubernetes AuthenticationConfiguration as documented in
// https://kubernetes.io/docs/reference/access-authn-authz/authentication.
// This is an advanced configuration option. During an end-user login flow, mistakes in this
// configuration will cause the user's login to fail.
// +optional
ClaimValidationRules []ClaimValidationRule `json:"claimValidationRules,omitempty"`
// userValidationRules are rules that are applied to final user before completing authentication.
// These allow invariants to be applied to incoming identities such as preventing the
// use of the system: prefix that is commonly used by Kubernetes components.
// The validation rules are logically ANDed together and must all return true for the validation to pass.
// This is similar to claimValidationRules from Kubernetes AuthenticationConfiguration as documented in
// https://kubernetes.io/docs/reference/access-authn-authz/authentication.
// This is an advanced configuration option. During an end-user login flow, mistakes in this
// configuration will cause the user's login to fail.
// +optional
UserValidationRules []UserValidationRule `json:"userValidationRules,omitempty"`
// tls is the configuration for communicating with the OIDC provider via TLS.
// +optional
TLS *TLSSpec `json:"tls,omitempty"`
}
// ClaimValidationRule provides the configuration for a single claim validation rule.
type ClaimValidationRule struct {
// claim is the name of a required claim.
// Same as --oidc-required-claim flag.
// Only string claim keys are supported.
// Mutually exclusive with expression and message.
// +optional
Claim string `json:"claim,omitempty"`
// requiredValue is the value of a required claim.
// Same as --oidc-required-claim flag.
// Only string claim values are supported.
// If claim is set and requiredValue is not set, the claim must be present with a value set to the empty string.
// Mutually exclusive with expression and message.
// +optional
RequiredValue string `json:"requiredValue,omitempty"`
// expression represents the expression which will be evaluated by CEL.
// Must produce a boolean.
//
// CEL expressions have access to the contents of the token claims, organized into CEL variable:
// - 'claims' is a map of claim names to claim values.
// For example, a variable named 'sub' can be accessed as 'claims.sub'.
// Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'.
// Must return true for the validation to pass.
//
// Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
//
// Mutually exclusive with claim and requiredValue.
// +optional
Expression string `json:"expression,omitempty"`
// message customizes the returned error message when expression returns false.
// message is a literal string.
// Mutually exclusive with claim and requiredValue.
// +optional
Message string `json:"message,omitempty"`
}
// UserValidationRule provides the configuration for a single user info validation rule.
type UserValidationRule struct {
// expression represents the expression which will be evaluated by CEL.
// Must return true for the validation to pass.
//
// CEL expressions have access to the contents of UserInfo, organized into CEL variable:
// - 'user' - authentication.k8s.io/v1, Kind=UserInfo object
// Refer to https://github.com/kubernetes/api/blob/release-1.28/authentication/v1/types.go#L105-L122 for the definition.
// API documentation: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#userinfo-v1-authentication-k8s-io
//
// Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
//
// +required
Expression string `json:"expression"`
// message customizes the returned error message when rule returns false.
// message is a literal string.
// +optional
Message string `json:"message,omitempty"`
}
// JWTTokenClaims allows customization of the claims that will be mapped to user identity
// for Kubernetes access.
type JWTTokenClaims struct {
// Groups is the name of the claim which should be read to extract the user's
// groups is the name of the claim which should be read to extract the user's
// group membership from the JWT token. When not specified, it will default to "groups".
// +optional
Groups string `json:"groups"`
// Username is the name of the claim which should be read to extract the
// username is the name of the claim which should be read to extract the
// username from the JWT token. When not specified, it will default to "username".
// +optional
Username string `json:"username"`
// extra is similar to claimMappings.extra from Kubernetes AuthenticationConfiguration as documented in
// https://kubernetes.io/docs/reference/access-authn-authz/authentication. However, note that the
// Pinniped Concierge issues client certificates to users for the purpose of authenticating, and
// the Kubernetes API server does not have any mechanism for transmitting auth extras via client
// certificates. When configured, these extras will appear in client certificates issued by the
// Pinniped Supervisor in the x509 Subject field as Organizational Units (OU). However, when this
// client certificate is presented to Kubernetes for authentication, Kubernetes will ignore these
// extras. This is probably only useful if you are using a custom authenticating proxy in front
// of your Kubernetes API server which can translate these OUs into auth extras, as described by
// https://kubernetes.io/docs/reference/access-authn-authz/authentication/#authenticating-proxy.
// This is an advanced configuration option. During an end-user login flow, each of these CEL expressions
// must evaluate to either a string or an array of strings, or else the user's login will fail.
// +optional
Extra []ExtraMapping `json:"extra,omitempty"`
}
// ExtraMapping provides the configuration for a single extra mapping.
type ExtraMapping struct {
// key is a string to use as the extra attribute key.
// key must be a domain-prefix path (e.g. example.org/foo). All characters before the first "/" must be a valid
// subdomain as defined by RFC 1123. All characters trailing the first "/" must
// be valid HTTP Path characters as defined by RFC 3986.
// key must be lowercase.
// Required to be unique.
// +required
Key string `json:"key"`
// valueExpression is a CEL expression to extract extra attribute value.
// valueExpression must produce a string or string array value.
// "", [], and null values are treated as the extra mapping not being present.
// Empty string values contained within a string array are filtered out.
//
// CEL expressions have access to the contents of the token claims, organized into CEL variable:
// - 'claims' is a map of claim names to claim values.
// For example, a variable named 'sub' can be accessed as 'claims.sub'.
// Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'.
//
// Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
//
// +required
ValueExpression string `json:"valueExpression"`
}
// JWTAuthenticator describes the configuration of a JWT authenticator.
@@ -86,14 +208,14 @@ type JWTAuthenticator struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
// Spec for configuring the authenticator.
// spec for configuring the authenticator.
Spec JWTAuthenticatorSpec `json:"spec"`
// Status of the authenticator.
// status of the authenticator.
Status JWTAuthenticatorStatus `json:"status,omitempty"`
}
// List of JWTAuthenticator objects.
// JWTAuthenticatorList is a list of JWTAuthenticator objects.
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type JWTAuthenticatorList struct {
metav1.TypeMeta `json:",inline"`

View File

@@ -29,6 +29,38 @@ func (in *CertificateAuthorityDataSourceSpec) DeepCopy() *CertificateAuthorityDa
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ClaimValidationRule) DeepCopyInto(out *ClaimValidationRule) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClaimValidationRule.
func (in *ClaimValidationRule) DeepCopy() *ClaimValidationRule {
if in == nil {
return nil
}
out := new(ClaimValidationRule)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ExtraMapping) DeepCopyInto(out *ExtraMapping) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExtraMapping.
func (in *ExtraMapping) DeepCopy() *ExtraMapping {
if in == nil {
return nil
}
out := new(ExtraMapping)
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
@@ -93,7 +125,17 @@ func (in *JWTAuthenticatorList) DeepCopyObject() runtime.Object {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *JWTAuthenticatorSpec) DeepCopyInto(out *JWTAuthenticatorSpec) {
*out = *in
out.Claims = in.Claims
in.Claims.DeepCopyInto(&out.Claims)
if in.ClaimValidationRules != nil {
in, out := &in.ClaimValidationRules, &out.ClaimValidationRules
*out = make([]ClaimValidationRule, len(*in))
copy(*out, *in)
}
if in.UserValidationRules != nil {
in, out := &in.UserValidationRules, &out.UserValidationRules
*out = make([]UserValidationRule, len(*in))
copy(*out, *in)
}
if in.TLS != nil {
in, out := &in.TLS, &out.TLS
*out = new(TLSSpec)
@@ -138,6 +180,11 @@ func (in *JWTAuthenticatorStatus) DeepCopy() *JWTAuthenticatorStatus {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *JWTTokenClaims) DeepCopyInto(out *JWTTokenClaims) {
*out = *in
if in.Extra != nil {
in, out := &in.Extra, &out.Extra
*out = make([]ExtraMapping, len(*in))
copy(*out, *in)
}
return
}
@@ -172,6 +219,22 @@ func (in *TLSSpec) DeepCopy() *TLSSpec {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *UserValidationRule) DeepCopyInto(out *UserValidationRule) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UserValidationRule.
func (in *UserValidationRule) DeepCopy() *UserValidationRule {
if in == nil {
return nil
}
out := new(UserValidationRule)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *WebhookAuthenticator) DeepCopyInto(out *WebhookAuthenticator) {
*out = *in

View File

@@ -1,2 +1,2 @@
Copyright 2020-2024 the Pinniped contributors. All Rights Reserved.
Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0

View File

@@ -1,4 +1,4 @@
// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Package certauthority implements a simple x509 certificate authority suitable for use in an aggregated API service.
@@ -170,8 +170,8 @@ func (c *CA) Pool() *x509.CertPool {
// IssueClientCert issues a new client certificate with username and groups included in the Kube-style
// certificate subject for the given identity and duration.
func (c *CA) IssueClientCert(username string, groups []string, ttl time.Duration) (*tls.Certificate, error) {
return c.issueCert(x509.ExtKeyUsageClientAuth, pkix.Name{CommonName: username, Organization: groups}, nil, nil, ttl)
func (c *CA) IssueClientCert(username string, groups []string, extras []string, ttl time.Duration) (*tls.Certificate, error) {
return c.issueCert(x509.ExtKeyUsageClientAuth, pkix.Name{CommonName: username, Organization: groups, OrganizationalUnit: extras}, nil, nil, ttl)
}
// IssueServerCert issues a new server certificate for the given identity and duration.
@@ -182,8 +182,8 @@ func (c *CA) IssueServerCert(dnsNames []string, ips []net.IP, ttl time.Duration)
// IssueClientCertPEM is similar to IssueClientCert, but returns the new cert as a pair of PEM-formatted byte slices
// for the certificate and private key, along with the notBefore and notAfter values.
func (c *CA) IssueClientCertPEM(username string, groups []string, ttl time.Duration) (*cert.PEM, error) {
return toPEM(c.IssueClientCert(username, groups, ttl))
func (c *CA) IssueClientCertPEM(username string, groups []string, extras []string, ttl time.Duration) (*cert.PEM, error) {
return toPEM(c.IssueClientCert(username, groups, extras, ttl))
}
// IssueServerCertPEM is similar to IssueServerCert, but returns the new cert as a pair of PEM-formatted byte slices

View File

@@ -1,4 +1,4 @@
// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package certauthority
@@ -326,7 +326,7 @@ func TestIssue(t *testing.T) {
require.Equal(t, now.Add(-5*time.Minute), got.Leaf.NotBefore) // always back-dated
require.Equal(t, now.Add(10*time.Minute), got.Leaf.NotAfter)
}
got, err = tt.ca.IssueClientCert("test-user", []string{"group1", "group2"}, 10*time.Minute)
got, err = tt.ca.IssueClientCert("test-user", []string{"group1", "group2"}, []string{"extra1=val1", "extra2=val2"}, 10*time.Minute)
if tt.wantErr != "" {
require.EqualError(t, err, tt.wantErr)
require.Nil(t, got)
@@ -378,28 +378,29 @@ func TestIssueMethods(t *testing.T) {
t.Run("client certs", func(t *testing.T) {
user := "test-username"
groups := []string{"group1", "group2"}
extras := []string{"extra1=val1", "extra2=val2"}
clientCert, err := ca.IssueClientCert(user, groups, ttl)
clientCert, err := ca.IssueClientCert(user, groups, extras, ttl)
require.NoError(t, err)
certPEM, keyPEM, err := ToPEM(clientCert)
require.NoError(t, err)
validateClientCert(t, ca.Bundle(), certPEM, keyPEM, user, groups, ttl)
validateClientCert(t, ca.Bundle(), certPEM, keyPEM, user, groups, extras, ttl)
pem, err := ca.IssueClientCertPEM(user, groups, ttl)
pem, err := ca.IssueClientCertPEM(user, groups, extras, ttl)
require.NoError(t, err)
validateClientCert(t, ca.Bundle(), pem.CertPEM, pem.KeyPEM, user, groups, ttl)
validateClientCert(t, ca.Bundle(), pem.CertPEM, pem.KeyPEM, user, groups, extras, ttl)
pem, err = ca.IssueClientCertPEM(user, nil, ttl)
pem, err = ca.IssueClientCertPEM(user, nil, nil, ttl)
require.NoError(t, err)
validateClientCert(t, ca.Bundle(), pem.CertPEM, pem.KeyPEM, user, nil, ttl)
validateClientCert(t, ca.Bundle(), pem.CertPEM, pem.KeyPEM, user, nil, nil, ttl)
pem, err = ca.IssueClientCertPEM(user, []string{}, ttl)
pem, err = ca.IssueClientCertPEM(user, []string{}, []string{}, ttl)
require.NoError(t, err)
validateClientCert(t, ca.Bundle(), pem.CertPEM, pem.KeyPEM, user, nil, ttl)
validateClientCert(t, ca.Bundle(), pem.CertPEM, pem.KeyPEM, user, nil, nil, ttl)
pem, err = ca.IssueClientCertPEM("", []string{}, ttl)
pem, err = ca.IssueClientCertPEM("", []string{}, []string{}, ttl)
require.NoError(t, err)
validateClientCert(t, ca.Bundle(), pem.CertPEM, pem.KeyPEM, "", nil, ttl)
validateClientCert(t, ca.Bundle(), pem.CertPEM, pem.KeyPEM, "", nil, nil, ttl)
})
t.Run("server certs", func(t *testing.T) {
@@ -434,13 +435,14 @@ func TestIssueMethods(t *testing.T) {
})
}
func validateClientCert(t *testing.T, caBundle []byte, certPEM []byte, keyPEM []byte, expectedUser string, expectedGroups []string, expectedTTL time.Duration) {
func validateClientCert(t *testing.T, caBundle []byte, certPEM []byte, keyPEM []byte, expectedUser string, expectedGroups []string, expectedExtras []string, expectedTTL time.Duration) {
const fudgeFactor = 10 * time.Second
v := testutil.ValidateClientCertificate(t, string(caBundle), string(certPEM))
v.RequireLifetime(time.Now(), time.Now().Add(expectedTTL), certBackdate+fudgeFactor)
v.RequireMatchesPrivateKey(string(keyPEM))
v.RequireCommonName(expectedUser)
v.RequireOrganizations(expectedGroups)
v.RequireOrganizationalUnits(expectedExtras)
v.RequireEmptyDNSNames()
v.RequireEmptyIPs()
}

View File

@@ -1,4 +1,4 @@
// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Package dynamiccertauthority implements a x509 certificate authority capable of issuing
@@ -34,7 +34,7 @@ func (c *ca) Name() string {
// IssueClientCertPEM issues a new client certificate for the given identity and duration, returning it as a
// pair of PEM-formatted byte slices for the certificate and private key, along with the notBefore and notAfter values.
func (c *ca) IssueClientCertPEM(username string, groups []string, ttl time.Duration) (*cert.PEM, error) {
func (c *ca) IssueClientCertPEM(username string, groups []string, extras []string, ttl time.Duration) (*cert.PEM, error) {
caCrtPEM, caKeyPEM := c.provider.CurrentCertKeyContent()
// in the future we could split dynamiccert.Private into two interfaces (Private and PrivateRead)
// and have this code take PrivateRead as input. We would then add ourselves as a listener to
@@ -44,5 +44,5 @@ func (c *ca) IssueClientCertPEM(username string, groups []string, ttl time.Durat
return nil, err
}
return ca.IssueClientCertPEM(username, groups, ttl)
return ca.IssueClientCertPEM(username, groups, extras, ttl)
}

View File

@@ -1,4 +1,4 @@
// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package dynamiccertauthority
@@ -91,8 +91,7 @@ func TestCAIssuePEM(t *testing.T) {
}
for _, step := range steps {
t.Run(step.name, func(t *testing.T) {
// Can't run these steps in parallel, because each one depends on the previous steps being
// run.
// Can't run these steps in parallel, because each one depends on the previous steps being run.
pem, err := issuePEM(provider, ca, step.caCrtPEM, step.caKeyPEM)
@@ -108,6 +107,7 @@ func TestCAIssuePEM(t *testing.T) {
crtAssertions := testutil.ValidateClientCertificate(t, string(caCrtPEM), string(pem.CertPEM))
crtAssertions.RequireCommonName("some-username")
crtAssertions.RequireOrganizations([]string{"some-group1", "some-group2"})
crtAssertions.RequireOrganizationalUnits([]string{"extra1=val1", "extra2=val2"})
crtAssertions.RequireLifetime(time.Now(), time.Now().Add(time.Hour*24), time.Minute*10)
crtAssertions.RequireMatchesPrivateKey(string(pem.KeyPEM))
}
@@ -124,5 +124,10 @@ func issuePEM(provider dynamiccert.Provider, ca clientcertissuer.ClientCertIssue
}
// otherwise check to see if there is an issuing error
return ca.IssueClientCertPEM("some-username", []string{"some-group1", "some-group2"}, time.Hour*24)
return ca.IssueClientCertPEM(
"some-username",
[]string{"some-group1", "some-group2"},
[]string{"extra1=val1", "extra2=val2"},
time.Hour*24,
)
}

View File

@@ -1,4 +1,4 @@
// Copyright 2021-2024 the Pinniped contributors. All Rights Reserved.
// Copyright 2021-2025 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package clientcertissuer
@@ -18,7 +18,7 @@ const defaultCertIssuerErr = constable.Error("failed to issue cert")
type ClientCertIssuer interface {
Name() string
IssueClientCertPEM(username string, groups []string, ttl time.Duration) (pem *cert.PEM, err error)
IssueClientCertPEM(username string, groups []string, extras []string, ttl time.Duration) (pem *cert.PEM, err error)
}
var _ ClientCertIssuer = ClientCertIssuers{}
@@ -38,11 +38,11 @@ func (c ClientCertIssuers) Name() string {
return strings.Join(names, ",")
}
func (c ClientCertIssuers) IssueClientCertPEM(username string, groups []string, ttl time.Duration) (*cert.PEM, error) {
func (c ClientCertIssuers) IssueClientCertPEM(username string, groups []string, extras []string, ttl time.Duration) (*cert.PEM, error) {
errs := make([]error, 0, len(c))
for _, issuer := range c {
pem, err := issuer.IssueClientCertPEM(username, groups, ttl)
pem, err := issuer.IssueClientCertPEM(username, groups, extras, ttl)
if err == nil {
return pem, nil
}

View File

@@ -1,4 +1,4 @@
// Copyright 2023-2024 the Pinniped contributors. All Rights Reserved.
// Copyright 2023-2025 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package clientcertissuer
@@ -67,6 +67,11 @@ func TestName(t *testing.T) {
func TestIssueClientCertPEM(t *testing.T) {
ctrl := gomock.NewController(t)
username := "test-username"
groups := []string{"group1", "group2"}
extras := []string{"extra1=val1", "extra2=val2"}
ttl := 32 * time.Second
tests := []struct {
name string
buildIssuerMocks func() ClientCertIssuers
@@ -85,7 +90,7 @@ func TestIssueClientCertPEM(t *testing.T) {
errClientCertIssuer := mockissuer.NewMockClientCertIssuer(ctrl)
errClientCertIssuer.EXPECT().Name().Return("error cert issuer")
errClientCertIssuer.EXPECT().
IssueClientCertPEM("username", []string{"group1", "group2"}, 32*time.Second).
IssueClientCertPEM(username, groups, extras, ttl).
Return(nil, errors.New("error from wrapped cert issuer"))
return ClientCertIssuers{errClientCertIssuer}
},
@@ -96,7 +101,7 @@ func TestIssueClientCertPEM(t *testing.T) {
buildIssuerMocks: func() ClientCertIssuers {
validClientCertIssuer := mockissuer.NewMockClientCertIssuer(ctrl)
validClientCertIssuer.EXPECT().
IssueClientCertPEM("username", []string{"group1", "group2"}, 32*time.Second).
IssueClientCertPEM(username, groups, extras, ttl).
Return(&cert.PEM{CertPEM: []byte("cert"), KeyPEM: []byte("key")}, nil)
return ClientCertIssuers{validClientCertIssuer}
},
@@ -109,12 +114,12 @@ func TestIssueClientCertPEM(t *testing.T) {
errClientCertIssuer := mockissuer.NewMockClientCertIssuer(ctrl)
errClientCertIssuer.EXPECT().Name().Return("error cert issuer")
errClientCertIssuer.EXPECT().
IssueClientCertPEM("username", []string{"group1", "group2"}, 32*time.Second).
IssueClientCertPEM(username, groups, extras, ttl).
Return(nil, errors.New("error from wrapped cert issuer"))
validClientCertIssuer := mockissuer.NewMockClientCertIssuer(ctrl)
validClientCertIssuer.EXPECT().
IssueClientCertPEM("username", []string{"group1", "group2"}, 32*time.Second).
IssueClientCertPEM(username, groups, extras, ttl).
Return(&cert.PEM{CertPEM: []byte("cert"), KeyPEM: []byte("key")}, nil)
return ClientCertIssuers{
errClientCertIssuer,
@@ -130,13 +135,13 @@ func TestIssueClientCertPEM(t *testing.T) {
err1ClientCertIssuer := mockissuer.NewMockClientCertIssuer(ctrl)
err1ClientCertIssuer.EXPECT().Name().Return("error1 cert issuer")
err1ClientCertIssuer.EXPECT().
IssueClientCertPEM("username", []string{"group1", "group2"}, 32*time.Second).
IssueClientCertPEM(username, groups, extras, ttl).
Return(nil, errors.New("error1 from wrapped cert issuer"))
err2ClientCertIssuer := mockissuer.NewMockClientCertIssuer(ctrl)
err2ClientCertIssuer.EXPECT().Name().Return("error2 cert issuer")
err2ClientCertIssuer.EXPECT().
IssueClientCertPEM("username", []string{"group1", "group2"}, 32*time.Second).
IssueClientCertPEM(username, groups, extras, ttl).
Return(nil, errors.New("error2 from wrapped cert issuer"))
return ClientCertIssuers{
@@ -154,7 +159,7 @@ func TestIssueClientCertPEM(t *testing.T) {
t.Parallel()
pem, err := testcase.buildIssuerMocks().
IssueClientCertPEM("username", []string{"group1", "group2"}, 32*time.Second)
IssueClientCertPEM(username, groups, extras, ttl)
if testcase.wantErrorMessage != "" {
require.ErrorContains(t, err, testcase.wantErrorMessage)

View File

@@ -2579,7 +2579,7 @@ type clientCert struct {
func newClientCert(t *testing.T, ca *certauthority.CA, username string, groups []string) clientCert {
t.Helper()
clientCertPEM, err := ca.IssueClientCertPEM(username, groups, time.Hour)
clientCertPEM, err := ca.IssueClientCertPEM(username, groups, nil, time.Hour)
require.NoError(t, err)
return clientCert{
certPEM: clientCertPEM.CertPEM,

View File

@@ -0,0 +1,107 @@
// Copyright 2025 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package jwtcachefiller
import (
"k8s.io/apiserver/pkg/apis/apiserver"
"k8s.io/utils/ptr"
authenticationv1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/authentication/v1alpha1"
)
// convertJWTAuthenticatorSpecType converts a Pinniped CRD spec type into the very similar
// Kubernetes library apiserver.JWTAuthenticator type. It applies a default value for username and group claims,
// but is otherwise a straight conversion. The Pinniped type includes TLS configuration which does not need
// to be converted because that is applied elsewhere.
func convertJWTAuthenticatorSpecType(spec *authenticationv1alpha1.JWTAuthenticatorSpec) apiserver.JWTAuthenticator {
usernameClaim := spec.Claims.Username
if usernameClaim == "" {
usernameClaim = defaultUsernameClaim
}
groupsClaim := spec.Claims.Groups
if groupsClaim == "" {
groupsClaim = defaultGroupsClaim
}
var aud []string
if len(spec.Audience) > 0 {
aud = []string{spec.Audience}
}
jwtAuthenticator := apiserver.JWTAuthenticator{
Issuer: apiserver.Issuer{
URL: spec.Issuer,
Audiences: aud,
},
ClaimMappings: apiserver.ClaimMappings{
Username: apiserver.PrefixedClaimOrExpression{
Claim: usernameClaim,
Prefix: ptr.To(""),
},
Groups: apiserver.PrefixedClaimOrExpression{
Claim: groupsClaim,
Prefix: ptr.To(""),
},
Extra: convertExtraType(spec.Claims.Extra),
},
ClaimValidationRules: convertClaimValidationRulesType(spec.ClaimValidationRules),
UserValidationRules: convertUserValidationRules(spec.UserValidationRules),
}
return jwtAuthenticator
}
func convertUserValidationRules(rules []authenticationv1alpha1.UserValidationRule) []apiserver.UserValidationRule {
if len(rules) == 0 {
return nil
}
apiServerRules := make([]apiserver.UserValidationRule, len(rules))
for i := range rules {
apiServerRules[i] = apiserver.UserValidationRule{
Expression: rules[i].Expression,
Message: rules[i].Message,
}
}
return apiServerRules
}
func convertClaimValidationRulesType(rules []authenticationv1alpha1.ClaimValidationRule) []apiserver.ClaimValidationRule {
if len(rules) == 0 {
return nil
}
apiServerRules := make([]apiserver.ClaimValidationRule, len(rules))
for i := range rules {
apiServerRules[i] = apiserver.ClaimValidationRule{
Claim: rules[i].Claim,
RequiredValue: rules[i].RequiredValue,
Expression: rules[i].Expression,
Message: rules[i].Message,
}
}
return apiServerRules
}
func convertExtraType(extras []authenticationv1alpha1.ExtraMapping) []apiserver.ExtraMapping {
if len(extras) == 0 {
return nil
}
apiServerExtras := make([]apiserver.ExtraMapping, len(extras))
for i := range extras {
apiServerExtras[i] = apiserver.ExtraMapping{
Key: extras[i].Key,
ValueExpression: extras[i].ValueExpression,
}
}
return apiServerExtras
}

View File

@@ -0,0 +1,149 @@
// Copyright 2025 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package jwtcachefiller
import (
"testing"
"github.com/stretchr/testify/require"
"k8s.io/apiserver/pkg/apis/apiserver"
"k8s.io/utils/ptr"
authenticationv1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/authentication/v1alpha1"
)
func Test_convertJWTAuthenticatorSpecType(t *testing.T) {
t.Parallel()
tests := []struct {
name string
spec *authenticationv1alpha1.JWTAuthenticatorSpec
want apiserver.JWTAuthenticator
}{
{
name: "defaults the username and groups claims",
spec: &authenticationv1alpha1.JWTAuthenticatorSpec{
Issuer: "https://example.com",
},
want: apiserver.JWTAuthenticator{
Issuer: apiserver.Issuer{
URL: "https://example.com",
},
ClaimMappings: apiserver.ClaimMappings{
Username: apiserver.PrefixedClaimOrExpression{
Claim: "username",
Prefix: ptr.To(""),
},
Groups: apiserver.PrefixedClaimOrExpression{
Claim: "groups",
Prefix: ptr.To(""),
},
},
},
},
{
name: "converts every field except for TLS",
spec: &authenticationv1alpha1.JWTAuthenticatorSpec{
Issuer: "https://example.com",
Audience: "example-aud",
Claims: authenticationv1alpha1.JWTTokenClaims{
Username: "some-username-claim",
Groups: "some-groups-claim",
Extra: []authenticationv1alpha1.ExtraMapping{
{
Key: "key1",
ValueExpression: "expr1",
},
{
Key: "key2",
ValueExpression: "expr2",
},
},
},
ClaimValidationRules: []authenticationv1alpha1.ClaimValidationRule{
{
Claim: "claim-claim1",
RequiredValue: "claim-value1",
Expression: "claim-expr1",
Message: "claim-msg1",
},
{
Claim: "claim-claim2",
RequiredValue: "claim-value2",
Expression: "claim-expr2",
Message: "claim-msg2",
},
},
UserValidationRules: []authenticationv1alpha1.UserValidationRule{
{
Expression: "user-expr1",
Message: "user-msg1",
},
{
Expression: "user-expr2",
Message: "user-msg2",
},
},
TLS: &authenticationv1alpha1.TLSSpec{
CertificateAuthorityData: "CA bundle value - does not need to be converted",
},
},
want: apiserver.JWTAuthenticator{
Issuer: apiserver.Issuer{
URL: "https://example.com",
Audiences: []string{"example-aud"},
},
ClaimMappings: apiserver.ClaimMappings{
Username: apiserver.PrefixedClaimOrExpression{
Claim: "some-username-claim",
Prefix: ptr.To(""),
},
Groups: apiserver.PrefixedClaimOrExpression{
Claim: "some-groups-claim",
Prefix: ptr.To(""),
},
Extra: []apiserver.ExtraMapping{
{
Key: "key1",
ValueExpression: "expr1",
},
{
Key: "key2",
ValueExpression: "expr2",
},
},
},
ClaimValidationRules: []apiserver.ClaimValidationRule{
{
Claim: "claim-claim1",
RequiredValue: "claim-value1",
Expression: "claim-expr1",
Message: "claim-msg1",
},
{
Claim: "claim-claim2",
RequiredValue: "claim-value2",
Expression: "claim-expr2",
Message: "claim-msg2",
},
},
UserValidationRules: []apiserver.UserValidationRule{
{
Expression: "user-expr1",
Message: "user-msg1",
},
{
Expression: "user-expr2",
Message: "user-msg2",
},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
require.Equal(t, tt.want, convertJWTAuthenticatorSpecType(tt.spec))
})
}
}

View File

@@ -1,4 +1,4 @@
// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Package jwtcachefiller implements a controller for filling an authncache.Cache with each
@@ -23,12 +23,14 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/apiserver/pkg/apis/apiserver"
"k8s.io/apiserver/pkg/apis/apiserver/validation"
"k8s.io/apiserver/pkg/authentication/authenticator"
authenticationcel "k8s.io/apiserver/pkg/authentication/cel"
"k8s.io/apiserver/plugin/pkg/authenticator/token/oidc"
corev1informers "k8s.io/client-go/informers/core/v1"
"k8s.io/utils/clock"
"k8s.io/utils/ptr"
authenticationv1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/authentication/v1alpha1"
oidcapi "go.pinniped.dev/generated/latest/apis/supervisor/oidc"
@@ -108,11 +110,13 @@ type tokenAuthenticatorCloser interface {
type cachedJWTAuthenticator struct {
authenticator.Token
issuer string
audience string
claims authenticationv1alpha1.JWTTokenClaims
caBundleHash tlsconfigutil.CABundleHash
cancel context.CancelFunc
issuer string
audience string
claims authenticationv1alpha1.JWTTokenClaims
userValidationRules []authenticationv1alpha1.UserValidationRule
claimValidationRules []authenticationv1alpha1.ClaimValidationRule
caBundleHash tlsconfigutil.CABundleHash
cancel context.CancelFunc
}
func (c *cachedJWTAuthenticator) Close() {
@@ -353,7 +357,9 @@ func (c *jwtCacheFillerController) havePreviouslyValidated(
// If any spec field has changed, then we need a new in-memory authenticator.
if authenticatorFromCache.issuer == spec.Issuer &&
authenticatorFromCache.audience == spec.Audience &&
authenticatorFromCache.claims == spec.Claims &&
equality.Semantic.DeepEqual(authenticatorFromCache.claims, spec.Claims) &&
equality.Semantic.DeepEqual(authenticatorFromCache.userValidationRules, spec.UserValidationRules) &&
equality.Semantic.DeepEqual(authenticatorFromCache.claimValidationRules, spec.ClaimValidationRules) &&
tlsBundleOk && // if there was any error while validating the latest CA bundle, then do not consider it previously validated
authenticatorFromCache.caBundleHash.Equal(caBundleHash) {
return true, true
@@ -657,33 +663,46 @@ func (c *jwtCacheFillerController) newCachedJWTAuthenticator(
return nil, conditions, nil
}
usernameClaim := spec.Claims.Username
if usernameClaim == "" {
usernameClaim = defaultUsernameClaim
apiServerJWTAuthenticator := convertJWTAuthenticatorSpecType(spec)
// Reuse the validation code from Kubernetes structured auth config. Most importantly,
// this validates the claim mapping extras, claim validation rules, and user validation
// rules for us.
errList := validation.ValidateAuthenticationConfiguration(
authenticationcel.NewDefaultCompiler(),
&apiserver.AuthenticationConfiguration{JWT: []apiserver.JWTAuthenticator{apiServerJWTAuthenticator}},
[]string{},
)
// In addition to all the validations checked by ValidateAuthenticationConfiguration(),
// we do not want to allow equal signs in key names. This is because we want to be able to
// add the keyname to a client certificate's OU as "keyName=value".
for i, mapping := range spec.Claims.Extra {
if strings.Contains(mapping.Key, "=") {
// Use the same field path that ValidateAuthenticationConfiguration() would build, for consistency.
fieldPath := field.NewPath("jwt").Index(0).Child("claimMappings").Child("extra").Index(i)
errList = append(errList, field.Invalid(fieldPath, "", "Pinniped does not allow extra key names to contain equals sign"))
}
}
groupsClaim := spec.Claims.Groups
if groupsClaim == "" {
groupsClaim = defaultGroupsClaim
if errList != nil {
rewriteJWTAuthenticatorErrorPrefixes(errList)
errText := "could not initialize jwt authenticator"
err := errList.ToAggregate()
msg := fmt.Sprintf("%s: %s", errText, err.Error())
conditions = append(conditions, &metav1.Condition{
Type: typeAuthenticatorValid,
Status: metav1.ConditionFalse,
Reason: reasonInvalidAuthenticator,
Message: msg,
})
return nil, conditions, nil
}
cancelCtx, cancel := context.WithCancel(context.Background())
oidcAuthenticator, err := oidc.New(cancelCtx, oidc.Options{
JWTAuthenticator: apiserver.JWTAuthenticator{
Issuer: apiserver.Issuer{
URL: spec.Issuer,
Audiences: []string{spec.Audience},
},
ClaimMappings: apiserver.ClaimMappings{
Username: apiserver.PrefixedClaimOrExpression{
Claim: usernameClaim,
Prefix: ptr.To(""),
},
Groups: apiserver.PrefixedClaimOrExpression{
Claim: groupsClaim,
Prefix: ptr.To(""),
},
},
},
JWTAuthenticator: apiServerJWTAuthenticator,
KeySet: keySet,
SupportedSigningAlgs: defaultSupportedSigningAlgos(),
Client: client,
@@ -706,17 +725,37 @@ 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)
}
conditions = append(conditions, successfulAuthenticatorValidCondition())
return &cachedJWTAuthenticator{
Token: oidcAuthenticator,
issuer: spec.Issuer,
audience: spec.Audience,
claims: spec.Claims,
caBundleHash: caBundleHash,
cancel: cancel,
Token: oidcAuthenticator,
issuer: spec.Issuer,
audience: spec.Audience,
claims: spec.Claims,
userValidationRules: spec.UserValidationRules,
claimValidationRules: spec.ClaimValidationRules,
caBundleHash: caBundleHash,
cancel: cancel,
}, conditions, nil
}
// We don't have any control over the error prefixes created by ValidateAuthenticationConfiguration(), but we
// can rewrite them to make them more consistent with our JWTAuthenticator CRD field names where they don't agree.
func rewriteJWTAuthenticatorErrorPrefixes(errList field.ErrorList) {
// ValidateAuthenticationConfiguration() will prefix all our errors with "jwt[0]." because we always pass it
// exactly one jwtAuthenticator to validate.
undesirablePrefix := fmt.Sprintf("%s.", field.NewPath("jwt").Index(0).String())
for _, err := range errList {
err.Field = strings.TrimPrefix(err.Field, undesirablePrefix)
if strings.HasPrefix(err.Field, "claimMappings.") {
// Pinniped's CRD calls this field "claims". Otherwise, our field names are the same.
err.Field = strings.Replace(err.Field, "claimMappings.", "claims.", 1)
}
}
}
func (c *jwtCacheFillerController) updateStatus(
ctx context.Context,
original *authenticationv1alpha1.JWTAuthenticator,

View File

@@ -360,6 +360,79 @@ func TestController(t *testing.T) {
Groups: customGroupsClaim,
},
}
someJWTAuthenticatorSpecWithEveryOptionalValue := &authenticationv1alpha1.JWTAuthenticatorSpec{
Issuer: goodIssuer,
Audience: goodAudience,
TLS: goodOIDCIssuerServerTLSSpec,
Claims: authenticationv1alpha1.JWTTokenClaims{
Username: "my-custom-username-claim",
Groups: customGroupsClaim,
Extra: []authenticationv1alpha1.ExtraMapping{
{
Key: "example.com/key-name", // must be a domain and path
ValueExpression: `"extra-value"`, // needs to be a valid CEL expression that returns a string or list of strings
},
},
},
ClaimValidationRules: []authenticationv1alpha1.ClaimValidationRule{
{
Claim: "iss", // note: can't specify this and Expression/Message at the same time
RequiredValue: goodIssuer,
},
},
UserValidationRules: []authenticationv1alpha1.UserValidationRule{
{
Expression: "true", // must be a valid CEL expression that returns a boolean
Message: "user-msg1",
},
},
}
invalidClaimsExtraJWTAuthenticatorSpec := &authenticationv1alpha1.JWTAuthenticatorSpec{
Issuer: goodIssuer,
Audience: goodAudience,
TLS: goodOIDCIssuerServerTLSSpec,
Claims: authenticationv1alpha1.JWTTokenClaims{
Extra: []authenticationv1alpha1.ExtraMapping{
{
Key: "this-is-an-invalid-key", // this should cause a validation error in the Kubernetes library validator
ValueExpression: `"extra-value"`,
},
},
},
}
invalidClaimValidationRulesJWTAuthenticatorSpec := &authenticationv1alpha1.JWTAuthenticatorSpec{
Issuer: goodIssuer,
Audience: goodAudience,
TLS: goodOIDCIssuerServerTLSSpec,
ClaimValidationRules: []authenticationv1alpha1.ClaimValidationRule{
{
Expression: "this is an invalid cel expression",
},
},
}
invalidUserValidationRulesJWTAuthenticatorSpec := &authenticationv1alpha1.JWTAuthenticatorSpec{
Issuer: goodIssuer,
Audience: goodAudience,
TLS: goodOIDCIssuerServerTLSSpec,
UserValidationRules: []authenticationv1alpha1.UserValidationRule{
{
Expression: "this is an invalid cel expression",
},
},
}
invalidClaimsExtraContainsEqualSignJWTAuthenticatorSpec := &authenticationv1alpha1.JWTAuthenticatorSpec{
Issuer: goodIssuer,
Audience: goodAudience,
TLS: goodOIDCIssuerServerTLSSpec,
Claims: authenticationv1alpha1.JWTTokenClaims{
Extra: []authenticationv1alpha1.ExtraMapping{
{
Key: "example.com/key=contains-equals-sign", // this should cause a validation error in our own code
ValueExpression: `"extra-value"`,
},
},
},
}
otherJWTAuthenticatorSpec := &authenticationv1alpha1.JWTAuthenticatorSpec{
Issuer: someOtherIssuer,
Audience: goodAudience,
@@ -549,6 +622,16 @@ func TestController(t *testing.T) {
Message: "unable to validate; see other conditions for details",
}
}
sadAuthenticatorValid := func(msg string, time metav1.Time, observedGeneration int64) metav1.Condition {
return metav1.Condition{
Type: "AuthenticatorValid",
Status: "False",
ObservedGeneration: observedGeneration,
LastTransitionTime: time,
Reason: "InvalidAuthenticator",
Message: msg,
}
}
// NOTE: we can't reach this error the way our code is written.
// We check many things and fail early, resulting in an "Unknown" Authenticator status.
// The only possible fail for the Authenticator itself would require us to allow more
@@ -717,6 +800,7 @@ func TestController(t *testing.T) {
wantActions func() []coretesting.Action
wantUsernameClaim string
wantGroupsClaim string
wantExtras map[string][]string
wantNamesOfJWTAuthenticatorsInCache []string
skipTestingCachedAuthenticator bool
}{
@@ -1108,6 +1192,43 @@ func TestController(t *testing.T) {
wantGroupsClaim: someJWTAuthenticatorSpecWithGroupsClaim.Claims.Groups,
wantNamesOfJWTAuthenticatorsInCache: []string{"test-name"},
},
{
name: "Sync: JWTAuthenticator with every optional value: loop will complete successfully and update status conditions",
jwtAuthenticators: []runtime.Object{
&authenticationv1alpha1.JWTAuthenticator{
ObjectMeta: metav1.ObjectMeta{
Name: "test-name",
},
Spec: *someJWTAuthenticatorSpecWithEveryOptionalValue,
},
},
wantLogLines: []string{
fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:<line>$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:<line>$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{
Name: "test-name",
},
Spec: *someJWTAuthenticatorSpecWithEveryOptionalValue,
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{Watch: true}),
updateStatusAction,
}
},
wantUsernameClaim: "my-custom-username-claim",
wantGroupsClaim: someJWTAuthenticatorSpecWithGroupsClaim.Claims.Groups,
wantExtras: map[string][]string{"example.com/key-name": {"extra-value"}},
wantNamesOfJWTAuthenticatorsInCache: []string{"test-name"},
},
{
name: "Sync: JWTAuthenticator with new spec.tls 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) {
@@ -1302,6 +1423,114 @@ func TestController(t *testing.T) {
wantNamesOfJWTAuthenticatorsInCache: []string{"test-name"},
wantClose: true,
},
{
name: "Sync: JWTAuthenticator with new spec.userValidationRules field: 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(someJWTAuthenticatorSpec.TLS.CertificateAuthorityData)
require.NoError(t, err)
cacheValue := newCacheValue(t, *someJWTAuthenticatorSpec, string(oldCA), wantClose)
cacheValue.userValidationRules = []authenticationv1alpha1.UserValidationRule{
{
Expression: "true",
Message: "some old rule",
},
}
cache.Store(
authncache.Key{
Name: "test-name",
Kind: "JWTAuthenticator",
APIGroup: authenticationv1alpha1.SchemeGroupVersion.Group,
},
cacheValue,
)
},
jwtAuthenticators: []runtime.Object{
&authenticationv1alpha1.JWTAuthenticator{
ObjectMeta: metav1.ObjectMeta{
Name: "test-name",
},
Spec: *someJWTAuthenticatorSpec,
},
},
wantLogLines: []string{
fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:<line>$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:<line>$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{
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{Watch: true}),
updateStatusAction,
}
},
wantNamesOfJWTAuthenticatorsInCache: []string{"test-name"},
wantClose: true,
},
{
name: "Sync: JWTAuthenticator with new spec.claimValidationRules field: 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(someJWTAuthenticatorSpec.TLS.CertificateAuthorityData)
require.NoError(t, err)
cacheValue := newCacheValue(t, *someJWTAuthenticatorSpec, string(oldCA), wantClose)
cacheValue.claimValidationRules = []authenticationv1alpha1.ClaimValidationRule{
{
Claim: "iss",
RequiredValue: "some old value",
},
}
cache.Store(
authncache.Key{
Name: "test-name",
Kind: "JWTAuthenticator",
APIGroup: authenticationv1alpha1.SchemeGroupVersion.Group,
},
cacheValue,
)
},
jwtAuthenticators: []runtime.Object{
&authenticationv1alpha1.JWTAuthenticator{
ObjectMeta: metav1.ObjectMeta{
Name: "test-name",
},
Spec: *someJWTAuthenticatorSpec,
},
},
wantLogLines: []string{
fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:<line>$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:<line>$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{
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{Watch: true}),
updateStatusAction,
}
},
wantNamesOfJWTAuthenticatorsInCache: []string{"test-name"},
wantClose: true,
},
{
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) {
@@ -2280,6 +2509,196 @@ func TestController(t *testing.T) {
},
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: "newCachedJWTAuthenticator: validateAuthenticationConfiguration: for any error in claims.extra: loop will fail sync, 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{
Name: "test-name",
},
Spec: *invalidClaimsExtraJWTAuthenticatorSpec,
},
},
wantLogLines: []string{
fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:<line>$jwtcachefiller.(*jwtCacheFillerController).syncIndividualJWTAuthenticator","message":"invalid jwt authenticator","jwtAuthenticator":"test-name","issuer":"%s","removedFromCache":false}`, invalidClaimsExtraJWTAuthenticatorSpec.Issuer),
fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:<line>$jwtcachefiller.(*jwtCacheFillerController).updateStatus","message":"jwtauthenticator status successfully updated","jwtAuthenticator":"test-name","issuer":"%s","phase":"Error"}`, invalidClaimsExtraJWTAuthenticatorSpec.Issuer),
},
wantActions: func() []coretesting.Action {
updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{
ObjectMeta: metav1.ObjectMeta{
Name: "test-name",
},
Spec: *invalidClaimsExtraJWTAuthenticatorSpec,
Status: authenticationv1alpha1.JWTAuthenticatorStatus{
Conditions: conditionstestutil.Replace(
allHappyConditionsSuccess(goodIssuer, frozenMetav1Now, 0),
[]metav1.Condition{
sadReadyCondition(frozenMetav1Now, 0),
happyIssuerURLValid(frozenMetav1Now, 0),
happyDiscoveryURLValid(frozenMetav1Now, 0),
sadAuthenticatorValid(
`could not initialize jwt authenticator: claims.extra[0].key: Invalid value: "this-is-an-invalid-key": must be a domain-prefixed path (such as "acme.io/foo")`,
frozenMetav1Now,
0,
),
happyJWKSURLValid(frozenMetav1Now, 0),
happyJWKSFetch(frozenMetav1Now, 0),
},
),
Phase: "Error",
},
})
updateStatusAction.Subresource = "status"
return []coretesting.Action{
coretesting.NewListAction(jwtAuthenticatorsGVR, jwtAUthenticatorGVK, "", metav1.ListOptions{}),
coretesting.NewWatchAction(jwtAuthenticatorsGVR, "", metav1.ListOptions{Watch: true}),
updateStatusAction,
}
},
},
{
name: "newCachedJWTAuthenticator: validateAuthenticationConfiguration: for any error in claimValidationRules: loop will fail sync, 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{
Name: "test-name",
},
Spec: *invalidClaimValidationRulesJWTAuthenticatorSpec,
},
},
wantLogLines: []string{
fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:<line>$jwtcachefiller.(*jwtCacheFillerController).syncIndividualJWTAuthenticator","message":"invalid jwt authenticator","jwtAuthenticator":"test-name","issuer":"%s","removedFromCache":false}`, invalidClaimValidationRulesJWTAuthenticatorSpec.Issuer),
fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:<line>$jwtcachefiller.(*jwtCacheFillerController).updateStatus","message":"jwtauthenticator status successfully updated","jwtAuthenticator":"test-name","issuer":"%s","phase":"Error"}`, invalidClaimValidationRulesJWTAuthenticatorSpec.Issuer),
},
wantActions: func() []coretesting.Action {
updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{
ObjectMeta: metav1.ObjectMeta{
Name: "test-name",
},
Spec: *invalidClaimValidationRulesJWTAuthenticatorSpec,
Status: authenticationv1alpha1.JWTAuthenticatorStatus{
Conditions: conditionstestutil.Replace(
allHappyConditionsSuccess(goodIssuer, frozenMetav1Now, 0),
[]metav1.Condition{
sadReadyCondition(frozenMetav1Now, 0),
happyIssuerURLValid(frozenMetav1Now, 0),
happyDiscoveryURLValid(frozenMetav1Now, 0),
sadAuthenticatorValid(
"could not initialize jwt authenticator: claimValidationRules[0].expression: Invalid value: \"this is an invalid cel expression\": "+
"compilation failed: ERROR: <input>:1:6: Syntax error: mismatched input 'is' expecting <EOF>\n | this is an invalid cel expression\n | .....^",
frozenMetav1Now,
0,
),
happyJWKSURLValid(frozenMetav1Now, 0),
happyJWKSFetch(frozenMetav1Now, 0),
},
),
Phase: "Error",
},
})
updateStatusAction.Subresource = "status"
return []coretesting.Action{
coretesting.NewListAction(jwtAuthenticatorsGVR, jwtAUthenticatorGVK, "", metav1.ListOptions{}),
coretesting.NewWatchAction(jwtAuthenticatorsGVR, "", metav1.ListOptions{Watch: true}),
updateStatusAction,
}
},
},
{
name: "newCachedJWTAuthenticator: validateAuthenticationConfiguration: for any error in userValidationRules: loop will fail sync, 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{
Name: "test-name",
},
Spec: *invalidUserValidationRulesJWTAuthenticatorSpec,
},
},
wantLogLines: []string{
fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:<line>$jwtcachefiller.(*jwtCacheFillerController).syncIndividualJWTAuthenticator","message":"invalid jwt authenticator","jwtAuthenticator":"test-name","issuer":"%s","removedFromCache":false}`, invalidUserValidationRulesJWTAuthenticatorSpec.Issuer),
fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:<line>$jwtcachefiller.(*jwtCacheFillerController).updateStatus","message":"jwtauthenticator status successfully updated","jwtAuthenticator":"test-name","issuer":"%s","phase":"Error"}`, invalidUserValidationRulesJWTAuthenticatorSpec.Issuer),
},
wantActions: func() []coretesting.Action {
updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{
ObjectMeta: metav1.ObjectMeta{
Name: "test-name",
},
Spec: *invalidUserValidationRulesJWTAuthenticatorSpec,
Status: authenticationv1alpha1.JWTAuthenticatorStatus{
Conditions: conditionstestutil.Replace(
allHappyConditionsSuccess(goodIssuer, frozenMetav1Now, 0),
[]metav1.Condition{
sadReadyCondition(frozenMetav1Now, 0),
happyIssuerURLValid(frozenMetav1Now, 0),
happyDiscoveryURLValid(frozenMetav1Now, 0),
sadAuthenticatorValid(
"could not initialize jwt authenticator: userValidationRules[0].expression: Invalid value: \"this is an invalid cel expression\": "+
"compilation failed: ERROR: <input>:1:6: Syntax error: mismatched input 'is' expecting <EOF>\n | this is an invalid cel expression\n | .....^",
frozenMetav1Now,
0,
),
happyJWKSURLValid(frozenMetav1Now, 0),
happyJWKSFetch(frozenMetav1Now, 0),
},
),
Phase: "Error",
},
})
updateStatusAction.Subresource = "status"
return []coretesting.Action{
coretesting.NewListAction(jwtAuthenticatorsGVR, jwtAUthenticatorGVK, "", metav1.ListOptions{}),
coretesting.NewWatchAction(jwtAuthenticatorsGVR, "", metav1.ListOptions{Watch: true}),
updateStatusAction,
}
},
},
{
name: "newCachedJWTAuthenticator: when any claims.extra[].key contains an equals sign: loop will fail sync, 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{
Name: "test-name",
},
Spec: *invalidClaimsExtraContainsEqualSignJWTAuthenticatorSpec,
},
},
wantLogLines: []string{
fmt.Sprintf(`{"level":"info","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:<line>$jwtcachefiller.(*jwtCacheFillerController).syncIndividualJWTAuthenticator","message":"invalid jwt authenticator","jwtAuthenticator":"test-name","issuer":"%s","removedFromCache":false}`, invalidClaimsExtraContainsEqualSignJWTAuthenticatorSpec.Issuer),
fmt.Sprintf(`{"level":"debug","timestamp":"2099-08-08T13:57:36.123456Z","logger":"jwtcachefiller-controller","caller":"jwtcachefiller/jwtcachefiller.go:<line>$jwtcachefiller.(*jwtCacheFillerController).updateStatus","message":"jwtauthenticator status successfully updated","jwtAuthenticator":"test-name","issuer":"%s","phase":"Error"}`, invalidClaimsExtraContainsEqualSignJWTAuthenticatorSpec.Issuer),
},
wantActions: func() []coretesting.Action {
updateStatusAction := coretesting.NewUpdateAction(jwtAuthenticatorsGVR, "", &authenticationv1alpha1.JWTAuthenticator{
ObjectMeta: metav1.ObjectMeta{
Name: "test-name",
},
Spec: *invalidClaimsExtraContainsEqualSignJWTAuthenticatorSpec,
Status: authenticationv1alpha1.JWTAuthenticatorStatus{
Conditions: conditionstestutil.Replace(
allHappyConditionsSuccess(goodIssuer, frozenMetav1Now, 0),
[]metav1.Condition{
sadReadyCondition(frozenMetav1Now, 0),
happyIssuerURLValid(frozenMetav1Now, 0),
happyDiscoveryURLValid(frozenMetav1Now, 0),
sadAuthenticatorValid(
`could not initialize jwt authenticator: claims.extra[0]: Invalid value: "": Pinniped does not allow extra key names to contain equals sign`,
frozenMetav1Now,
0,
),
happyJWKSURLValid(frozenMetav1Now, 0),
happyJWKSFetch(frozenMetav1Now, 0),
},
),
Phase: "Error",
},
})
updateStatusAction.Subresource = "status"
return []coretesting.Action{
coretesting.NewListAction(jwtAuthenticatorsGVR, jwtAUthenticatorGVK, "", metav1.ListOptions{}),
coretesting.NewWatchAction(jwtAuthenticatorsGVR, "", metav1.ListOptions{Watch: true}),
updateStatusAction,
}
},
},
{
name: "updateStatus: called with matching original and updated conditions: will not make request to update conditions",
jwtAuthenticators: []runtime.Object{
@@ -2512,6 +2931,7 @@ func TestController(t *testing.T) {
goodUsername,
tt.wantUsernameClaim,
tt.wantGroupsClaim,
tt.wantExtras,
goodIssuer,
) {
t.Run(test.name, func(t *testing.T) {
@@ -2593,6 +3013,7 @@ func testTableForAuthenticateTokenTests(
goodUsername string,
expectedUsernameClaim string,
expectedGroupsClaim string,
expectedExtras map[string][]string,
issuer string,
) []struct {
name string
@@ -2616,7 +3037,8 @@ func testTableForAuthenticateTokenTests(
name: "good token without groups and with EC signature",
wantResponse: &authenticator.Response{
User: &user.DefaultInfo{
Name: goodUsername,
Name: goodUsername,
Extra: expectedExtras,
},
},
wantAuthenticated: true,
@@ -2630,7 +3052,8 @@ func testTableForAuthenticateTokenTests(
},
wantResponse: &authenticator.Response{
User: &user.DefaultInfo{
Name: goodUsername,
Name: goodUsername,
Extra: expectedExtras,
},
},
wantAuthenticated: true,
@@ -2644,6 +3067,7 @@ func testTableForAuthenticateTokenTests(
User: &user.DefaultInfo{
Name: goodUsername,
Groups: []string{group0, group1},
Extra: expectedExtras,
},
},
wantAuthenticated: true,
@@ -2657,6 +3081,7 @@ func testTableForAuthenticateTokenTests(
User: &user.DefaultInfo{
Name: goodUsername,
Groups: []string{"some-distributed-group-1", "some-distributed-group-2"},
Extra: expectedExtras,
},
},
wantAuthenticated: true,
@@ -2684,6 +3109,7 @@ func testTableForAuthenticateTokenTests(
User: &user.DefaultInfo{
Name: goodUsername,
Groups: []string{group0},
Extra: expectedExtras,
},
},
wantAuthenticated: true,
@@ -2695,7 +3121,8 @@ func testTableForAuthenticateTokenTests(
},
wantResponse: &authenticator.Response{
User: &user.DefaultInfo{
Name: goodUsername,
Name: goodUsername,
Extra: expectedExtras,
},
},
wantAuthenticated: true,
@@ -2828,10 +3255,12 @@ func newCacheValue(t *testing.T, spec authenticationv1alpha1.JWTAuthenticatorSpe
})
return &cachedJWTAuthenticator{
issuer: spec.Issuer,
audience: spec.Audience,
claims: spec.Claims,
caBundleHash: tlsconfigutil.NewCABundleHash([]byte(caBundle)),
issuer: spec.Issuer,
audience: spec.Audience,
claims: spec.Claims,
userValidationRules: spec.UserValidationRules,
claimValidationRules: spec.ClaimValidationRules,
caBundleHash: tlsconfigutil.NewCABundleHash([]byte(caBundle)),
cancel: func() {
wasClosed = true
},

View File

@@ -1,4 +1,4 @@
// Copyright 2021-2024 the Pinniped contributors. All Rights Reserved.
// Copyright 2021-2025 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package impersonatorconfig
@@ -1145,7 +1145,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
mTLSClientCertCAPrivateKeyPEM, err = mTLSClientCertCA.PrivateKeyToPEM()
r.NoError(err)
mTLSClientCertCASecret = newSigningKeySecret(mTLSClientCertCASecretName, mTLSClientCertCACertPEM, mTLSClientCertCAPrivateKeyPEM)
validClientCert, err = mTLSClientCertCA.IssueClientCert("username", nil, time.Hour)
validClientCert, err = mTLSClientCertCA.IssueClientCert("username", nil, nil, time.Hour)
r.NoError(err)
externalCA = newCA()

View File

@@ -1,4 +1,4 @@
// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
//

View File

@@ -1,4 +1,4 @@
// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
//

View File

@@ -1,4 +1,4 @@
// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
//

View File

@@ -1,4 +1,4 @@
// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
//
@@ -46,18 +46,18 @@ func (m *MockClientCertIssuer) EXPECT() *MockClientCertIssuerMockRecorder {
}
// IssueClientCertPEM mocks base method.
func (m *MockClientCertIssuer) IssueClientCertPEM(username string, groups []string, ttl time.Duration) (*cert.PEM, error) {
func (m *MockClientCertIssuer) IssueClientCertPEM(username string, groups, extras []string, ttl time.Duration) (*cert.PEM, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "IssueClientCertPEM", username, groups, ttl)
ret := m.ctrl.Call(m, "IssueClientCertPEM", username, groups, extras, ttl)
ret0, _ := ret[0].(*cert.PEM)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// IssueClientCertPEM indicates an expected call of IssueClientCertPEM.
func (mr *MockClientCertIssuerMockRecorder) IssueClientCertPEM(username, groups, ttl any) *gomock.Call {
func (mr *MockClientCertIssuerMockRecorder) IssueClientCertPEM(username, groups, extras, ttl any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IssueClientCertPEM", reflect.TypeOf((*MockClientCertIssuer)(nil).IssueClientCertPEM), username, groups, ttl)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IssueClientCertPEM", reflect.TypeOf((*MockClientCertIssuer)(nil).IssueClientCertPEM), username, groups, extras, ttl)
}
// Name mocks base method.

View File

@@ -1,4 +1,4 @@
// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
//

View File

@@ -1,4 +1,4 @@
// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
//

View File

@@ -1,4 +1,4 @@
// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
//

View File

@@ -1,4 +1,4 @@
// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
//

View File

@@ -1,4 +1,4 @@
// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
//

View File

@@ -1,4 +1,4 @@
// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
//

View File

@@ -1,4 +1,4 @@
// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
//

View File

@@ -1,4 +1,4 @@
// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
//

View File

@@ -9,6 +9,9 @@ import (
"crypto/sha256"
"errors"
"fmt"
"slices"
"sort"
"strings"
"time"
apierrors "k8s.io/apimachinery/pkg/api/errors"
@@ -16,6 +19,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
utilvalidation "k8s.io/apimachinery/pkg/util/validation"
"k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/apiserver/pkg/authentication/user"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
@@ -158,7 +162,12 @@ func (r *REST) Create(ctx context.Context, obj runtime.Object, createValidation
return authenticationFailedResponse(), nil
}
pem, err := r.issuer.IssueClientCertPEM(userInfo.GetName(), userInfo.GetGroups(), clientCertificateTTL)
pem, err := r.issuer.IssueClientCertPEM(
userInfo.GetName(),
userInfo.GetGroups(),
extrasAsKeyValues(userInfo.GetExtra()),
clientCertificateTTL,
)
if err != nil {
r.auditLogger.Audit(auditevent.TokenCredentialRequestUnexpectedError, &plog.AuditParams{
ReqCtx: ctx,
@@ -179,6 +188,7 @@ func (r *REST) Create(ctx context.Context, obj runtime.Object, createValidation
PIIKeysAndValues: []any{
"username", userInfo.GetName(),
"groups", userInfo.GetGroups(),
"extras", userInfo.GetExtra(),
},
KeysAndValues: []any{
"issuedClientCert", map[string]string{
@@ -200,6 +210,18 @@ func (r *REST) Create(ctx context.Context, obj runtime.Object, createValidation
}, nil
}
func extrasAsKeyValues(extras map[string][]string) []string {
var kvExtras []string
for k, v := range extras {
for _, vv := range v {
// Note that this will result in a key getting repeated if it has multiple values.
kvExtras = append(kvExtras, fmt.Sprintf("%s=%s", k, vv))
}
}
slices.Sort(kvExtras)
return kvExtras
}
func validateRequest(ctx context.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, options *metav1.CreateOptions) (*loginapi.TokenCredentialRequest, error) {
credentialRequest, ok := obj.(*loginapi.TokenCredentialRequest)
if !ok {
@@ -245,23 +267,81 @@ func validateUserInfo(userInfo user.Info) error {
return errors.New("UIDs are not supported")
}
// certs cannot assert extras, but starting in K8s 1.32 the authenticator will always provide this information
if len(userInfo.GetExtra()) == 0 { // it's ok for this to be empty...
return nil
allErrs := validateExtraKeys(userInfo.GetExtra())
if allErrs != nil {
return fmt.Errorf("authenticator returned illegal userInfo extra key(s): %w", allErrs.ToAggregate())
}
// ... but if it's not empty, should have only exactly this one key.
if len(userInfo.GetExtra()) > 1 {
return errors.New("extra may have only one key 'authentication.kubernetes.io/credential-id'")
}
_, ok := userInfo.GetExtra()["authentication.kubernetes.io/credential-id"]
if !ok {
return errors.New("extra may have only one key 'authentication.kubernetes.io/credential-id'")
}
return nil
}
func validateExtraKeys(extras map[string][]string) field.ErrorList {
// Prevent WebhookAuthenticators from returning illegal extras.
//
// JWTAuthenticators are already effectively prevented from returning illegal extras because we validate
// the extra key names that are configured on the JWTAuthenticator CRD, but it shouldn't hurt to check again
// here for JWTAuthenticators too.
//
// These validations are inspired by those done in k8s.io/apiserver@v0.33.2/pkg/apis/apiserver/validation/validation.go.
//
// Keys must be a domain-prefix path (e.g. example.org/foo).
// All characters before the first "/" must be a valid subdomain as defined by RFC 1123.
// All characters trailing the first "/" must be valid HTTP Path characters as defined by RFC 3986.
// k8s.io, kubernetes.io and their subdomains are reserved for Kubernetes use and cannot be used.
// Keys must be lowercase.
var allErrs field.ErrorList
// Sort the keys for stable order of error messages.
keys := make([]string, 0, len(extras))
for k := range extras {
keys = append(keys, k)
}
sort.Strings(keys)
for _, extraKey := range keys {
path := field.NewPath(fmt.Sprintf("userInfo extra key %q", extraKey))
// This is a special key that is always added by authenticators starting in K8s 1.32, so always allow it.
if extraKey == "authentication.kubernetes.io/credential-id" {
continue
}
// Noe that IsDomainPrefixedPath also checks for empty keys.
allErrs = append(allErrs, utilvalidation.IsDomainPrefixedPath(path, extraKey)...)
// Cannot use reserved prefixes.
if isKubernetesDomainPrefix(extraKey) {
allErrs = append(allErrs, field.Invalid(path, extraKey, "k8s.io, kubernetes.io and their subdomains are reserved for Kubernetes use"))
}
// We can't allow equals signs in the key name, because we need to be able to encode the key names and values
// into the client cert as OU "keyName=value".
if strings.Contains(extraKey, "=") {
allErrs = append(allErrs, field.Invalid(path, extraKey, "Pinniped does not allow extra key names to contain equals sign"))
}
}
return allErrs
}
func isKubernetesDomainPrefix(key string) bool {
domainPrefix := getDomainPrefix(key)
if domainPrefix == "kubernetes.io" || strings.HasSuffix(domainPrefix, ".kubernetes.io") {
return true
}
if domainPrefix == "k8s.io" || strings.HasSuffix(domainPrefix, ".k8s.io") {
return true
}
return false
}
func getDomainPrefix(key string) string {
if parts := strings.SplitN(key, "/", 2); len(parts) == 2 {
return parts[0]
}
return ""
}
func authenticationFailedResponse() *loginapi.TokenCredentialRequest {
m := "authentication failed"
return &loginapi.TokenCredentialRequest{

View File

@@ -92,7 +92,7 @@ func TestCreate(t *testing.T) {
ctrl.Finish()
})
it("CreateSucceedsWhenGivenATokenAndTheWebhookAuthenticatesTheToken", func() {
it("CreateSucceedsWhenGivenATokenAndTheAuthenticatorAuthenticatesTheToken", func() {
req := validCredentialRequest()
requestAuthenticator := mockcredentialrequest.NewMockTokenCredentialRequestAuthenticator(ctrl)
@@ -106,6 +106,7 @@ func TestCreate(t *testing.T) {
clientCertIssuer.EXPECT().IssueClientCertPEM(
"test-user",
[]string{"test-group-1", "test-group-2"},
nil,
5*time.Minute,
).Return(&cert.PEM{
CertPEM: []byte("test-cert"),
@@ -150,6 +151,7 @@ func TestCreate(t *testing.T) {
"personalInfo": map[string]any{
"username": "test-user",
"groups": []any{"test-group-1", "test-group-2"},
"extras": map[string]any{},
},
}),
}
@@ -167,7 +169,7 @@ func TestCreate(t *testing.T) {
clientCertIssuer := mockissuer.NewMockClientCertIssuer(ctrl)
clientCertIssuer.EXPECT().
IssueClientCertPEM(gomock.Any(), gomock.Any(), gomock.Any()).
IssueClientCertPEM(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).
Return(nil, fmt.Errorf("some certificate authority error"))
storage := NewREST(requestAuthenticator, clientCertIssuer, schema.GroupResource{}, auditLogger)
@@ -193,7 +195,7 @@ func TestCreate(t *testing.T) {
}
})
it("CreateSucceedsWithAnUnauthenticatedStatusWhenGivenATokenAndTheWebhookReturnsNilUser", func() {
it("CreateSucceedsWithAnUnauthenticatedStatusWhenGivenATokenAndTheAuthenticatorReturnsNilUser", func() {
req := validCredentialRequest()
requestAuthenticator := mockcredentialrequest.NewMockTokenCredentialRequestAuthenticator(ctrl)
@@ -222,12 +224,12 @@ func TestCreate(t *testing.T) {
}
})
it("CreateSucceedsWithAnUnauthenticatedStatusWhenWebhookFails", func() {
it("CreateSucceedsWithAnUnauthenticatedStatusWhenAuthenticatorFails", func() {
req := validCredentialRequest()
requestAuthenticator := mockcredentialrequest.NewMockTokenCredentialRequestAuthenticator(ctrl)
requestAuthenticator.EXPECT().AuthenticateTokenCredentialRequest(gomock.Any(), req).
Return(nil, errors.New("some webhook error"))
Return(nil, errors.New("some authentication error"))
storage := NewREST(requestAuthenticator, nil, schema.GroupResource{}, auditLogger)
@@ -248,12 +250,12 @@ func TestCreate(t *testing.T) {
"name": "fake-authenticator-name",
},
"reason": "authenticator returned an error",
"err": "some webhook error",
"err": "some authentication error",
}),
}
})
it("CreateSucceedsWithAnUnauthenticatedStatusWhenWebhookReturnsAnEmptyUsername", func() {
it("CreateSucceedsWithAnUnauthenticatedStatusWhenAuthenticatorReturnsAnEmptyUsername", func() {
req := validCredentialRequest()
requestAuthenticator := mockcredentialrequest.NewMockTokenCredentialRequestAuthenticator(ctrl)
@@ -289,7 +291,7 @@ func TestCreate(t *testing.T) {
}
})
it("CreateSucceedsWithAnUnauthenticatedStatusWhenWebhookReturnsAUserWithUID", func() {
it("CreateSucceedsWithAnUnauthenticatedStatusWhenAuthenticatorReturnsAUserWithUID", func() {
req := validCredentialRequest()
requestAuthenticator := mockcredentialrequest.NewMockTokenCredentialRequestAuthenticator(ctrl)
@@ -329,47 +331,7 @@ func TestCreate(t *testing.T) {
}
})
it("CreateSucceedsWithAnUnauthenticatedStatusWhenWebhookReturnsAUserWithExtra", func() {
req := validCredentialRequest()
requestAuthenticator := mockcredentialrequest.NewMockTokenCredentialRequestAuthenticator(ctrl)
requestAuthenticator.EXPECT().AuthenticateTokenCredentialRequest(gomock.Any(), req).
Return(&user.DefaultInfo{
Name: "test-user",
Groups: []string{"test-group-1", "test-group-2"},
Extra: map[string][]string{"test-key": {"test-val-1", "test-val-2"}},
}, nil)
storage := NewREST(requestAuthenticator, nil, schema.GroupResource{}, auditLogger)
response, err := callCreate(storage, req)
requireSuccessfulResponseWithAuthenticationFailureMessage(t, err, response)
wantAuditLog = []testutil.WantedAuditLog{
testutil.WantAuditLog("TokenCredentialRequest Token Received", map[string]any{
"auditID": "fake-audit-id",
"tokenID": tokenToHash(req.Spec.Token),
}),
testutil.WantAuditLog("TokenCredentialRequest Unsupported UserInfo", map[string]any{
"auditID": "fake-audit-id",
"authenticator": map[string]any{
"apiGroup": "fake-api-group.com",
"kind": "FakeAuthenticatorKind",
"name": "fake-authenticator-name",
},
"reason": "unsupported value in userInfo returned by authenticator",
"err": "extra may have only one key 'authentication.kubernetes.io/credential-id'",
"userInfoExtrasCount": float64(1),
"personalInfo": map[string]any{
"userInfoName": "test-user",
"userInfoUID": "",
},
}),
}
})
it("CreateSucceedsWithAnUnauthenticatedStatusWhenWebhookReturnsAUserWithTooManyExtra", func() {
it("CreateSucceedsWhenAuthenticatorReturnsAUserWithExtras", func() {
req := validCredentialRequest()
requestAuthenticator := mockcredentialrequest.NewMockTokenCredentialRequestAuthenticator(ctrl)
@@ -378,55 +340,22 @@ func TestCreate(t *testing.T) {
Name: "test-user",
Groups: []string{"test-group-1", "test-group-2"},
Extra: map[string][]string{
"test-key": {"test-val-1", "test-val-2"},
"authentication.kubernetes.io/credential-id": {"some-value"},
"authentication.kubernetes.io/credential-id": {"test-val-1", "test-val-2"},
"example.com/extra1": {"test-val-a"},
"example.com/extra2": {"test-val-b"},
},
}, nil)
storage := NewREST(requestAuthenticator, nil, schema.GroupResource{}, auditLogger)
response, err := callCreate(storage, req)
requireSuccessfulResponseWithAuthenticationFailureMessage(t, err, response)
wantAuditLog = []testutil.WantedAuditLog{
testutil.WantAuditLog("TokenCredentialRequest Token Received", map[string]any{
"auditID": "fake-audit-id",
"tokenID": tokenToHash(req.Spec.Token),
}),
testutil.WantAuditLog("TokenCredentialRequest Unsupported UserInfo", map[string]any{
"auditID": "fake-audit-id",
"authenticator": map[string]any{
"apiGroup": "fake-api-group.com",
"kind": "FakeAuthenticatorKind",
"name": "fake-authenticator-name",
},
"reason": "unsupported value in userInfo returned by authenticator",
"err": "extra may have only one key 'authentication.kubernetes.io/credential-id'",
"userInfoExtrasCount": float64(2),
"personalInfo": map[string]any{
"userInfoName": "test-user",
"userInfoUID": "",
},
}),
}
})
it("CreateSucceedsWhenWebhookReturnsAUserWithValidExtra", func() {
req := validCredentialRequest()
requestAuthenticator := mockcredentialrequest.NewMockTokenCredentialRequestAuthenticator(ctrl)
requestAuthenticator.EXPECT().AuthenticateTokenCredentialRequest(gomock.Any(), req).
Return(&user.DefaultInfo{
Name: "test-user",
Groups: []string{"test-group-1", "test-group-2"},
Extra: map[string][]string{"authentication.kubernetes.io/credential-id": {"test-val-1", "test-val-2"}},
}, nil)
clientCertIssuer := mockissuer.NewMockClientCertIssuer(ctrl)
clientCertIssuer.EXPECT().IssueClientCertPEM(
"test-user",
[]string{"test-group-1", "test-group-2"},
[]string{
"authentication.kubernetes.io/credential-id=test-val-1",
"authentication.kubernetes.io/credential-id=test-val-2",
"example.com/extra1=test-val-a",
"example.com/extra2=test-val-b",
},
5*time.Minute,
).Return(&cert.PEM{
CertPEM: []byte("test-cert"),
@@ -471,6 +400,51 @@ func TestCreate(t *testing.T) {
"personalInfo": map[string]any{
"username": "test-user",
"groups": []any{"test-group-1", "test-group-2"},
"extras": map[string]any{
"authentication.kubernetes.io/credential-id": []any{"test-val-1", "test-val-2"},
"example.com/extra1": []any{"test-val-a"},
"example.com/extra2": []any{"test-val-b"},
},
},
}),
}
})
it("CreateSucceedsWithAnUnauthenticatedStatusWhenWebhookReturnsAUserWithInvalidExtraKey", func() {
req := validCredentialRequest()
requestAuthenticator := mockcredentialrequest.NewMockTokenCredentialRequestAuthenticator(ctrl)
requestAuthenticator.EXPECT().AuthenticateTokenCredentialRequest(gomock.Any(), req).
Return(&user.DefaultInfo{
Name: "test-user",
Groups: []string{"test-group-1", "test-group-2"},
Extra: map[string][]string{"key-name": {"test-val-1", "test-val-2"}},
}, nil)
storage := NewREST(requestAuthenticator, nil, schema.GroupResource{}, auditLogger)
response, err := callCreate(storage, req)
requireSuccessfulResponseWithAuthenticationFailureMessage(t, err, response)
wantAuditLog = []testutil.WantedAuditLog{
testutil.WantAuditLog("TokenCredentialRequest Token Received", map[string]any{
"auditID": "fake-audit-id",
"tokenID": tokenToHash(req.Spec.Token),
}),
testutil.WantAuditLog("TokenCredentialRequest Unsupported UserInfo", map[string]any{
"auditID": "fake-audit-id",
"authenticator": map[string]any{
"apiGroup": "fake-api-group.com",
"kind": "FakeAuthenticatorKind",
"name": "fake-authenticator-name",
},
"reason": "unsupported value in userInfo returned by authenticator",
"err": `authenticator returned illegal userInfo extra key(s): userInfo extra key "key-name": Invalid value: "key-name": must be a domain-prefixed path (such as "acme.io/foo")`,
"userInfoExtrasCount": float64(1),
"personalInfo": map[string]any{
"userInfoName": "test-user",
"userInfoUID": "",
},
}),
}
@@ -552,6 +526,7 @@ func TestCreate(t *testing.T) {
"personalInfo": map[string]any{
"username": "test-user",
"groups": []any{},
"extras": map[string]any{},
},
}),
}
@@ -606,6 +581,7 @@ func TestCreate(t *testing.T) {
"personalInfo": map[string]any{
"username": "test-user",
"groups": []any{},
"extras": map[string]any{},
},
}),
}
@@ -697,7 +673,7 @@ func requireSuccessfulResponseWithAuthenticationFailureMessage(t *testing.T, err
func successfulIssuer(ctrl *gomock.Controller, fakeNow time.Time) clientcertissuer.ClientCertIssuer {
clientCertIssuer := mockissuer.NewMockClientCertIssuer(ctrl)
clientCertIssuer.EXPECT().
IssueClientCertPEM(gomock.Any(), gomock.Any(), gomock.Any()).
IssueClientCertPEM(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).
Return(&cert.PEM{
CertPEM: []byte("test-cert"),
KeyPEM: []byte("test-key"),
@@ -706,3 +682,112 @@ func successfulIssuer(ctrl *gomock.Controller, fakeNow time.Time) clientcertissu
}, nil)
return clientCertIssuer
}
func TestValidateExtraKeys(t *testing.T) {
tests := []struct {
name string
extras map[string][]string
wantErr string
}{
{
name: "allowed extras keys cause no error",
extras: map[string][]string{
"foo.com/example": {"foo"},
"x.y.z.foo.io/some-path": {"bar"},
"authentication.kubernetes.io/credential-id": {"baz"},
},
},
{
name: "empty extras keys cause error",
extras: map[string][]string{
"": {"foo"},
"foo.io/some-path": {"bar"},
},
wantErr: `userInfo extra key "": Required value`,
},
{
name: "extras keys not prefixed by domain name cause error",
extras: map[string][]string{
"foo": {"foo"},
"foo.io/some-path": {"bar"},
},
wantErr: `userInfo extra key "foo": Invalid value: "foo": must be a domain-prefixed path (such as "acme.io/foo")`,
},
{
name: "extras keys with upper case chars cause error",
extras: map[string][]string{
"fooBar.com/value": {"foo"},
"foo.io/some-path": {"bar"},
},
wantErr: `userInfo extra key "fooBar.com/value": Invalid value: "fooBar.com": ` +
`a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', ` +
`and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is ` +
`'[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')`,
},
{
name: "extras keys with unexpected chars in domain name cause error",
extras: map[string][]string{
"foobar🦭.com/path": {"foo"},
"foo.io/some-path": {"bar"},
},
wantErr: `userInfo extra key "foobar🦭.com/path": Invalid value: "foobar🦭.com": ` +
`a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', ` +
`and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is ` +
`'[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')`,
},
{
name: "extras keys with unexpected chars in path cause error",
extras: map[string][]string{
"foobar.com/🦭": {"foo"},
"foo.io/some-path": {"bar"},
},
wantErr: `userInfo extra key "foobar.com/🦭": Invalid value: "🦭": ` +
`Invalid path (regex used for validation is '[A-Za-z0-9/\-._~%!$&'()*+,;=:]+')`,
},
{
name: "extras keys with k8s.io cause error",
extras: map[string][]string{
"k8s.io/some-path": {"foo"},
"sub.k8s.io/some-path": {"foo"},
"foo.io/some-path": {"bar"},
},
wantErr: `[` +
`userInfo extra key "k8s.io/some-path": Invalid value: "k8s.io/some-path": k8s.io, kubernetes.io and their subdomains are reserved for Kubernetes use, ` +
`userInfo extra key "sub.k8s.io/some-path": Invalid value: "sub.k8s.io/some-path": k8s.io, kubernetes.io and their subdomains are reserved for Kubernetes use` +
`]`,
},
{
name: "extras keys with kubernetes.io cause error",
extras: map[string][]string{
"kubernetes.io/some-path": {"foo"},
"sub.kubernetes.io/some-path": {"foo"},
"foo.io/some-path": {"bar"},
},
wantErr: `[` +
`userInfo extra key "kubernetes.io/some-path": Invalid value: "kubernetes.io/some-path": k8s.io, kubernetes.io and their subdomains are reserved for Kubernetes use, ` +
`userInfo extra key "sub.kubernetes.io/some-path": Invalid value: "sub.kubernetes.io/some-path": k8s.io, kubernetes.io and their subdomains are reserved for Kubernetes use` +
`]`,
},
{
name: "extras keys with equals sign cause error",
extras: map[string][]string{
"foobar.com/some=path": {"foo"},
"foo.io/some-path": {"bar"},
},
wantErr: `userInfo extra key "foobar.com/some=path": Invalid value: "foobar.com/some=path": Pinniped does not allow extra key names to contain equals sign`,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
err := validateExtraKeys(tt.extras)
if tt.wantErr == "" {
require.Nilf(t, err, "wanted no error but got %s", err.ToAggregate())
} else {
require.EqualError(t, err.ToAggregate(), tt.wantErr)
}
})
}
}

View File

@@ -1,4 +1,4 @@
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package testutil
@@ -122,11 +122,18 @@ func (v *ValidCert) RequireCommonName(commonName string) {
require.Equal(v.t, commonName, v.parsed.Subject.CommonName)
}
// RequireOrganizations asserts that the certificate contains the provided orgs in the subject.
func (v *ValidCert) RequireOrganizations(orgs []string) {
v.t.Helper()
require.Equal(v.t, orgs, v.parsed.Subject.Organization)
}
// RequireOrganizationalUnits asserts that the certificate contains the provided organizational units in the subject.
func (v *ValidCert) RequireOrganizationalUnits(ous []string) {
v.t.Helper()
require.Equal(v.t, ous, v.parsed.Subject.OrganizationalUnit)
}
// CreateCertificate creates a certificate with the provided time bounds, and returns the PEM
// representation of the certificate and its private key. The returned certificate is capable of
// signing child certificates.