mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-12 05:21:58 +00:00
Merge pull request #123458 from aramase/aramase/i/min_jwt_payload
add min valid jwt payload to API docs for structured authn config
This commit is contained in:
commit
5cf4fbe524
@ -176,6 +176,14 @@ type AuthenticationConfiguration struct {
|
|||||||
// authenticators is neither defined nor stable across releases. Since
|
// authenticators is neither defined nor stable across releases. Since
|
||||||
// each JWT authenticator must have a unique issuer URL, at most one
|
// each JWT authenticator must have a unique issuer URL, at most one
|
||||||
// JWT authenticator will attempt to cryptographically validate the token.
|
// JWT authenticator will attempt to cryptographically validate the token.
|
||||||
|
//
|
||||||
|
// The minimum valid JWT payload must contain the following claims:
|
||||||
|
// {
|
||||||
|
// "iss": "https://issuer.example.com",
|
||||||
|
// "aud": ["audience"],
|
||||||
|
// "exp": 1234567890,
|
||||||
|
// "<username claim>": "username"
|
||||||
|
// }
|
||||||
JWT []JWTAuthenticator `json:"jwt"`
|
JWT []JWTAuthenticator `json:"jwt"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2953,6 +2953,38 @@ func TestToken(t *testing.T) {
|
|||||||
}`, valid.Unix()),
|
}`, valid.Unix()),
|
||||||
want: &user.DefaultInfo{},
|
want: &user.DefaultInfo{},
|
||||||
},
|
},
|
||||||
|
// test to assert the minimum valid jwt payload
|
||||||
|
// the required claims are iss, aud, exp and <claimMappings.Username> (in this case user).
|
||||||
|
{
|
||||||
|
name: "minimum valid jwt payload",
|
||||||
|
options: Options{
|
||||||
|
JWTAuthenticator: apiserver.JWTAuthenticator{
|
||||||
|
Issuer: apiserver.Issuer{
|
||||||
|
URL: "https://auth.example.com",
|
||||||
|
Audiences: []string{"my-client"},
|
||||||
|
},
|
||||||
|
ClaimMappings: apiserver.ClaimMappings{
|
||||||
|
Username: apiserver.PrefixedClaimOrExpression{
|
||||||
|
Expression: "claims.user",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
now: func() time.Time { return now },
|
||||||
|
},
|
||||||
|
signingKey: loadRSAPrivKey(t, "testdata/rsa_1.pem", jose.RS256),
|
||||||
|
pubKeys: []*jose.JSONWebKey{
|
||||||
|
loadRSAKey(t, "testdata/rsa_1.pem", jose.RS256),
|
||||||
|
},
|
||||||
|
claims: fmt.Sprintf(`{
|
||||||
|
"iss": "https://auth.example.com",
|
||||||
|
"aud": "my-client",
|
||||||
|
"user": "jane",
|
||||||
|
"exp": %d
|
||||||
|
}`, valid.Unix()),
|
||||||
|
want: &user.DefaultInfo{
|
||||||
|
Name: "jane",
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(test.name, test.run)
|
t.Run(test.name, test.run)
|
||||||
|
@ -107,6 +107,15 @@ var (
|
|||||||
// authenticationConfigFunc is a function that returns a string representation of an authentication config.
|
// authenticationConfigFunc is a function that returns a string representation of an authentication config.
|
||||||
type authenticationConfigFunc func(t *testing.T, issuerURL, caCert string) string
|
type authenticationConfigFunc func(t *testing.T, issuerURL, caCert string) string
|
||||||
|
|
||||||
|
type apiServerOIDCConfig struct {
|
||||||
|
oidcURL string
|
||||||
|
oidcClientID string
|
||||||
|
oidcCAFilePath string
|
||||||
|
oidcUsernamePrefix string
|
||||||
|
oidcUsernameClaim string
|
||||||
|
authenticationConfigYAML string
|
||||||
|
}
|
||||||
|
|
||||||
func TestOIDC(t *testing.T) {
|
func TestOIDC(t *testing.T) {
|
||||||
t.Log("Testing OIDC authenticator with --oidc-* flags")
|
t.Log("Testing OIDC authenticator with --oidc-* flags")
|
||||||
runTests(t, false)
|
runTests(t, false)
|
||||||
@ -123,15 +132,54 @@ func runTests(t *testing.T, useAuthenticationConfig bool) {
|
|||||||
var tests = []singleTest[*rsa.PrivateKey, *rsa.PublicKey]{
|
var tests = []singleTest[*rsa.PrivateKey, *rsa.PublicKey]{
|
||||||
{
|
{
|
||||||
name: "ID token is ok",
|
name: "ID token is ok",
|
||||||
configureInfrastructure: configureTestInfrastructure[*rsa.PrivateKey, *rsa.PublicKey],
|
configureInfrastructure: func(t *testing.T, fn authenticationConfigFunc, keyFunc func(t *testing.T) (*rsa.PrivateKey, *rsa.PublicKey)) (
|
||||||
configureOIDCServerBehaviour: func(t *testing.T, oidcServer *utilsoidc.TestServer, signingPrivateKey *rsa.PrivateKey) {
|
oidcServer *utilsoidc.TestServer,
|
||||||
|
apiServer *kubeapiserverapptesting.TestServer,
|
||||||
|
signingPrivateKey *rsa.PrivateKey,
|
||||||
|
caCertContent []byte,
|
||||||
|
caFilePath string,
|
||||||
|
) {
|
||||||
|
caCertContent, _, caFilePath, caKeyFilePath := generateCert(t)
|
||||||
|
signingPrivateKey, publicKey := keyFunc(t)
|
||||||
|
oidcServer = utilsoidc.BuildAndRunTestServer(t, caFilePath, caKeyFilePath)
|
||||||
|
|
||||||
|
if useAuthenticationConfig {
|
||||||
|
authenticationConfig := fmt.Sprintf(`
|
||||||
|
apiVersion: apiserver.config.k8s.io/v1alpha1
|
||||||
|
kind: AuthenticationConfiguration
|
||||||
|
jwt:
|
||||||
|
- issuer:
|
||||||
|
url: %s
|
||||||
|
audiences:
|
||||||
|
- %s
|
||||||
|
certificateAuthority: |
|
||||||
|
%s
|
||||||
|
claimMappings:
|
||||||
|
username:
|
||||||
|
claim: user
|
||||||
|
prefix: %s
|
||||||
|
`, oidcServer.URL(), defaultOIDCClientID, indentCertificateAuthority(string(caCertContent)), defaultOIDCUsernamePrefix)
|
||||||
|
apiServer = startTestAPIServerForOIDC(t, apiServerOIDCConfig{authenticationConfigYAML: authenticationConfig}, &signingPrivateKey.PublicKey)
|
||||||
|
} else {
|
||||||
|
apiServer = startTestAPIServerForOIDC(t, apiServerOIDCConfig{oidcURL: oidcServer.URL(), oidcClientID: defaultOIDCClientID,
|
||||||
|
oidcCAFilePath: caFilePath, oidcUsernamePrefix: defaultOIDCUsernamePrefix, oidcUsernameClaim: "user"}, &signingPrivateKey.PublicKey)
|
||||||
|
}
|
||||||
|
oidcServer.JwksHandler().EXPECT().KeySet().AnyTimes().DoAndReturn(utilsoidc.DefaultJwksHandlerBehavior(t, publicKey))
|
||||||
|
|
||||||
|
adminClient := kubernetes.NewForConfigOrDie(apiServer.ClientConfig)
|
||||||
|
configureRBAC(t, adminClient, defaultRole, defaultRoleBinding)
|
||||||
|
|
||||||
|
return oidcServer, apiServer, signingPrivateKey, caCertContent, caFilePath
|
||||||
|
}, configureOIDCServerBehaviour: func(t *testing.T, oidcServer *utilsoidc.TestServer, signingPrivateKey *rsa.PrivateKey) {
|
||||||
idTokenLifetime := time.Second * 1200
|
idTokenLifetime := time.Second * 1200
|
||||||
oidcServer.TokenHandler().EXPECT().Token().Times(1).DoAndReturn(utilsoidc.TokenHandlerBehaviorReturningPredefinedJWT(
|
oidcServer.TokenHandler().EXPECT().Token().Times(1).DoAndReturn(utilsoidc.TokenHandlerBehaviorReturningPredefinedJWT(
|
||||||
t,
|
t,
|
||||||
signingPrivateKey,
|
signingPrivateKey,
|
||||||
|
// This asserts the minimum valid claims for an ID token required by the authenticator.
|
||||||
|
// "iss", "aud", "exp" and a claim for the username.
|
||||||
map[string]interface{}{
|
map[string]interface{}{
|
||||||
"iss": oidcServer.URL(),
|
"iss": oidcServer.URL(),
|
||||||
"sub": defaultOIDCClaimedUsername,
|
"user": defaultOIDCClaimedUsername,
|
||||||
"aud": defaultOIDCClientID,
|
"aud": defaultOIDCClientID,
|
||||||
"exp": time.Now().Add(idTokenLifetime).Unix(),
|
"exp": time.Now().Add(idTokenLifetime).Unix(),
|
||||||
},
|
},
|
||||||
@ -244,9 +292,9 @@ jwt:
|
|||||||
claim: sub
|
claim: sub
|
||||||
prefix: %s
|
prefix: %s
|
||||||
`, oidcServer.URL(), defaultOIDCClientID, indentCertificateAuthority(string(caCertContent)), defaultOIDCUsernamePrefix)
|
`, oidcServer.URL(), defaultOIDCClientID, indentCertificateAuthority(string(caCertContent)), defaultOIDCUsernamePrefix)
|
||||||
apiServer = startTestAPIServerForOIDC(t, "", "", "", authenticationConfig, &signingPrivateKey.PublicKey)
|
apiServer = startTestAPIServerForOIDC(t, apiServerOIDCConfig{authenticationConfigYAML: authenticationConfig}, &signingPrivateKey.PublicKey)
|
||||||
} else {
|
} else {
|
||||||
apiServer = startTestAPIServerForOIDC(t, oidcServer.URL(), defaultOIDCClientID, caFilePath, "", &signingPrivateKey.PublicKey)
|
apiServer = startTestAPIServerForOIDC(t, apiServerOIDCConfig{oidcURL: oidcServer.URL(), oidcClientID: defaultOIDCClientID, oidcCAFilePath: caFilePath, oidcUsernamePrefix: defaultOIDCUsernamePrefix}, &signingPrivateKey.PublicKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
adminClient := kubernetes.NewForConfigOrDie(apiServer.ClientConfig)
|
adminClient := kubernetes.NewForConfigOrDie(apiServer.ClientConfig)
|
||||||
@ -875,9 +923,9 @@ func configureTestInfrastructure[K utilsoidc.JosePrivateKey, L utilsoidc.JosePub
|
|||||||
|
|
||||||
authenticationConfig := fn(t, oidcServer.URL(), string(caCertContent))
|
authenticationConfig := fn(t, oidcServer.URL(), string(caCertContent))
|
||||||
if len(authenticationConfig) > 0 {
|
if len(authenticationConfig) > 0 {
|
||||||
apiServer = startTestAPIServerForOIDC(t, "", "", "", authenticationConfig, publicKey)
|
apiServer = startTestAPIServerForOIDC(t, apiServerOIDCConfig{authenticationConfigYAML: authenticationConfig}, publicKey)
|
||||||
} else {
|
} else {
|
||||||
apiServer = startTestAPIServerForOIDC(t, oidcServer.URL(), defaultOIDCClientID, caFilePath, "", publicKey)
|
apiServer = startTestAPIServerForOIDC(t, apiServerOIDCConfig{oidcURL: oidcServer.URL(), oidcClientID: defaultOIDCClientID, oidcCAFilePath: caFilePath, oidcUsernamePrefix: defaultOIDCUsernamePrefix}, publicKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
oidcServer.JwksHandler().EXPECT().KeySet().AnyTimes().DoAndReturn(utilsoidc.DefaultJwksHandlerBehavior(t, publicKey))
|
oidcServer.JwksHandler().EXPECT().KeySet().AnyTimes().DoAndReturn(utilsoidc.DefaultJwksHandlerBehavior(t, publicKey))
|
||||||
@ -929,18 +977,21 @@ func configureClientConfigForOIDC(t *testing.T, config *rest.Config, clientID, c
|
|||||||
return cfg
|
return cfg
|
||||||
}
|
}
|
||||||
|
|
||||||
func startTestAPIServerForOIDC[L utilsoidc.JosePublicKey](t *testing.T, oidcURL, oidcClientID, oidcCAFilePath, authenticationConfigYAML string, publicKey L) *kubeapiserverapptesting.TestServer {
|
func startTestAPIServerForOIDC[L utilsoidc.JosePublicKey](t *testing.T, c apiServerOIDCConfig, publicKey L) *kubeapiserverapptesting.TestServer {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
var customFlags []string
|
var customFlags []string
|
||||||
if authenticationConfigYAML != "" {
|
if len(c.authenticationConfigYAML) > 0 {
|
||||||
customFlags = []string{fmt.Sprintf("--authentication-config=%s", writeTempFile(t, authenticationConfigYAML))}
|
customFlags = []string{fmt.Sprintf("--authentication-config=%s", writeTempFile(t, c.authenticationConfigYAML))}
|
||||||
} else {
|
} else {
|
||||||
customFlags = []string{
|
customFlags = []string{
|
||||||
fmt.Sprintf("--oidc-issuer-url=%s", oidcURL),
|
fmt.Sprintf("--oidc-issuer-url=%s", c.oidcURL),
|
||||||
fmt.Sprintf("--oidc-client-id=%s", oidcClientID),
|
fmt.Sprintf("--oidc-client-id=%s", c.oidcClientID),
|
||||||
fmt.Sprintf("--oidc-ca-file=%s", oidcCAFilePath),
|
fmt.Sprintf("--oidc-ca-file=%s", c.oidcCAFilePath),
|
||||||
fmt.Sprintf("--oidc-username-prefix=%s", defaultOIDCUsernamePrefix),
|
fmt.Sprintf("--oidc-username-prefix=%s", c.oidcUsernamePrefix),
|
||||||
|
}
|
||||||
|
if len(c.oidcUsernameClaim) > 0 {
|
||||||
|
customFlags = append(customFlags, fmt.Sprintf("--oidc-username-claim=%s", c.oidcUsernameClaim))
|
||||||
}
|
}
|
||||||
customFlags = append(customFlags, maybeSetSigningAlgs(publicKey)...)
|
customFlags = append(customFlags, maybeSetSigningAlgs(publicKey)...)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user