Skip to content

Commit 05cb0a5

Browse files
authored
Merge pull request kubernetes#123696 from aramase/aramase/f/kep_3331_v1beta1_api
Duplicate v1alpha1 AuthenticationConfiguration to v1beta1
2 parents bd25605 + b502aa6 commit 05cb0a5

File tree

6 files changed

+924
-1
lines changed

6 files changed

+924
-1
lines changed

pkg/kubeapiserver/options/authentication_test.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1030,6 +1030,64 @@ jwt:
10301030
},
10311031
expectedConfig: &apiserver.AuthenticationConfiguration{},
10321032
},
1033+
{
1034+
name: "v1beta1 - json",
1035+
file: func() string {
1036+
return writeTempFile(t, `{
1037+
"apiVersion":"apiserver.config.k8s.io/v1beta1",
1038+
"kind":"AuthenticationConfiguration",
1039+
"jwt":[{"issuer":{"url": "https://test-issuer"}}]}`)
1040+
},
1041+
expectedConfig: &apiserver.AuthenticationConfiguration{
1042+
JWT: []apiserver.JWTAuthenticator{
1043+
{
1044+
Issuer: apiserver.Issuer{
1045+
URL: "https://test-issuer",
1046+
},
1047+
},
1048+
},
1049+
},
1050+
},
1051+
{
1052+
name: "v1beta1 - yaml",
1053+
file: func() string {
1054+
return writeTempFile(t, `
1055+
apiVersion: apiserver.config.k8s.io/v1beta1
1056+
kind: AuthenticationConfiguration
1057+
jwt:
1058+
- issuer:
1059+
url: https://test-issuer
1060+
claimMappings:
1061+
username:
1062+
claim: sub
1063+
prefix: ""
1064+
`)
1065+
},
1066+
expectedConfig: &apiserver.AuthenticationConfiguration{
1067+
JWT: []apiserver.JWTAuthenticator{
1068+
{
1069+
Issuer: apiserver.Issuer{
1070+
URL: "https://test-issuer",
1071+
},
1072+
ClaimMappings: apiserver.ClaimMappings{
1073+
Username: apiserver.PrefixedClaimOrExpression{
1074+
Claim: "sub",
1075+
Prefix: pointer.String(""),
1076+
},
1077+
},
1078+
},
1079+
},
1080+
},
1081+
},
1082+
{
1083+
name: "v1beta1 - no jwt",
1084+
file: func() string {
1085+
return writeTempFile(t, `{
1086+
"apiVersion":"apiserver.config.k8s.io/v1beta1",
1087+
"kind":"AuthenticationConfiguration"}`)
1088+
},
1089+
expectedConfig: &apiserver.AuthenticationConfiguration{},
1090+
},
10331091
}
10341092

10351093
for _, tc := range testCases {

staging/src/k8s.io/apiserver/pkg/apis/apiserver/v1beta1/register.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ func addKnownTypes(scheme *runtime.Scheme) error {
5252
&EgressSelectorConfiguration{},
5353
)
5454
scheme.AddKnownTypes(ConfigSchemeGroupVersion,
55+
&AuthenticationConfiguration{},
5556
&AuthorizationConfiguration{},
5657
&TracingConfiguration{},
5758
)

staging/src/k8s.io/apiserver/pkg/apis/apiserver/v1beta1/types.go

Lines changed: 318 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,324 @@ type TracingConfiguration struct {
132132

133133
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
134134

135+
// AuthenticationConfiguration provides versioned configuration for authentication.
136+
type AuthenticationConfiguration struct {
137+
metav1.TypeMeta
138+
139+
// jwt is a list of authenticator to authenticate Kubernetes users using
140+
// JWT compliant tokens. The authenticator will attempt to parse a raw ID token,
141+
// verify it's been signed by the configured issuer. The public key to verify the
142+
// signature is discovered from the issuer's public endpoint using OIDC discovery.
143+
// For an incoming token, each JWT authenticator will be attempted in
144+
// the order in which it is specified in this list. Note however that
145+
// other authenticators may run before or after the JWT authenticators.
146+
// The specific position of JWT authenticators in relation to other
147+
// authenticators is neither defined nor stable across releases. Since
148+
// each JWT authenticator must have a unique issuer URL, at most one
149+
// JWT authenticator will attempt to cryptographically validate the token.
150+
//
151+
// The minimum valid JWT payload must contain the following claims:
152+
// {
153+
// "iss": "https://issuer.example.com",
154+
// "aud": ["audience"],
155+
// "exp": 1234567890,
156+
// "<username claim>": "username"
157+
// }
158+
JWT []JWTAuthenticator `json:"jwt"`
159+
}
160+
161+
// JWTAuthenticator provides the configuration for a single JWT authenticator.
162+
type JWTAuthenticator struct {
163+
// issuer contains the basic OIDC provider connection options.
164+
// +required
165+
Issuer Issuer `json:"issuer"`
166+
167+
// claimValidationRules are rules that are applied to validate token claims to authenticate users.
168+
// +optional
169+
ClaimValidationRules []ClaimValidationRule `json:"claimValidationRules,omitempty"`
170+
171+
// claimMappings points claims of a token to be treated as user attributes.
172+
// +required
173+
ClaimMappings ClaimMappings `json:"claimMappings"`
174+
175+
// userValidationRules are rules that are applied to final user before completing authentication.
176+
// These allow invariants to be applied to incoming identities such as preventing the
177+
// use of the system: prefix that is commonly used by Kubernetes components.
178+
// The validation rules are logically ANDed together and must all return true for the validation to pass.
179+
// +optional
180+
UserValidationRules []UserValidationRule `json:"userValidationRules,omitempty"`
181+
}
182+
183+
// Issuer provides the configuration for an external provider's specific settings.
184+
type Issuer struct {
185+
// url points to the issuer URL in a format https://url or https://url/path.
186+
// This must match the "iss" claim in the presented JWT, and the issuer returned from discovery.
187+
// Same value as the --oidc-issuer-url flag.
188+
// Discovery information is fetched from "{url}/.well-known/openid-configuration" unless overridden by discoveryURL.
189+
// Required to be unique across all JWT authenticators.
190+
// Note that egress selection configuration is not used for this network connection.
191+
// +required
192+
URL string `json:"url"`
193+
194+
// discoveryURL, if specified, overrides the URL used to fetch discovery
195+
// information instead of using "{url}/.well-known/openid-configuration".
196+
// The exact value specified is used, so "/.well-known/openid-configuration"
197+
// must be included in discoveryURL if needed.
198+
//
199+
// The "issuer" field in the fetched discovery information must match the "issuer.url" field
200+
// in the AuthenticationConfiguration and will be used to validate the "iss" claim in the presented JWT.
201+
// This is for scenarios where the well-known and jwks endpoints are hosted at a different
202+
// location than the issuer (such as locally in the cluster).
203+
//
204+
// Example:
205+
// A discovery url that is exposed using kubernetes service 'oidc' in namespace 'oidc-namespace'
206+
// and discovery information is available at '/.well-known/openid-configuration'.
207+
// discoveryURL: "https://oidc.oidc-namespace/.well-known/openid-configuration"
208+
// certificateAuthority is used to verify the TLS connection and the hostname on the leaf certificate
209+
// must be set to 'oidc.oidc-namespace'.
210+
//
211+
// curl https://oidc.oidc-namespace/.well-known/openid-configuration (.discoveryURL field)
212+
// {
213+
// issuer: "https://oidc.example.com" (.url field)
214+
// }
215+
//
216+
// discoveryURL must be different from url.
217+
// Required to be unique across all JWT authenticators.
218+
// Note that egress selection configuration is not used for this network connection.
219+
// +optional
220+
DiscoveryURL *string `json:"discoveryURL,omitempty"`
221+
222+
// certificateAuthority contains PEM-encoded certificate authority certificates
223+
// used to validate the connection when fetching discovery information.
224+
// If unset, the system verifier is used.
225+
// Same value as the content of the file referenced by the --oidc-ca-file flag.
226+
// +optional
227+
CertificateAuthority string `json:"certificateAuthority,omitempty"`
228+
229+
// audiences is the set of acceptable audiences the JWT must be issued to.
230+
// At least one of the entries must match the "aud" claim in presented JWTs.
231+
// Same value as the --oidc-client-id flag (though this field supports an array).
232+
// Required to be non-empty.
233+
// +required
234+
Audiences []string `json:"audiences"`
235+
236+
// audienceMatchPolicy defines how the "audiences" field is used to match the "aud" claim in the presented JWT.
237+
// Allowed values are:
238+
// 1. "MatchAny" when multiple audiences are specified and
239+
// 2. empty (or unset) or "MatchAny" when a single audience is specified.
240+
//
241+
// - MatchAny: the "aud" claim in the presented JWT must match at least one of the entries in the "audiences" field.
242+
// For example, if "audiences" is ["foo", "bar"], the "aud" claim in the presented JWT must contain either "foo" or "bar" (and may contain both).
243+
//
244+
// - "": The match policy can be empty (or unset) when a single audience is specified in the "audiences" field. The "aud" claim in the presented JWT must contain the single audience (and may contain others).
245+
//
246+
// For more nuanced audience validation, use claimValidationRules.
247+
// example: claimValidationRule[].expression: 'sets.equivalent(claims.aud, ["bar", "foo", "baz"])' to require an exact match.
248+
// +optional
249+
AudienceMatchPolicy AudienceMatchPolicyType `json:"audienceMatchPolicy,omitempty"`
250+
}
251+
252+
// AudienceMatchPolicyType is a set of valid values for issuer.audienceMatchPolicy
253+
type AudienceMatchPolicyType string
254+
255+
// Valid types for AudienceMatchPolicyType
256+
const (
257+
// MatchAny means the "aud" claim in the presented JWT must match at least one of the entries in the "audiences" field.
258+
AudienceMatchPolicyMatchAny AudienceMatchPolicyType = "MatchAny"
259+
)
260+
261+
// ClaimValidationRule provides the configuration for a single claim validation rule.
262+
type ClaimValidationRule struct {
263+
// claim is the name of a required claim.
264+
// Same as --oidc-required-claim flag.
265+
// Only string claim keys are supported.
266+
// Mutually exclusive with expression and message.
267+
// +optional
268+
Claim string `json:"claim,omitempty"`
269+
// requiredValue is the value of a required claim.
270+
// Same as --oidc-required-claim flag.
271+
// Only string claim values are supported.
272+
// If claim is set and requiredValue is not set, the claim must be present with a value set to the empty string.
273+
// Mutually exclusive with expression and message.
274+
// +optional
275+
RequiredValue string `json:"requiredValue,omitempty"`
276+
277+
// expression represents the expression which will be evaluated by CEL.
278+
// Must produce a boolean.
279+
//
280+
// CEL expressions have access to the contents of the token claims, organized into CEL variable:
281+
// - 'claims' is a map of claim names to claim values.
282+
// For example, a variable named 'sub' can be accessed as 'claims.sub'.
283+
// Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'.
284+
// Must return true for the validation to pass.
285+
//
286+
// Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
287+
//
288+
// Mutually exclusive with claim and requiredValue.
289+
// +optional
290+
Expression string `json:"expression,omitempty"`
291+
// message customizes the returned error message when expression returns false.
292+
// message is a literal string.
293+
// Mutually exclusive with claim and requiredValue.
294+
// +optional
295+
Message string `json:"message,omitempty"`
296+
}
297+
298+
// ClaimMappings provides the configuration for claim mapping
299+
type ClaimMappings struct {
300+
// username represents an option for the username attribute.
301+
// The claim's value must be a singular string.
302+
// Same as the --oidc-username-claim and --oidc-username-prefix flags.
303+
// If username.expression is set, the expression must produce a string value.
304+
//
305+
// In the flag based approach, the --oidc-username-claim and --oidc-username-prefix are optional. If --oidc-username-claim is not set,
306+
// the default value is "sub". For the authentication config, there is no defaulting for claim or prefix. The claim and prefix must be set explicitly.
307+
// For claim, if --oidc-username-claim was not set with legacy flag approach, configure username.claim="sub" in the authentication config.
308+
// For prefix:
309+
// (1) --oidc-username-prefix="-", no prefix was added to the username. For the same behavior using authentication config,
310+
// set username.prefix=""
311+
// (2) --oidc-username-prefix="" and --oidc-username-claim != "email", prefix was "<value of --oidc-issuer-url>#". For the same
312+
// behavior using authentication config, set username.prefix="<value of issuer.url>#"
313+
// (3) --oidc-username-prefix="<value>". For the same behavior using authentication config, set username.prefix="<value>"
314+
// +required
315+
Username PrefixedClaimOrExpression `json:"username"`
316+
// groups represents an option for the groups attribute.
317+
// The claim's value must be a string or string array claim.
318+
// If groups.claim is set, the prefix must be specified (and can be the empty string).
319+
// If groups.expression is set, the expression must produce a string or string array value.
320+
// "", [], and null values are treated as the group mapping not being present.
321+
// +optional
322+
Groups PrefixedClaimOrExpression `json:"groups,omitempty"`
323+
324+
// uid represents an option for the uid attribute.
325+
// Claim must be a singular string claim.
326+
// If uid.expression is set, the expression must produce a string value.
327+
// +optional
328+
UID ClaimOrExpression `json:"uid"`
329+
330+
// extra represents an option for the extra attribute.
331+
// expression must produce a string or string array value.
332+
// If the value is empty, the extra mapping will not be present.
333+
//
334+
// hard-coded extra key/value
335+
// - key: "foo"
336+
// valueExpression: "'bar'"
337+
// This will result in an extra attribute - foo: ["bar"]
338+
//
339+
// hard-coded key, value copying claim value
340+
// - key: "foo"
341+
// valueExpression: "claims.some_claim"
342+
// This will result in an extra attribute - foo: [value of some_claim]
343+
//
344+
// hard-coded key, value derived from claim value
345+
// - key: "admin"
346+
// valueExpression: '(has(claims.is_admin) && claims.is_admin) ? "true":""'
347+
// This will result in:
348+
// - if is_admin claim is present and true, extra attribute - admin: ["true"]
349+
// - if is_admin claim is present and false or is_admin claim is not present, no extra attribute will be added
350+
//
351+
// +optional
352+
Extra []ExtraMapping `json:"extra,omitempty"`
353+
}
354+
355+
// PrefixedClaimOrExpression provides the configuration for a single prefixed claim or expression.
356+
type PrefixedClaimOrExpression struct {
357+
// claim is the JWT claim to use.
358+
// Mutually exclusive with expression.
359+
// +optional
360+
Claim string `json:"claim,omitempty"`
361+
// prefix is prepended to claim's value to prevent clashes with existing names.
362+
// prefix needs to be set if claim is set and can be the empty string.
363+
// Mutually exclusive with expression.
364+
// +optional
365+
Prefix *string `json:"prefix,omitempty"`
366+
367+
// expression represents the expression which will be evaluated by CEL.
368+
//
369+
// CEL expressions have access to the contents of the token claims, organized into CEL variable:
370+
// - 'claims' is a map of claim names to claim values.
371+
// For example, a variable named 'sub' can be accessed as 'claims.sub'.
372+
// Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'.
373+
//
374+
// Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
375+
//
376+
// Mutually exclusive with claim and prefix.
377+
// +optional
378+
Expression string `json:"expression,omitempty"`
379+
}
380+
381+
// ClaimOrExpression provides the configuration for a single claim or expression.
382+
type ClaimOrExpression struct {
383+
// claim is the JWT claim to use.
384+
// Either claim or expression must be set.
385+
// Mutually exclusive with expression.
386+
// +optional
387+
Claim string `json:"claim,omitempty"`
388+
389+
// expression represents the expression which will be evaluated by CEL.
390+
//
391+
// CEL expressions have access to the contents of the token claims, organized into CEL variable:
392+
// - 'claims' is a map of claim names to claim values.
393+
// For example, a variable named 'sub' can be accessed as 'claims.sub'.
394+
// Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'.
395+
//
396+
// Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
397+
//
398+
// Mutually exclusive with claim.
399+
// +optional
400+
Expression string `json:"expression,omitempty"`
401+
}
402+
403+
// ExtraMapping provides the configuration for a single extra mapping.
404+
type ExtraMapping struct {
405+
// key is a string to use as the extra attribute key.
406+
// key must be a domain-prefix path (e.g. example.org/foo). All characters before the first "/" must be a valid
407+
// subdomain as defined by RFC 1123. All characters trailing the first "/" must
408+
// be valid HTTP Path characters as defined by RFC 3986.
409+
// key must be lowercase.
410+
// Required to be unique.
411+
// +required
412+
Key string `json:"key"`
413+
414+
// valueExpression is a CEL expression to extract extra attribute value.
415+
// valueExpression must produce a string or string array value.
416+
// "", [], and null values are treated as the extra mapping not being present.
417+
// Empty string values contained within a string array are filtered out.
418+
//
419+
// CEL expressions have access to the contents of the token claims, organized into CEL variable:
420+
// - 'claims' is a map of claim names to claim values.
421+
// For example, a variable named 'sub' can be accessed as 'claims.sub'.
422+
// Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'.
423+
//
424+
// Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
425+
//
426+
// +required
427+
ValueExpression string `json:"valueExpression"`
428+
}
429+
430+
// UserValidationRule provides the configuration for a single user info validation rule.
431+
type UserValidationRule struct {
432+
// expression represents the expression which will be evaluated by CEL.
433+
// Must return true for the validation to pass.
434+
//
435+
// CEL expressions have access to the contents of UserInfo, organized into CEL variable:
436+
// - 'user' - authentication.k8s.io/v1, Kind=UserInfo object
437+
// Refer to https://github.com/kubernetes/api/blob/release-1.28/authentication/v1/types.go#L105-L122 for the definition.
438+
// API documentation: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#userinfo-v1-authentication-k8s-io
439+
//
440+
// Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
441+
//
442+
// +required
443+
Expression string `json:"expression"`
444+
445+
// message customizes the returned error message when rule returns false.
446+
// message is a literal string.
447+
// +optional
448+
Message string `json:"message,omitempty"`
449+
}
450+
451+
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
452+
135453
type AuthorizationConfiguration struct {
136454
metav1.TypeMeta
137455

0 commit comments

Comments
 (0)