diff --git a/config/v1/tests/authentications.config.openshift.io/ExternalOIDCWithUpstreamParity.yaml b/config/v1/tests/authentications.config.openshift.io/ExternalOIDCWithUpstreamParity.yaml new file mode 100644 index 00000000000..b04943822c0 --- /dev/null +++ b/config/v1/tests/authentications.config.openshift.io/ExternalOIDCWithUpstreamParity.yaml @@ -0,0 +1,589 @@ + +apiVersion: apiextensions.k8s.io/v1 +name: "Authentication" +crdName: authentications.config.openshift.io +featureGates: +- ExternalOIDCWithUpstreamParity +tests: + onCreate: + - name: Valid discoveryURL + initial: | + apiVersion: config.openshift.io/v1 + kind: Authentication + spec: + type: OIDC + oidcProviders: + - name: myoidc + issuer: + issuerURL: https://meh.tld/ + audiences: ['openshift-aud'] + discoveryURL: https://auth.example.com/.well-known/openid-configuration + claimMappings: + username: + claim: "preferred_username" + prefixPolicy: Prefix + prefix: + prefixString: "myoidc:" + expected: | + apiVersion: config.openshift.io/v1 + kind: Authentication + spec: + type: OIDC + oidcProviders: + - name: myoidc + issuer: + issuerURL: https://meh.tld/ + audiences: ['openshift-aud'] + discoveryURL: https://auth.example.com/.well-known/openid-configuration + claimMappings: + username: + claim: "preferred_username" + prefixPolicy: Prefix + prefix: + prefixString: "myoidc:" + + - name: discoveryURL must be a valid URL + initial: | + apiVersion: config.openshift.io/v1 + kind: Authentication + spec: + type: OIDC + oidcProviders: + - name: myoidc + issuer: + issuerURL: https://meh.tld/ + audiences: ['openshift-aud'] + discoveryURL: not-a-valid-url + claimMappings: + username: + claim: "preferred_username" + prefixPolicy: Prefix + prefix: + prefixString: "myoidc:" + expectedError: "spec.oidcProviders[0].issuer.discoveryURL: Invalid value: \"string\": discoveryURL must be a valid URL, spec.oidcProviders[0].issuer.discoveryURL: Invalid value: \"string\": discoveryURL must be a valid https URL" + + - name: discoveryURL must not contain user info + initial: | + apiVersion: config.openshift.io/v1 + kind: Authentication + spec: + type: OIDC + oidcProviders: + - name: myoidc + issuer: + issuerURL: https://meh.tld/ + audiences: ['openshift-aud'] + discoveryURL: https://user:pass@auth.example.com/ + claimMappings: + username: + claim: "preferred_username" + prefixPolicy: Prefix + prefix: + prefixString: "myoidc:" + expectedError: "spec.oidcProviders[0].issuer.discoveryURL: Invalid value: \"string\": discoveryURL must not contain user info" + + - name: discoveryURL at max length (2048 chars, should pass) + initial: | + apiVersion: config.openshift.io/v1 + kind: Authentication + spec: + type: OIDC + oidcProviders: + - name: myoidc + issuer: + issuerURL: https://meh.tld/ + audiences: ['openshift-aud'] + discoveryURL: https://auth.example.com/aaaaaaaaaaaaaaa + claimMappings: + username: + claim: "preferred_username" + expected: | + apiVersion: config.openshift.io/v1 + kind: Authentication + spec: + type: OIDC + oidcProviders: + - name: myoidc + issuer: + issuerURL: https://meh.tld/ + audiences: ['openshift-aud'] + discoveryURL: https://auth.example.com/aaaaaaaaaaaaaaa + claimMappings: + username: + claim: "preferred_username" + + - name: discoveryURL exceeds max length by one (2049 chars, should fail) + initial: | + apiVersion: config.openshift.io/v1 + kind: Authentication + spec: + type: OIDC + oidcProviders: + - name: myoidc + issuer: + issuerURL: https://meh.tld/ + audiences: ['openshift-aud'] + discoveryURL: | + https://auth.example.com/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + claimMappings: + username: + claim: "preferred_username" + expectedError: "spec.oidcProviders[0].issuer.discoveryURL: Too long" + + - name: discoveryURL must not contain fragment + initial: | + apiVersion: config.openshift.io/v1 + kind: Authentication + spec: + type: OIDC + oidcProviders: + - name: myoidc + issuer: + issuerURL: https://meh.tld/ + audiences: ['openshift-aud'] + discoveryURL: https://auth.example.com/#fragment + claimMappings: + username: + claim: "preferred_username" + prefixPolicy: Prefix + prefix: + prefixString: "myoidc:" + expectedError: "spec.oidcProviders[0].issuer.discoveryURL: Invalid value: \"string\": discoveryURL must not contain fragments" + + - name: discoveryURL must use https + initial: | + apiVersion: config.openshift.io/v1 + kind: Authentication + spec: + type: OIDC + oidcProviders: + - name: myoidc + issuer: + issuerURL: https://meh.tld/ + audiences: ['openshift-aud'] + discoveryURL: http://auth.example.com/invalid + claimMappings: + username: + claim: "preferred_username" + prefixPolicy: Prefix + prefix: + prefixString: "myoidc:" + expectedError: "spec.oidcProviders[0].issuer.discoveryURL: Invalid value: \"string\": discoveryURL must be a valid https URL" + + - name: discoveryURL must not contain query + initial: | + apiVersion: config.openshift.io/v1 + kind: Authentication + spec: + type: OIDC + oidcProviders: + - name: myoidc + issuer: + issuerURL: https://meh.tld/ + audiences: ['openshift-aud'] + discoveryURL: https://auth.example.com/path?foo=bar + claimMappings: + username: + claim: "preferred_username" + prefixPolicy: Prefix + prefix: + prefixString: "myoidc:" + expectedError: "spec.oidcProviders[0].issuer.discoveryURL: Invalid value: \"string\": discoveryURL must not contain query parameters" + + - name: discoveryURL must be different from URL + initial: | + apiVersion: config.openshift.io/v1 + kind: Authentication + spec: + type: OIDC + oidcProviders: + - name: myoidc + issuer: + issuerURL: https://auth.example.com/.well-known/openid-configuration + audiences: ['openshift-aud'] + discoveryURL: https://auth.example.com/.well-known/openid-configuration + claimMappings: + username: + claim: "preferred_username" + prefixPolicy: Prefix + prefix: + prefixString: "myoidc:" + expectedError: "spec.oidcProviders[0].issuer: Invalid value: \"object\": discoveryURL must be different from issuerURL" + + - name: Valid AudienceMatchPolicy + initial: | + apiVersion: config.openshift.io/v1 + kind: Authentication + spec: + type: OIDC + oidcProviders: + - name: myoidc + issuer: + issuerURL: https://meh.tld + audiences: ['openshift-aud'] + audienceMatchPolicy: MatchAny + claimMappings: + username: + claim: "preferred_username" + prefixPolicy: Prefix + prefix: + prefixString: "myoidc:" + expected: | + apiVersion: config.openshift.io/v1 + kind: Authentication + spec: + type: OIDC + oidcProviders: + - name: myoidc + issuer: + issuerURL: https://meh.tld + audiences: ['openshift-aud'] + audienceMatchPolicy: MatchAny + claimMappings: + username: + claim: "preferred_username" + prefixPolicy: Prefix + prefix: + prefixString: "myoidc:" + + - name: Invalid AudienceMatchPolicy + initial: | + apiVersion: config.openshift.io/v1 + kind: Authentication + spec: + type: OIDC + oidcProviders: + - name: myoidc + issuer: + issuerURL: https://meh.tld + audiences: ['openshift-aud'] + audienceMatchPolicy: InvalidPolicy + claimMappings: + username: + claim: "preferred_username" + prefixPolicy: Prefix + prefix: + prefixString: "myoidc:" + expectedError: "audienceMatchPolicy: Unsupported value" + + - name: Valid RequiredClaim rule + initial: | + apiVersion: config.openshift.io/v1 + kind: Authentication + spec: + type: OIDC + oidcProviders: + - name: myoidc + issuer: + issuerURL: https://meh.tld + audiences: ['openshift-aud'] + claimValidationRules: + - type: RequiredClaim + requiredClaim: + claim: "role" + requiredValue: "admin" + claimMappings: + username: + claim: "preferred_username" + prefixPolicy: Prefix + prefix: + prefixString: "myoidc:" + expected: | + apiVersion: config.openshift.io/v1 + kind: Authentication + spec: + type: OIDC + oidcProviders: + - name: myoidc + issuer: + issuerURL: https://meh.tld + audiences: ['openshift-aud'] + claimValidationRules: + - type: RequiredClaim + requiredClaim: + claim: "role" + requiredValue: "admin" + claimMappings: + username: + claim: "preferred_username" + prefixPolicy: Prefix + prefix: + prefixString: "myoidc:" + + - name: Missing requiredClaim when type is RequiredClaim + initial: | + apiVersion: config.openshift.io/v1 + kind: Authentication + spec: + type: OIDC + oidcProviders: + - name: myoidc + issuer: + issuerURL: https://meh.tld + audiences: ['openshift-aud'] + claimValidationRules: + - type: RequiredClaim + claimMappings: + username: + claim: "preferred_username" + prefixPolicy: Prefix + prefix: + prefixString: "myoidc:" + expectedError: "requiredClaim must be set when type is 'RequiredClaim'" + + - name: Valid ExpressionRule configuration + initial: | + apiVersion: config.openshift.io/v1 + kind: Authentication + spec: + type: OIDC + oidcProviders: + - name: myoidc + issuer: + issuerURL: https://meh.tld + audiences: ['openshift-aud'] + claimMappings: + username: + claim: "preferred_username" + prefixPolicy: Prefix + prefix: + prefixString: "myoidc:" + claimValidationRules: + - type: Expression + expressionRule: + expression: "claims.email.endsWith('@example.com')" + message: "email must be from example.com" + expected: | + apiVersion: config.openshift.io/v1 + kind: Authentication + spec: + type: OIDC + oidcProviders: + - name: myoidc + issuer: + issuerURL: https://meh.tld + audiences: ['openshift-aud'] + claimValidationRules: + - type: Expression + expressionRule: + expression: "claims.email.endsWith('@example.com')" + message: "email must be from example.com" + claimMappings: + username: + claim: "preferred_username" + prefixPolicy: Prefix + prefix: + prefixString: "myoidc:" + + - name: Missing expressionRule for Expression type + initial: | + apiVersion: config.openshift.io/v1 + kind: Authentication + spec: + type: OIDC + oidcProviders: + - name: myoidc + issuer: + issuerURL: https://meh.tld + audiences: ['openshift-aud'] + claimMappings: + username: + claim: "preferred_username" + prefixPolicy: Prefix + prefix: + prefixString: "myoidc:" + claimValidationRules: + - type: Expression + expectedError: "expressionRule must be set when type is 'Expression', and forbidden otherwise" + + - name: Expression too long + initial: | + apiVersion: config.openshift.io/v1 + kind: Authentication + spec: + type: OIDC + oidcProviders: + - name: myoidc + issuer: + issuerURL: https://meh.tld + audiences: ['openshift-aud'] + claimMappings: + username: + claim: "preferred_username" + prefixPolicy: Prefix + prefix: + prefixString: "myoidc:" + claimValidationRules: + - type: Expression + expressionRule: + expression: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + + expectedError: "spec.oidcProviders[0].claimValidationRules[0].expressionRule.expression: Too long" + + - name: Empty expression in expressionRule + initial: | + apiVersion: config.openshift.io/v1 + kind: Authentication + spec: + type: OIDC + oidcProviders: + - name: myoidc + issuer: + issuerURL: https://meh.tld + audiences: ['openshift-aud'] + claimMappings: + username: + claim: "preferred_username" + prefixPolicy: Prefix + prefix: + prefixString: "myoidc:" + claimValidationRules: + - type: Expression + expressionRule: + sexpression: "" + message: "must not be empty" + expectedError: "spec.oidcProviders[0].claimValidationRules[0].expressionRule.expression: Required value" + + - name: Valid TokenUserValidationRule with expression and message + initial: | + apiVersion: config.openshift.io/v1 + kind: Authentication + spec: + type: OIDC + oidcProviders: + - name: myoidc + issuer: + issuerURL: https://meh.tld + audiences: ['openshift-aud'] + claimMappings: + username: + claim: "preferred_username" + prefixPolicy: Prefix + prefix: + prefixString: "myoidc:" + userValidationRules: + - expression: "user.username.startsWith('admin')" + message: "Only admin users are allowed" + expected: | + apiVersion: config.openshift.io/v1 + kind: Authentication + spec: + type: OIDC + oidcProviders: + - name: myoidc + issuer: + issuerURL: https://meh.tld + audiences: ['openshift-aud'] + claimMappings: + username: + claim: "preferred_username" + prefixPolicy: Prefix + prefix: + prefixString: "myoidc:" + userValidationRules: + - expression: "user.username.startsWith('admin')" + message: "Only admin users are allowed" + + - name: Missing expression in TokenUserValidationRule + initial: | + apiVersion: config.openshift.io/v1 + kind: Authentication + spec: + type: OIDC + oidcProviders: + - name: myoidc + issuer: + issuerURL: https://meh.tld + audiences: ['openshift-aud'] + claimMappings: + username: + claim: "preferred_username" + prefixPolicy: Prefix + prefix: + prefixString: "myoidc:" + userValidationRules: + - message: "Should never reach here" + expectedError: "expression: Required value" + + - name: Expression too long in TokenUserValidationRule + initial: | + apiVersion: config.openshift.io/v1 + kind: Authentication + spec: + type: OIDC + oidcProviders: + - name: myoidc + issuer: + issuerURL: https://meh.tld + audiences: ['openshift-aud'] + claimMappings: + username: + claim: "preferred_username" + prefixPolicy: Prefix + prefix: + prefixString: "myoidc:" + userValidationRules: + - expression: "aaaaaaaaaabhdgbshfghdfklgfdlhsfghfdsgdfusurghughurhghrgushgelurhglurhgukshjfdhjghfdughdfuhgushrgjhrgjhulhruhgurhgusrhughrughrsuhrughsughuhlgiusrhruighluirshguhrguhrluighlugihlrughruhgurhgurlhgrujhgurhsurhlughuisrhurhgurhegushguhseughreughruhguhreughrseuhlrushlghlruhglhur" + message: "This expression is too long" + expectedError: "expression: Too long" + + - name: Empty expression in TokenUserValidationRule + initial: | + apiVersion: config.openshift.io/v1 + kind: Authentication + spec: + type: OIDC + oidcProviders: + - name: myoidc + issuer: + issuerURL: https://meh.tld + audiences: ['openshift-aud'] + claimMappings: + username: + claim: "preferred_username" + prefixPolicy: Prefix + prefix: + prefixString: "myoidc:" + userValidationRules: + - expression: "" + message: "Empty expressions are invalid" + expectedError: "spec.oidcProviders[0].userValidationRules[0].expression: Invalid value: \"\": spec.oidcProviders[0].userValidationRules[0].expression in body should be at least 1 chars long" + + + - name: Valid TokenUserValidationRule with expression only + initial: | + apiVersion: config.openshift.io/v1 + kind: Authentication + spec: + type: OIDC + oidcProviders: + - name: myoidc + issuer: + issuerURL: https://meh.tld + audiences: ['openshift-aud'] + claimMappings: + username: + claim: "preferred_username" + prefixPolicy: Prefix + prefix: + prefixString: "myoidc:" + userValidationRules: + - expression: "user.groups.exists(g, g == 'admins')" + expected: | + apiVersion: config.openshift.io/v1 + kind: Authentication + spec: + type: OIDC + oidcProviders: + - name: myoidc + issuer: + issuerURL: https://meh.tld + audiences: ['openshift-aud'] + claimMappings: + username: + claim: "preferred_username" + prefixPolicy: Prefix + prefix: + prefixString: "myoidc:" + userValidationRules: + - expression: "user.groups.exists(g, g == 'admins')" diff --git a/config/v1/types_authentication.go b/config/v1/types_authentication.go index 52a41b2fef2..b4e54314dfe 100644 --- a/config/v1/types_authentication.go +++ b/config/v1/types_authentication.go @@ -5,7 +5,7 @@ import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" // +genclient // +genclient:nonNamespaced // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -// +openshift:validation:FeatureGateAwareXValidation:featureGate=ExternalOIDC;ExternalOIDCWithUIDAndExtraClaimMappings,rule="!has(self.spec.oidcProviders) || self.spec.oidcProviders.all(p, !has(p.oidcClients) || p.oidcClients.all(specC, self.status.oidcClients.exists(statusC, statusC.componentNamespace == specC.componentNamespace && statusC.componentName == specC.componentName) || (has(oldSelf.spec.oidcProviders) && oldSelf.spec.oidcProviders.exists(oldP, oldP.name == p.name && has(oldP.oidcClients) && oldP.oidcClients.exists(oldC, oldC.componentNamespace == specC.componentNamespace && oldC.componentName == specC.componentName)))))",message="all oidcClients in the oidcProviders must match their componentName and componentNamespace to either a previously configured oidcClient or they must exist in the status.oidcClients" +// +openshift:validation:FeatureGateAwareXValidation:featureGate=ExternalOIDC;ExternalOIDCWithUIDAndExtraClaimMappings;ExternalOIDCWithUpstreamParity,rule="!has(self.spec.oidcProviders) || self.spec.oidcProviders.all(p, !has(p.oidcClients) || p.oidcClients.all(specC, self.status.oidcClients.exists(statusC, statusC.componentNamespace == specC.componentNamespace && statusC.componentName == specC.componentName) || (has(oldSelf.spec.oidcProviders) && oldSelf.spec.oidcProviders.exists(oldP, oldP.name == p.name && has(oldP.oidcClients) && oldP.oidcClients.exists(oldC, oldC.componentNamespace == specC.componentNamespace && oldC.componentName == specC.componentName)))))",message="all oidcClients in the oidcProviders must match their componentName and componentNamespace to either a previously configured oidcClient or they must exist in the status.oidcClients" // Authentication specifies cluster-wide settings for authentication (like OAuth and // webhook token authenticators). The canonical name of an instance is `cluster`. @@ -91,6 +91,7 @@ type AuthenticationSpec struct { // +kubebuilder:validation:MaxItems=1 // +openshift:enable:FeatureGate=ExternalOIDC // +openshift:enable:FeatureGate=ExternalOIDCWithUIDAndExtraClaimMappings + // +openshift:enable:FeatureGate=ExternalOIDCWithUpstreamParity // +optional OIDCProviders []OIDCProvider `json:"oidcProviders,omitempty"` } @@ -243,11 +244,22 @@ type OIDCProvider struct { // +listType=atomic // +optional ClaimValidationRules []TokenClaimValidationRule `json:"claimValidationRules,omitempty"` + + // userValidationRules defines the set of rules used to validate claims in a user’s token. + // These rules determine whether a token subject is considered valid based on its claims. + // Each rule is evaluated independently. + // See the TokenUserValidationRule type for more information on rule structure. + // +listType=atomic + // +kubebuilder:validation:MaxItems=64 + // +optional + // +openshift:enable:FeatureGate=ExternalOIDCWithUpstreamParity + UserValidationRules []TokenUserValidationRule `json:"userValidationRules,omitempty"` } // +kubebuilder:validation:MinLength=1 type TokenAudience string +// +openshift:validation:FeatureGateAwareXValidation:featureGate=ExternalOIDCWithUpstreamParity,rule="self.?discoveryURL.orValue(\"\").size() > 0 ? (self.issuerURL.size() == 0 || self.discoveryURL.find('^.+[^/]') != self.issuerURL.find('^.+[^/]')) : true",message="discoveryURL must be different from issuerURL" type TokenIssuer struct { // issuerURL is a required field that configures the URL used to issue tokens // by the identity provider. @@ -291,8 +303,46 @@ type TokenIssuer struct { // // +optional CertificateAuthority ConfigMapNameReference `json:"issuerCertificateAuthority"` + // discoveryURL is an optional field that, if specified, overrides the default discovery endpoint + // used to retrieve OIDC configuration metadata. By default, the discovery URL is derived from `url` + // as "{url}/.well-known/openid-configuration". + // + // The discoveryURL must: + // - Be a valid absolute URL. + // - Use the HTTPS scheme. + // - Not contain query parameters, user info, or fragments. + // - Be different from the value of `url` (ignoring trailing slashes) + // + // +optional + // +openshift:enable:FeatureGate=ExternalOIDCWithUpstreamParity + // +kubebuilder:validation:XValidation:rule="self.size() > 0 ? isURL(self) : true",message="discoveryURL must be a valid URL" + // +kubebuilder:validation:XValidation:rule="self.size() > 0 ? (isURL(self) && url(self).getScheme() == 'https') : true",message="discoveryURL must be a valid https URL" + // +kubebuilder:validation:XValidation:rule="self.matches('^[^?]*$')",message="discoveryURL must not contain query parameters" + // +kubebuilder:validation:XValidation:rule="self.matches('^[^#]*$')",message="discoveryURL must not contain fragments" + // +kubebuilder:validation:XValidation:rule="self.matches('^[^@]*$')",message="discoveryURL must not contain user info" + // +kubebuilder:validation:MaxLength=2048 + DiscoveryURL *string `json:"discoveryURL,omitempty"` + + // audienceMatchPolicy specifies how token audiences are matched. + // If omitted, the system applies a default policy. + // Valid values are: + // - "MatchAny": The token is accepted if any of its audiences match any of the configured audiences. + // + // +optional + // +openshift:enable:FeatureGate=ExternalOIDCWithUpstreamParity + AudienceMatchPolicy *AudienceMatchPolicy `json:"audienceMatchPolicy,omitempty"` } +// AudienceMatchPolicyType is a set of valid values for Issuer.AudienceMatchPolicy. +// +// +kubebuilder:validation:Enum=MatchAny;"" +type AudienceMatchPolicy string + +// Valid types for AudienceMatchPolicyType +const ( + AudienceMatchPolicyMatchAny AudienceMatchPolicy = "MatchAny" +) + type TokenClaimMappings struct { // username is a required field that configures how the username of a cluster identity // should be constructed from the claims in a JWT token issued by the identity provider. @@ -717,44 +767,54 @@ type PrefixedClaimMapping struct { Prefix string `json:"prefix"` } -// TokenValidationRuleType represents the different -// claim validation rule types that can be configured. -// +enum +// TokenValidationRuleType defines the type of token validation rule. +// +// +kubebuilder:validation:Enum=RequiredClaim;Expression type TokenValidationRuleType string const ( - TokenValidationRuleTypeRequiredClaim = "RequiredClaim" + TokenValidationRuleRequiredClaim = "RequiredClaim" + TokenValidationRuleExpression = "Expression" ) +// TokenClaimValidationRule represents a validation rule based on token claims. +// If type is RequiredClaim, requiredClaim must be set. +// If type is Expression, expressionRule must be set. +// +// +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'RequiredClaim' ? has(self.requiredClaim) : !has(self.requiredClaim)",message="requiredClaim must be set when type is 'RequiredClaim', and forbidden otherwise" +// +openshift:validation:FeatureGateAwareXValidation:featureGate=ExternalOIDCWithUpstreamParity,rule="has(self.type) && self.type == 'Expression' ? has(self.expressionRule) : !has(self.expressionRule)",message="expressionRule must be set when type is 'Expression', and forbidden otherwise" type TokenClaimValidationRule struct { // type is an optional field that configures the type of the validation rule. // - // Allowed values are 'RequiredClaim' and omitted (not provided or an empty string). + // Allowed values are "RequiredClaim" and "Expression". + // + // When set to 'RequiredClaim', the Kubernetes API server will be configured + // to validate that the incoming JWT contains the required claim and that its + // value matches the required value. // - // When set to 'RequiredClaim', the Kubernetes API server - // will be configured to validate that the incoming JWT - // contains the required claim and that its value matches - // the required value. + // When set to 'Expression', the Kubernetes API server will be configured + // to validate the incoming JWT against the configured CEL expression. // - // Defaults to 'RequiredClaim'. + // Defaults to "RequiredClaim". // - // +kubebuilder:validation:Enum={"RequiredClaim"} // +kubebuilder:default="RequiredClaim" + // +kubebuilder:validation:Enum=RequiredClaim;Expression Type TokenValidationRuleType `json:"type"` - // requiredClaim is an optional field that configures the required claim - // and value that the Kubernetes API server will use to validate if an incoming - // JWT is valid for this identity provider. - // + // requiredClaim allows configuring a required claim name and its expected value. + // RequiredClaim is used when type is RequiredClaim. // +optional RequiredClaim *TokenRequiredClaim `json:"requiredClaim,omitempty"` + + // expressionRule contains the configuration for the "Expression" type. + // Must be set if type == "Expression". + // + // +optional + ExpressionRule *TokenExpressionRule `json:"expressionRule,omitempty"` } type TokenRequiredClaim struct { - // claim is a required field that configures the name of the required claim. - // When taken from the JWT claims, claim must be a string value. - // - // claim must not be an empty string (""). + // claim is a name of a required claim. Only claims with string values are supported. // // +kubebuilder:validation:MinLength=1 // +required @@ -771,3 +831,53 @@ type TokenRequiredClaim struct { // +required RequiredValue string `json:"requiredValue"` } + +type TokenExpressionRule struct { + // expression is a CEL expression evaluated against token claims. + // The expression must be a non-empty string and no longer than 4096 characters. + // This field is required. + // + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=4096 + // +required + // +openshift:enable:FeatureGate=ExternalOIDCWithUpstreamParity + Expression string `json:"expression,omitempty"` + + // message allows configuring the human-readable message that is returned + // from the Kubernetes API server when a token fails validation based on + // the CEL expression defined in 'expression'. This field is optional. + // + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=256 + // +openshift:enable:FeatureGate=ExternalOIDCWithUpstreamParity + Message string `json:"message,omitempty"` +} + +// TokenUserValidationRule provides a CEL-based rule used to validate a token subject. +// Each rule contains a CEL expression that is evaluated against the token’s claims. +type TokenUserValidationRule struct { + // expression is a CEL expression that must evaluate + // to true for the token to be accepted. The expression is evaluated against the token's + // user information (e.g., username, groups). + // + // If the expression evaluates to false, the token is rejected. + // See https://kubernetes.io/docs/reference/using-api/cel/ for CEL syntax. + // At least one rule must evaluate to true for the token to be considered valid. + // + // This field must be non-empty and may not exceed 4096 characters. + // + // +required + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=4096 + // +openshift:enable:FeatureGate=ExternalOIDCWithUpstreamParity + Expression string `json:"expression,omitempty"` + // message is an optional, human-readable message returned by the API server when + // this validation rule fails. It can help clarify why a token was rejected. + // + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=256 + // +openshift:enable:FeatureGate=ExternalOIDCWithUpstreamParity + Message string `json:"message,omitempty"` +} diff --git a/config/v1/zz_generated.crd-manifests/0000_10_config-operator_01_authentications-Hypershift-CustomNoUpgrade.crd.yaml b/config/v1/zz_generated.crd-manifests/0000_10_config-operator_01_authentications-Hypershift-CustomNoUpgrade.crd.yaml index 4f67bf9e0ca..eff7329eeff 100644 --- a/config/v1/zz_generated.crd-manifests/0000_10_config-operator_01_authentications-Hypershift-CustomNoUpgrade.crd.yaml +++ b/config/v1/zz_generated.crd-manifests/0000_10_config-operator_01_authentications-Hypershift-CustomNoUpgrade.crd.yaml @@ -347,19 +347,43 @@ spec: Validation rules are joined via an AND operation. items: + description: |- + TokenClaimValidationRule represents a validation rule based on token claims. + If type is RequiredClaim, requiredClaim must be set. + If type is Expression, expressionRule must be set. properties: + expressionRule: + description: |- + expressionRule contains the configuration for the "Expression" type. + Must be set if type == "Expression". + properties: + expression: + description: |- + expression is a CEL expression evaluated against token claims. + The expression must be a non-empty string and no longer than 4096 characters. + This field is required. + maxLength: 4096 + minLength: 1 + type: string + message: + description: |- + message allows configuring the human-readable message that is returned + from the Kubernetes API server when a token fails validation based on + the CEL expression defined in 'expression'. This field is optional. + maxLength: 256 + minLength: 1 + type: string + required: + - expression + type: object requiredClaim: description: |- - requiredClaim is an optional field that configures the required claim - and value that the Kubernetes API server will use to validate if an incoming - JWT is valid for this identity provider. + requiredClaim allows configuring a required claim name and its expected value. + RequiredClaim is used when type is RequiredClaim. properties: claim: - description: |- - claim is a required field that configures the name of the required claim. - When taken from the JWT claims, claim must be a string value. - - claim must not be an empty string (""). + description: claim is a name of a required claim. + Only claims with string values are supported. minLength: 1 type: string requiredValue: @@ -377,22 +401,38 @@ spec: - requiredValue type: object type: + allOf: + - enum: + - RequiredClaim + - Expression + - enum: + - RequiredClaim + - Expression default: RequiredClaim description: |- type is an optional field that configures the type of the validation rule. - Allowed values are 'RequiredClaim' and omitted (not provided or an empty string). + Allowed values are "RequiredClaim" and "Expression". - When set to 'RequiredClaim', the Kubernetes API server - will be configured to validate that the incoming JWT - contains the required claim and that its value matches - the required value. + When set to 'RequiredClaim', the Kubernetes API server will be configured + to validate that the incoming JWT contains the required claim and that its + value matches the required value. - Defaults to 'RequiredClaim'. - enum: - - RequiredClaim + When set to 'Expression', the Kubernetes API server will be configured + to validate the incoming JWT against the configured CEL expression. + + Defaults to "RequiredClaim". type: string type: object + x-kubernetes-validations: + - message: expressionRule must be set when type is 'Expression', + and forbidden otherwise + rule: 'has(self.type) && self.type == ''Expression'' ? has(self.expressionRule) + : !has(self.expressionRule)' + - message: requiredClaim must be set when type is 'RequiredClaim', + and forbidden otherwise + rule: 'has(self.type) && self.type == ''RequiredClaim'' + ? has(self.requiredClaim) : !has(self.requiredClaim)' type: array x-kubernetes-list-type: atomic issuer: @@ -401,6 +441,16 @@ spec: with the identity provider and how tokens issued from the identity provider are evaluated by the Kubernetes API server. properties: + audienceMatchPolicy: + description: |- + audienceMatchPolicy specifies how token audiences are matched. + If omitted, the system applies a default policy. + Valid values are: + - "MatchAny": The token is accepted if any of its audiences match any of the configured audiences. + enum: + - MatchAny + - "" + type: string audiences: description: |- audiences is a required field that configures the acceptable audiences @@ -415,6 +465,31 @@ spec: minItems: 1 type: array x-kubernetes-list-type: set + discoveryURL: + description: |- + discoveryURL is an optional field that, if specified, overrides the default discovery endpoint + used to retrieve OIDC configuration metadata. By default, the discovery URL is derived from `url` + as "{url}/.well-known/openid-configuration". + + The discoveryURL must: + - Be a valid absolute URL. + - Use the HTTPS scheme. + - Not contain query parameters, user info, or fragments. + - Be different from the value of `url` (ignoring trailing slashes) + maxLength: 2048 + type: string + x-kubernetes-validations: + - message: discoveryURL must be a valid URL + rule: 'self.size() > 0 ? isURL(self) : true' + - message: discoveryURL must be a valid https URL + rule: 'self.size() > 0 ? (isURL(self) && url(self).getScheme() + == ''https'') : true' + - message: discoveryURL must not contain query parameters + rule: self.matches('^[^?]*$') + - message: discoveryURL must not contain fragments + rule: self.matches('^[^#]*$') + - message: discoveryURL must not contain user info + rule: self.matches('^[^@]*$') issuerCertificateAuthority: description: |- issuerCertificateAuthority is an optional field that configures the @@ -461,6 +536,11 @@ spec: - audiences - issuerURL type: object + x-kubernetes-validations: + - message: discoveryURL must be different from issuerURL + rule: 'self.?discoveryURL.orValue("").size() > 0 ? (self.issuerURL.size() + == 0 || self.discoveryURL.find(''^.+[^/]'') != self.issuerURL.find(''^.+[^/]'')) + : true' name: description: |- name is a required field that configures the unique human-readable identifier @@ -560,6 +640,44 @@ spec: - componentNamespace - componentName x-kubernetes-list-type: map + userValidationRules: + description: |- + userValidationRules defines the set of rules used to validate claims in a user’s token. + These rules determine whether a token subject is considered valid based on its claims. + Each rule is evaluated independently. + See the TokenUserValidationRule type for more information on rule structure. + items: + description: |- + TokenUserValidationRule provides a CEL-based rule used to validate a token subject. + Each rule contains a CEL expression that is evaluated against the token’s claims. + properties: + expression: + description: |- + expression is a CEL expression that must evaluate + to true for the token to be accepted. The expression is evaluated against the token's + user information (e.g., username, groups). + + If the expression evaluates to false, the token is rejected. + See https://kubernetes.io/docs/reference/using-api/cel/ for CEL syntax. + At least one rule must evaluate to true for the token to be considered valid. + + This field must be non-empty and may not exceed 4096 characters. + maxLength: 4096 + minLength: 1 + type: string + message: + description: |- + message is an optional, human-readable message returned by the API server when + this validation rule fails. It can help clarify why a token was rejected. + maxLength: 256 + minLength: 1 + type: string + required: + - expression + type: object + maxItems: 64 + type: array + x-kubernetes-list-type: atomic required: - claimMappings - issuer diff --git a/config/v1/zz_generated.crd-manifests/0000_10_config-operator_01_authentications-Hypershift-Default.crd.yaml b/config/v1/zz_generated.crd-manifests/0000_10_config-operator_01_authentications-Hypershift-Default.crd.yaml index 2a3b60571cb..12189002007 100644 --- a/config/v1/zz_generated.crd-manifests/0000_10_config-operator_01_authentications-Hypershift-Default.crd.yaml +++ b/config/v1/zz_generated.crd-manifests/0000_10_config-operator_01_authentications-Hypershift-Default.crd.yaml @@ -196,19 +196,24 @@ spec: Validation rules are joined via an AND operation. items: + description: |- + TokenClaimValidationRule represents a validation rule based on token claims. + If type is RequiredClaim, requiredClaim must be set. + If type is Expression, expressionRule must be set. properties: + expressionRule: + description: |- + expressionRule contains the configuration for the "Expression" type. + Must be set if type == "Expression". + type: object requiredClaim: description: |- - requiredClaim is an optional field that configures the required claim - and value that the Kubernetes API server will use to validate if an incoming - JWT is valid for this identity provider. + requiredClaim allows configuring a required claim name and its expected value. + RequiredClaim is used when type is RequiredClaim. properties: claim: - description: |- - claim is a required field that configures the name of the required claim. - When taken from the JWT claims, claim must be a string value. - - claim must not be an empty string (""). + description: claim is a name of a required claim. + Only claims with string values are supported. minLength: 1 type: string requiredValue: @@ -226,22 +231,34 @@ spec: - requiredValue type: object type: + allOf: + - enum: + - RequiredClaim + - Expression + - enum: + - RequiredClaim + - Expression default: RequiredClaim description: |- type is an optional field that configures the type of the validation rule. - Allowed values are 'RequiredClaim' and omitted (not provided or an empty string). + Allowed values are "RequiredClaim" and "Expression". - When set to 'RequiredClaim', the Kubernetes API server - will be configured to validate that the incoming JWT - contains the required claim and that its value matches - the required value. + When set to 'RequiredClaim', the Kubernetes API server will be configured + to validate that the incoming JWT contains the required claim and that its + value matches the required value. - Defaults to 'RequiredClaim'. - enum: - - RequiredClaim + When set to 'Expression', the Kubernetes API server will be configured + to validate the incoming JWT against the configured CEL expression. + + Defaults to "RequiredClaim". type: string type: object + x-kubernetes-validations: + - message: requiredClaim must be set when type is 'RequiredClaim', + and forbidden otherwise + rule: 'has(self.type) && self.type == ''RequiredClaim'' + ? has(self.requiredClaim) : !has(self.requiredClaim)' type: array x-kubernetes-list-type: atomic issuer: diff --git a/config/v1/zz_generated.crd-manifests/0000_10_config-operator_01_authentications-Hypershift-DevPreviewNoUpgrade.crd.yaml b/config/v1/zz_generated.crd-manifests/0000_10_config-operator_01_authentications-Hypershift-DevPreviewNoUpgrade.crd.yaml index 195efce400b..373dbfac764 100644 --- a/config/v1/zz_generated.crd-manifests/0000_10_config-operator_01_authentications-Hypershift-DevPreviewNoUpgrade.crd.yaml +++ b/config/v1/zz_generated.crd-manifests/0000_10_config-operator_01_authentications-Hypershift-DevPreviewNoUpgrade.crd.yaml @@ -347,19 +347,43 @@ spec: Validation rules are joined via an AND operation. items: + description: |- + TokenClaimValidationRule represents a validation rule based on token claims. + If type is RequiredClaim, requiredClaim must be set. + If type is Expression, expressionRule must be set. properties: + expressionRule: + description: |- + expressionRule contains the configuration for the "Expression" type. + Must be set if type == "Expression". + properties: + expression: + description: |- + expression is a CEL expression evaluated against token claims. + The expression must be a non-empty string and no longer than 4096 characters. + This field is required. + maxLength: 4096 + minLength: 1 + type: string + message: + description: |- + message allows configuring the human-readable message that is returned + from the Kubernetes API server when a token fails validation based on + the CEL expression defined in 'expression'. This field is optional. + maxLength: 256 + minLength: 1 + type: string + required: + - expression + type: object requiredClaim: description: |- - requiredClaim is an optional field that configures the required claim - and value that the Kubernetes API server will use to validate if an incoming - JWT is valid for this identity provider. + requiredClaim allows configuring a required claim name and its expected value. + RequiredClaim is used when type is RequiredClaim. properties: claim: - description: |- - claim is a required field that configures the name of the required claim. - When taken from the JWT claims, claim must be a string value. - - claim must not be an empty string (""). + description: claim is a name of a required claim. + Only claims with string values are supported. minLength: 1 type: string requiredValue: @@ -377,22 +401,38 @@ spec: - requiredValue type: object type: + allOf: + - enum: + - RequiredClaim + - Expression + - enum: + - RequiredClaim + - Expression default: RequiredClaim description: |- type is an optional field that configures the type of the validation rule. - Allowed values are 'RequiredClaim' and omitted (not provided or an empty string). + Allowed values are "RequiredClaim" and "Expression". - When set to 'RequiredClaim', the Kubernetes API server - will be configured to validate that the incoming JWT - contains the required claim and that its value matches - the required value. + When set to 'RequiredClaim', the Kubernetes API server will be configured + to validate that the incoming JWT contains the required claim and that its + value matches the required value. - Defaults to 'RequiredClaim'. - enum: - - RequiredClaim + When set to 'Expression', the Kubernetes API server will be configured + to validate the incoming JWT against the configured CEL expression. + + Defaults to "RequiredClaim". type: string type: object + x-kubernetes-validations: + - message: expressionRule must be set when type is 'Expression', + and forbidden otherwise + rule: 'has(self.type) && self.type == ''Expression'' ? has(self.expressionRule) + : !has(self.expressionRule)' + - message: requiredClaim must be set when type is 'RequiredClaim', + and forbidden otherwise + rule: 'has(self.type) && self.type == ''RequiredClaim'' + ? has(self.requiredClaim) : !has(self.requiredClaim)' type: array x-kubernetes-list-type: atomic issuer: @@ -401,6 +441,16 @@ spec: with the identity provider and how tokens issued from the identity provider are evaluated by the Kubernetes API server. properties: + audienceMatchPolicy: + description: |- + audienceMatchPolicy specifies how token audiences are matched. + If omitted, the system applies a default policy. + Valid values are: + - "MatchAny": The token is accepted if any of its audiences match any of the configured audiences. + enum: + - MatchAny + - "" + type: string audiences: description: |- audiences is a required field that configures the acceptable audiences @@ -415,6 +465,31 @@ spec: minItems: 1 type: array x-kubernetes-list-type: set + discoveryURL: + description: |- + discoveryURL is an optional field that, if specified, overrides the default discovery endpoint + used to retrieve OIDC configuration metadata. By default, the discovery URL is derived from `url` + as "{url}/.well-known/openid-configuration". + + The discoveryURL must: + - Be a valid absolute URL. + - Use the HTTPS scheme. + - Not contain query parameters, user info, or fragments. + - Be different from the value of `url` (ignoring trailing slashes) + maxLength: 2048 + type: string + x-kubernetes-validations: + - message: discoveryURL must be a valid URL + rule: 'self.size() > 0 ? isURL(self) : true' + - message: discoveryURL must be a valid https URL + rule: 'self.size() > 0 ? (isURL(self) && url(self).getScheme() + == ''https'') : true' + - message: discoveryURL must not contain query parameters + rule: self.matches('^[^?]*$') + - message: discoveryURL must not contain fragments + rule: self.matches('^[^#]*$') + - message: discoveryURL must not contain user info + rule: self.matches('^[^@]*$') issuerCertificateAuthority: description: |- issuerCertificateAuthority is an optional field that configures the @@ -461,6 +536,11 @@ spec: - audiences - issuerURL type: object + x-kubernetes-validations: + - message: discoveryURL must be different from issuerURL + rule: 'self.?discoveryURL.orValue("").size() > 0 ? (self.issuerURL.size() + == 0 || self.discoveryURL.find(''^.+[^/]'') != self.issuerURL.find(''^.+[^/]'')) + : true' name: description: |- name is a required field that configures the unique human-readable identifier @@ -560,6 +640,44 @@ spec: - componentNamespace - componentName x-kubernetes-list-type: map + userValidationRules: + description: |- + userValidationRules defines the set of rules used to validate claims in a user’s token. + These rules determine whether a token subject is considered valid based on its claims. + Each rule is evaluated independently. + See the TokenUserValidationRule type for more information on rule structure. + items: + description: |- + TokenUserValidationRule provides a CEL-based rule used to validate a token subject. + Each rule contains a CEL expression that is evaluated against the token’s claims. + properties: + expression: + description: |- + expression is a CEL expression that must evaluate + to true for the token to be accepted. The expression is evaluated against the token's + user information (e.g., username, groups). + + If the expression evaluates to false, the token is rejected. + See https://kubernetes.io/docs/reference/using-api/cel/ for CEL syntax. + At least one rule must evaluate to true for the token to be considered valid. + + This field must be non-empty and may not exceed 4096 characters. + maxLength: 4096 + minLength: 1 + type: string + message: + description: |- + message is an optional, human-readable message returned by the API server when + this validation rule fails. It can help clarify why a token was rejected. + maxLength: 256 + minLength: 1 + type: string + required: + - expression + type: object + maxItems: 64 + type: array + x-kubernetes-list-type: atomic required: - claimMappings - issuer diff --git a/config/v1/zz_generated.crd-manifests/0000_10_config-operator_01_authentications-Hypershift-TechPreviewNoUpgrade.crd.yaml b/config/v1/zz_generated.crd-manifests/0000_10_config-operator_01_authentications-Hypershift-TechPreviewNoUpgrade.crd.yaml index 4e8c79c3201..2723a655104 100644 --- a/config/v1/zz_generated.crd-manifests/0000_10_config-operator_01_authentications-Hypershift-TechPreviewNoUpgrade.crd.yaml +++ b/config/v1/zz_generated.crd-manifests/0000_10_config-operator_01_authentications-Hypershift-TechPreviewNoUpgrade.crd.yaml @@ -347,19 +347,43 @@ spec: Validation rules are joined via an AND operation. items: + description: |- + TokenClaimValidationRule represents a validation rule based on token claims. + If type is RequiredClaim, requiredClaim must be set. + If type is Expression, expressionRule must be set. properties: + expressionRule: + description: |- + expressionRule contains the configuration for the "Expression" type. + Must be set if type == "Expression". + properties: + expression: + description: |- + expression is a CEL expression evaluated against token claims. + The expression must be a non-empty string and no longer than 4096 characters. + This field is required. + maxLength: 4096 + minLength: 1 + type: string + message: + description: |- + message allows configuring the human-readable message that is returned + from the Kubernetes API server when a token fails validation based on + the CEL expression defined in 'expression'. This field is optional. + maxLength: 256 + minLength: 1 + type: string + required: + - expression + type: object requiredClaim: description: |- - requiredClaim is an optional field that configures the required claim - and value that the Kubernetes API server will use to validate if an incoming - JWT is valid for this identity provider. + requiredClaim allows configuring a required claim name and its expected value. + RequiredClaim is used when type is RequiredClaim. properties: claim: - description: |- - claim is a required field that configures the name of the required claim. - When taken from the JWT claims, claim must be a string value. - - claim must not be an empty string (""). + description: claim is a name of a required claim. + Only claims with string values are supported. minLength: 1 type: string requiredValue: @@ -377,22 +401,38 @@ spec: - requiredValue type: object type: + allOf: + - enum: + - RequiredClaim + - Expression + - enum: + - RequiredClaim + - Expression default: RequiredClaim description: |- type is an optional field that configures the type of the validation rule. - Allowed values are 'RequiredClaim' and omitted (not provided or an empty string). + Allowed values are "RequiredClaim" and "Expression". - When set to 'RequiredClaim', the Kubernetes API server - will be configured to validate that the incoming JWT - contains the required claim and that its value matches - the required value. + When set to 'RequiredClaim', the Kubernetes API server will be configured + to validate that the incoming JWT contains the required claim and that its + value matches the required value. - Defaults to 'RequiredClaim'. - enum: - - RequiredClaim + When set to 'Expression', the Kubernetes API server will be configured + to validate the incoming JWT against the configured CEL expression. + + Defaults to "RequiredClaim". type: string type: object + x-kubernetes-validations: + - message: expressionRule must be set when type is 'Expression', + and forbidden otherwise + rule: 'has(self.type) && self.type == ''Expression'' ? has(self.expressionRule) + : !has(self.expressionRule)' + - message: requiredClaim must be set when type is 'RequiredClaim', + and forbidden otherwise + rule: 'has(self.type) && self.type == ''RequiredClaim'' + ? has(self.requiredClaim) : !has(self.requiredClaim)' type: array x-kubernetes-list-type: atomic issuer: @@ -401,6 +441,16 @@ spec: with the identity provider and how tokens issued from the identity provider are evaluated by the Kubernetes API server. properties: + audienceMatchPolicy: + description: |- + audienceMatchPolicy specifies how token audiences are matched. + If omitted, the system applies a default policy. + Valid values are: + - "MatchAny": The token is accepted if any of its audiences match any of the configured audiences. + enum: + - MatchAny + - "" + type: string audiences: description: |- audiences is a required field that configures the acceptable audiences @@ -415,6 +465,31 @@ spec: minItems: 1 type: array x-kubernetes-list-type: set + discoveryURL: + description: |- + discoveryURL is an optional field that, if specified, overrides the default discovery endpoint + used to retrieve OIDC configuration metadata. By default, the discovery URL is derived from `url` + as "{url}/.well-known/openid-configuration". + + The discoveryURL must: + - Be a valid absolute URL. + - Use the HTTPS scheme. + - Not contain query parameters, user info, or fragments. + - Be different from the value of `url` (ignoring trailing slashes) + maxLength: 2048 + type: string + x-kubernetes-validations: + - message: discoveryURL must be a valid URL + rule: 'self.size() > 0 ? isURL(self) : true' + - message: discoveryURL must be a valid https URL + rule: 'self.size() > 0 ? (isURL(self) && url(self).getScheme() + == ''https'') : true' + - message: discoveryURL must not contain query parameters + rule: self.matches('^[^?]*$') + - message: discoveryURL must not contain fragments + rule: self.matches('^[^#]*$') + - message: discoveryURL must not contain user info + rule: self.matches('^[^@]*$') issuerCertificateAuthority: description: |- issuerCertificateAuthority is an optional field that configures the @@ -461,6 +536,11 @@ spec: - audiences - issuerURL type: object + x-kubernetes-validations: + - message: discoveryURL must be different from issuerURL + rule: 'self.?discoveryURL.orValue("").size() > 0 ? (self.issuerURL.size() + == 0 || self.discoveryURL.find(''^.+[^/]'') != self.issuerURL.find(''^.+[^/]'')) + : true' name: description: |- name is a required field that configures the unique human-readable identifier @@ -560,6 +640,44 @@ spec: - componentNamespace - componentName x-kubernetes-list-type: map + userValidationRules: + description: |- + userValidationRules defines the set of rules used to validate claims in a user’s token. + These rules determine whether a token subject is considered valid based on its claims. + Each rule is evaluated independently. + See the TokenUserValidationRule type for more information on rule structure. + items: + description: |- + TokenUserValidationRule provides a CEL-based rule used to validate a token subject. + Each rule contains a CEL expression that is evaluated against the token’s claims. + properties: + expression: + description: |- + expression is a CEL expression that must evaluate + to true for the token to be accepted. The expression is evaluated against the token's + user information (e.g., username, groups). + + If the expression evaluates to false, the token is rejected. + See https://kubernetes.io/docs/reference/using-api/cel/ for CEL syntax. + At least one rule must evaluate to true for the token to be considered valid. + + This field must be non-empty and may not exceed 4096 characters. + maxLength: 4096 + minLength: 1 + type: string + message: + description: |- + message is an optional, human-readable message returned by the API server when + this validation rule fails. It can help clarify why a token was rejected. + maxLength: 256 + minLength: 1 + type: string + required: + - expression + type: object + maxItems: 64 + type: array + x-kubernetes-list-type: atomic required: - claimMappings - issuer diff --git a/config/v1/zz_generated.crd-manifests/0000_10_config-operator_01_authentications-SelfManagedHA-CustomNoUpgrade.crd.yaml b/config/v1/zz_generated.crd-manifests/0000_10_config-operator_01_authentications-SelfManagedHA-CustomNoUpgrade.crd.yaml index 72c798fae70..91b11806a68 100644 --- a/config/v1/zz_generated.crd-manifests/0000_10_config-operator_01_authentications-SelfManagedHA-CustomNoUpgrade.crd.yaml +++ b/config/v1/zz_generated.crd-manifests/0000_10_config-operator_01_authentications-SelfManagedHA-CustomNoUpgrade.crd.yaml @@ -347,19 +347,43 @@ spec: Validation rules are joined via an AND operation. items: + description: |- + TokenClaimValidationRule represents a validation rule based on token claims. + If type is RequiredClaim, requiredClaim must be set. + If type is Expression, expressionRule must be set. properties: + expressionRule: + description: |- + expressionRule contains the configuration for the "Expression" type. + Must be set if type == "Expression". + properties: + expression: + description: |- + expression is a CEL expression evaluated against token claims. + The expression must be a non-empty string and no longer than 4096 characters. + This field is required. + maxLength: 4096 + minLength: 1 + type: string + message: + description: |- + message allows configuring the human-readable message that is returned + from the Kubernetes API server when a token fails validation based on + the CEL expression defined in 'expression'. This field is optional. + maxLength: 256 + minLength: 1 + type: string + required: + - expression + type: object requiredClaim: description: |- - requiredClaim is an optional field that configures the required claim - and value that the Kubernetes API server will use to validate if an incoming - JWT is valid for this identity provider. + requiredClaim allows configuring a required claim name and its expected value. + RequiredClaim is used when type is RequiredClaim. properties: claim: - description: |- - claim is a required field that configures the name of the required claim. - When taken from the JWT claims, claim must be a string value. - - claim must not be an empty string (""). + description: claim is a name of a required claim. + Only claims with string values are supported. minLength: 1 type: string requiredValue: @@ -377,22 +401,38 @@ spec: - requiredValue type: object type: + allOf: + - enum: + - RequiredClaim + - Expression + - enum: + - RequiredClaim + - Expression default: RequiredClaim description: |- type is an optional field that configures the type of the validation rule. - Allowed values are 'RequiredClaim' and omitted (not provided or an empty string). + Allowed values are "RequiredClaim" and "Expression". - When set to 'RequiredClaim', the Kubernetes API server - will be configured to validate that the incoming JWT - contains the required claim and that its value matches - the required value. + When set to 'RequiredClaim', the Kubernetes API server will be configured + to validate that the incoming JWT contains the required claim and that its + value matches the required value. - Defaults to 'RequiredClaim'. - enum: - - RequiredClaim + When set to 'Expression', the Kubernetes API server will be configured + to validate the incoming JWT against the configured CEL expression. + + Defaults to "RequiredClaim". type: string type: object + x-kubernetes-validations: + - message: expressionRule must be set when type is 'Expression', + and forbidden otherwise + rule: 'has(self.type) && self.type == ''Expression'' ? has(self.expressionRule) + : !has(self.expressionRule)' + - message: requiredClaim must be set when type is 'RequiredClaim', + and forbidden otherwise + rule: 'has(self.type) && self.type == ''RequiredClaim'' + ? has(self.requiredClaim) : !has(self.requiredClaim)' type: array x-kubernetes-list-type: atomic issuer: @@ -401,6 +441,16 @@ spec: with the identity provider and how tokens issued from the identity provider are evaluated by the Kubernetes API server. properties: + audienceMatchPolicy: + description: |- + audienceMatchPolicy specifies how token audiences are matched. + If omitted, the system applies a default policy. + Valid values are: + - "MatchAny": The token is accepted if any of its audiences match any of the configured audiences. + enum: + - MatchAny + - "" + type: string audiences: description: |- audiences is a required field that configures the acceptable audiences @@ -415,6 +465,31 @@ spec: minItems: 1 type: array x-kubernetes-list-type: set + discoveryURL: + description: |- + discoveryURL is an optional field that, if specified, overrides the default discovery endpoint + used to retrieve OIDC configuration metadata. By default, the discovery URL is derived from `url` + as "{url}/.well-known/openid-configuration". + + The discoveryURL must: + - Be a valid absolute URL. + - Use the HTTPS scheme. + - Not contain query parameters, user info, or fragments. + - Be different from the value of `url` (ignoring trailing slashes) + maxLength: 2048 + type: string + x-kubernetes-validations: + - message: discoveryURL must be a valid URL + rule: 'self.size() > 0 ? isURL(self) : true' + - message: discoveryURL must be a valid https URL + rule: 'self.size() > 0 ? (isURL(self) && url(self).getScheme() + == ''https'') : true' + - message: discoveryURL must not contain query parameters + rule: self.matches('^[^?]*$') + - message: discoveryURL must not contain fragments + rule: self.matches('^[^#]*$') + - message: discoveryURL must not contain user info + rule: self.matches('^[^@]*$') issuerCertificateAuthority: description: |- issuerCertificateAuthority is an optional field that configures the @@ -461,6 +536,11 @@ spec: - audiences - issuerURL type: object + x-kubernetes-validations: + - message: discoveryURL must be different from issuerURL + rule: 'self.?discoveryURL.orValue("").size() > 0 ? (self.issuerURL.size() + == 0 || self.discoveryURL.find(''^.+[^/]'') != self.issuerURL.find(''^.+[^/]'')) + : true' name: description: |- name is a required field that configures the unique human-readable identifier @@ -560,6 +640,44 @@ spec: - componentNamespace - componentName x-kubernetes-list-type: map + userValidationRules: + description: |- + userValidationRules defines the set of rules used to validate claims in a user’s token. + These rules determine whether a token subject is considered valid based on its claims. + Each rule is evaluated independently. + See the TokenUserValidationRule type for more information on rule structure. + items: + description: |- + TokenUserValidationRule provides a CEL-based rule used to validate a token subject. + Each rule contains a CEL expression that is evaluated against the token’s claims. + properties: + expression: + description: |- + expression is a CEL expression that must evaluate + to true for the token to be accepted. The expression is evaluated against the token's + user information (e.g., username, groups). + + If the expression evaluates to false, the token is rejected. + See https://kubernetes.io/docs/reference/using-api/cel/ for CEL syntax. + At least one rule must evaluate to true for the token to be considered valid. + + This field must be non-empty and may not exceed 4096 characters. + maxLength: 4096 + minLength: 1 + type: string + message: + description: |- + message is an optional, human-readable message returned by the API server when + this validation rule fails. It can help clarify why a token was rejected. + maxLength: 256 + minLength: 1 + type: string + required: + - expression + type: object + maxItems: 64 + type: array + x-kubernetes-list-type: atomic required: - claimMappings - issuer diff --git a/config/v1/zz_generated.crd-manifests/0000_10_config-operator_01_authentications-SelfManagedHA-DevPreviewNoUpgrade.crd.yaml b/config/v1/zz_generated.crd-manifests/0000_10_config-operator_01_authentications-SelfManagedHA-DevPreviewNoUpgrade.crd.yaml index 998e804191f..ecb533f94de 100644 --- a/config/v1/zz_generated.crd-manifests/0000_10_config-operator_01_authentications-SelfManagedHA-DevPreviewNoUpgrade.crd.yaml +++ b/config/v1/zz_generated.crd-manifests/0000_10_config-operator_01_authentications-SelfManagedHA-DevPreviewNoUpgrade.crd.yaml @@ -347,19 +347,43 @@ spec: Validation rules are joined via an AND operation. items: + description: |- + TokenClaimValidationRule represents a validation rule based on token claims. + If type is RequiredClaim, requiredClaim must be set. + If type is Expression, expressionRule must be set. properties: + expressionRule: + description: |- + expressionRule contains the configuration for the "Expression" type. + Must be set if type == "Expression". + properties: + expression: + description: |- + expression is a CEL expression evaluated against token claims. + The expression must be a non-empty string and no longer than 4096 characters. + This field is required. + maxLength: 4096 + minLength: 1 + type: string + message: + description: |- + message allows configuring the human-readable message that is returned + from the Kubernetes API server when a token fails validation based on + the CEL expression defined in 'expression'. This field is optional. + maxLength: 256 + minLength: 1 + type: string + required: + - expression + type: object requiredClaim: description: |- - requiredClaim is an optional field that configures the required claim - and value that the Kubernetes API server will use to validate if an incoming - JWT is valid for this identity provider. + requiredClaim allows configuring a required claim name and its expected value. + RequiredClaim is used when type is RequiredClaim. properties: claim: - description: |- - claim is a required field that configures the name of the required claim. - When taken from the JWT claims, claim must be a string value. - - claim must not be an empty string (""). + description: claim is a name of a required claim. + Only claims with string values are supported. minLength: 1 type: string requiredValue: @@ -377,22 +401,38 @@ spec: - requiredValue type: object type: + allOf: + - enum: + - RequiredClaim + - Expression + - enum: + - RequiredClaim + - Expression default: RequiredClaim description: |- type is an optional field that configures the type of the validation rule. - Allowed values are 'RequiredClaim' and omitted (not provided or an empty string). + Allowed values are "RequiredClaim" and "Expression". - When set to 'RequiredClaim', the Kubernetes API server - will be configured to validate that the incoming JWT - contains the required claim and that its value matches - the required value. + When set to 'RequiredClaim', the Kubernetes API server will be configured + to validate that the incoming JWT contains the required claim and that its + value matches the required value. - Defaults to 'RequiredClaim'. - enum: - - RequiredClaim + When set to 'Expression', the Kubernetes API server will be configured + to validate the incoming JWT against the configured CEL expression. + + Defaults to "RequiredClaim". type: string type: object + x-kubernetes-validations: + - message: expressionRule must be set when type is 'Expression', + and forbidden otherwise + rule: 'has(self.type) && self.type == ''Expression'' ? has(self.expressionRule) + : !has(self.expressionRule)' + - message: requiredClaim must be set when type is 'RequiredClaim', + and forbidden otherwise + rule: 'has(self.type) && self.type == ''RequiredClaim'' + ? has(self.requiredClaim) : !has(self.requiredClaim)' type: array x-kubernetes-list-type: atomic issuer: @@ -401,6 +441,16 @@ spec: with the identity provider and how tokens issued from the identity provider are evaluated by the Kubernetes API server. properties: + audienceMatchPolicy: + description: |- + audienceMatchPolicy specifies how token audiences are matched. + If omitted, the system applies a default policy. + Valid values are: + - "MatchAny": The token is accepted if any of its audiences match any of the configured audiences. + enum: + - MatchAny + - "" + type: string audiences: description: |- audiences is a required field that configures the acceptable audiences @@ -415,6 +465,31 @@ spec: minItems: 1 type: array x-kubernetes-list-type: set + discoveryURL: + description: |- + discoveryURL is an optional field that, if specified, overrides the default discovery endpoint + used to retrieve OIDC configuration metadata. By default, the discovery URL is derived from `url` + as "{url}/.well-known/openid-configuration". + + The discoveryURL must: + - Be a valid absolute URL. + - Use the HTTPS scheme. + - Not contain query parameters, user info, or fragments. + - Be different from the value of `url` (ignoring trailing slashes) + maxLength: 2048 + type: string + x-kubernetes-validations: + - message: discoveryURL must be a valid URL + rule: 'self.size() > 0 ? isURL(self) : true' + - message: discoveryURL must be a valid https URL + rule: 'self.size() > 0 ? (isURL(self) && url(self).getScheme() + == ''https'') : true' + - message: discoveryURL must not contain query parameters + rule: self.matches('^[^?]*$') + - message: discoveryURL must not contain fragments + rule: self.matches('^[^#]*$') + - message: discoveryURL must not contain user info + rule: self.matches('^[^@]*$') issuerCertificateAuthority: description: |- issuerCertificateAuthority is an optional field that configures the @@ -461,6 +536,11 @@ spec: - audiences - issuerURL type: object + x-kubernetes-validations: + - message: discoveryURL must be different from issuerURL + rule: 'self.?discoveryURL.orValue("").size() > 0 ? (self.issuerURL.size() + == 0 || self.discoveryURL.find(''^.+[^/]'') != self.issuerURL.find(''^.+[^/]'')) + : true' name: description: |- name is a required field that configures the unique human-readable identifier @@ -560,6 +640,44 @@ spec: - componentNamespace - componentName x-kubernetes-list-type: map + userValidationRules: + description: |- + userValidationRules defines the set of rules used to validate claims in a user’s token. + These rules determine whether a token subject is considered valid based on its claims. + Each rule is evaluated independently. + See the TokenUserValidationRule type for more information on rule structure. + items: + description: |- + TokenUserValidationRule provides a CEL-based rule used to validate a token subject. + Each rule contains a CEL expression that is evaluated against the token’s claims. + properties: + expression: + description: |- + expression is a CEL expression that must evaluate + to true for the token to be accepted. The expression is evaluated against the token's + user information (e.g., username, groups). + + If the expression evaluates to false, the token is rejected. + See https://kubernetes.io/docs/reference/using-api/cel/ for CEL syntax. + At least one rule must evaluate to true for the token to be considered valid. + + This field must be non-empty and may not exceed 4096 characters. + maxLength: 4096 + minLength: 1 + type: string + message: + description: |- + message is an optional, human-readable message returned by the API server when + this validation rule fails. It can help clarify why a token was rejected. + maxLength: 256 + minLength: 1 + type: string + required: + - expression + type: object + maxItems: 64 + type: array + x-kubernetes-list-type: atomic required: - claimMappings - issuer diff --git a/config/v1/zz_generated.crd-manifests/0000_10_config-operator_01_authentications-SelfManagedHA-TechPreviewNoUpgrade.crd.yaml b/config/v1/zz_generated.crd-manifests/0000_10_config-operator_01_authentications-SelfManagedHA-TechPreviewNoUpgrade.crd.yaml index 75446be6cca..95ddd24093e 100644 --- a/config/v1/zz_generated.crd-manifests/0000_10_config-operator_01_authentications-SelfManagedHA-TechPreviewNoUpgrade.crd.yaml +++ b/config/v1/zz_generated.crd-manifests/0000_10_config-operator_01_authentications-SelfManagedHA-TechPreviewNoUpgrade.crd.yaml @@ -347,19 +347,43 @@ spec: Validation rules are joined via an AND operation. items: + description: |- + TokenClaimValidationRule represents a validation rule based on token claims. + If type is RequiredClaim, requiredClaim must be set. + If type is Expression, expressionRule must be set. properties: + expressionRule: + description: |- + expressionRule contains the configuration for the "Expression" type. + Must be set if type == "Expression". + properties: + expression: + description: |- + expression is a CEL expression evaluated against token claims. + The expression must be a non-empty string and no longer than 4096 characters. + This field is required. + maxLength: 4096 + minLength: 1 + type: string + message: + description: |- + message allows configuring the human-readable message that is returned + from the Kubernetes API server when a token fails validation based on + the CEL expression defined in 'expression'. This field is optional. + maxLength: 256 + minLength: 1 + type: string + required: + - expression + type: object requiredClaim: description: |- - requiredClaim is an optional field that configures the required claim - and value that the Kubernetes API server will use to validate if an incoming - JWT is valid for this identity provider. + requiredClaim allows configuring a required claim name and its expected value. + RequiredClaim is used when type is RequiredClaim. properties: claim: - description: |- - claim is a required field that configures the name of the required claim. - When taken from the JWT claims, claim must be a string value. - - claim must not be an empty string (""). + description: claim is a name of a required claim. + Only claims with string values are supported. minLength: 1 type: string requiredValue: @@ -377,22 +401,38 @@ spec: - requiredValue type: object type: + allOf: + - enum: + - RequiredClaim + - Expression + - enum: + - RequiredClaim + - Expression default: RequiredClaim description: |- type is an optional field that configures the type of the validation rule. - Allowed values are 'RequiredClaim' and omitted (not provided or an empty string). + Allowed values are "RequiredClaim" and "Expression". - When set to 'RequiredClaim', the Kubernetes API server - will be configured to validate that the incoming JWT - contains the required claim and that its value matches - the required value. + When set to 'RequiredClaim', the Kubernetes API server will be configured + to validate that the incoming JWT contains the required claim and that its + value matches the required value. - Defaults to 'RequiredClaim'. - enum: - - RequiredClaim + When set to 'Expression', the Kubernetes API server will be configured + to validate the incoming JWT against the configured CEL expression. + + Defaults to "RequiredClaim". type: string type: object + x-kubernetes-validations: + - message: expressionRule must be set when type is 'Expression', + and forbidden otherwise + rule: 'has(self.type) && self.type == ''Expression'' ? has(self.expressionRule) + : !has(self.expressionRule)' + - message: requiredClaim must be set when type is 'RequiredClaim', + and forbidden otherwise + rule: 'has(self.type) && self.type == ''RequiredClaim'' + ? has(self.requiredClaim) : !has(self.requiredClaim)' type: array x-kubernetes-list-type: atomic issuer: @@ -401,6 +441,16 @@ spec: with the identity provider and how tokens issued from the identity provider are evaluated by the Kubernetes API server. properties: + audienceMatchPolicy: + description: |- + audienceMatchPolicy specifies how token audiences are matched. + If omitted, the system applies a default policy. + Valid values are: + - "MatchAny": The token is accepted if any of its audiences match any of the configured audiences. + enum: + - MatchAny + - "" + type: string audiences: description: |- audiences is a required field that configures the acceptable audiences @@ -415,6 +465,31 @@ spec: minItems: 1 type: array x-kubernetes-list-type: set + discoveryURL: + description: |- + discoveryURL is an optional field that, if specified, overrides the default discovery endpoint + used to retrieve OIDC configuration metadata. By default, the discovery URL is derived from `url` + as "{url}/.well-known/openid-configuration". + + The discoveryURL must: + - Be a valid absolute URL. + - Use the HTTPS scheme. + - Not contain query parameters, user info, or fragments. + - Be different from the value of `url` (ignoring trailing slashes) + maxLength: 2048 + type: string + x-kubernetes-validations: + - message: discoveryURL must be a valid URL + rule: 'self.size() > 0 ? isURL(self) : true' + - message: discoveryURL must be a valid https URL + rule: 'self.size() > 0 ? (isURL(self) && url(self).getScheme() + == ''https'') : true' + - message: discoveryURL must not contain query parameters + rule: self.matches('^[^?]*$') + - message: discoveryURL must not contain fragments + rule: self.matches('^[^#]*$') + - message: discoveryURL must not contain user info + rule: self.matches('^[^@]*$') issuerCertificateAuthority: description: |- issuerCertificateAuthority is an optional field that configures the @@ -461,6 +536,11 @@ spec: - audiences - issuerURL type: object + x-kubernetes-validations: + - message: discoveryURL must be different from issuerURL + rule: 'self.?discoveryURL.orValue("").size() > 0 ? (self.issuerURL.size() + == 0 || self.discoveryURL.find(''^.+[^/]'') != self.issuerURL.find(''^.+[^/]'')) + : true' name: description: |- name is a required field that configures the unique human-readable identifier @@ -560,6 +640,44 @@ spec: - componentNamespace - componentName x-kubernetes-list-type: map + userValidationRules: + description: |- + userValidationRules defines the set of rules used to validate claims in a user’s token. + These rules determine whether a token subject is considered valid based on its claims. + Each rule is evaluated independently. + See the TokenUserValidationRule type for more information on rule structure. + items: + description: |- + TokenUserValidationRule provides a CEL-based rule used to validate a token subject. + Each rule contains a CEL expression that is evaluated against the token’s claims. + properties: + expression: + description: |- + expression is a CEL expression that must evaluate + to true for the token to be accepted. The expression is evaluated against the token's + user information (e.g., username, groups). + + If the expression evaluates to false, the token is rejected. + See https://kubernetes.io/docs/reference/using-api/cel/ for CEL syntax. + At least one rule must evaluate to true for the token to be considered valid. + + This field must be non-empty and may not exceed 4096 characters. + maxLength: 4096 + minLength: 1 + type: string + message: + description: |- + message is an optional, human-readable message returned by the API server when + this validation rule fails. It can help clarify why a token was rejected. + maxLength: 256 + minLength: 1 + type: string + required: + - expression + type: object + maxItems: 64 + type: array + x-kubernetes-list-type: atomic required: - claimMappings - issuer diff --git a/config/v1/zz_generated.deepcopy.go b/config/v1/zz_generated.deepcopy.go index 788e10479b6..4a300c62165 100644 --- a/config/v1/zz_generated.deepcopy.go +++ b/config/v1/zz_generated.deepcopy.go @@ -4599,6 +4599,11 @@ func (in *OIDCProvider) DeepCopyInto(out *OIDCProvider) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.UserValidationRules != nil { + in, out := &in.UserValidationRules, &out.UserValidationRules + *out = make([]TokenUserValidationRule, len(*in)) + copy(*out, *in) + } return } @@ -6230,6 +6235,11 @@ func (in *TokenClaimValidationRule) DeepCopyInto(out *TokenClaimValidationRule) *out = new(TokenRequiredClaim) **out = **in } + if in.ExpressionRule != nil { + in, out := &in.ExpressionRule, &out.ExpressionRule + *out = new(TokenExpressionRule) + **out = **in + } return } @@ -6264,6 +6274,22 @@ func (in *TokenConfig) DeepCopy() *TokenConfig { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TokenExpressionRule) DeepCopyInto(out *TokenExpressionRule) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TokenExpressionRule. +func (in *TokenExpressionRule) DeepCopy() *TokenExpressionRule { + if in == nil { + return nil + } + out := new(TokenExpressionRule) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TokenIssuer) DeepCopyInto(out *TokenIssuer) { *out = *in @@ -6273,6 +6299,16 @@ func (in *TokenIssuer) DeepCopyInto(out *TokenIssuer) { copy(*out, *in) } out.CertificateAuthority = in.CertificateAuthority + if in.DiscoveryURL != nil { + in, out := &in.DiscoveryURL, &out.DiscoveryURL + *out = new(string) + **out = **in + } + if in.AudienceMatchPolicy != nil { + in, out := &in.AudienceMatchPolicy, &out.AudienceMatchPolicy + *out = new(AudienceMatchPolicy) + **out = **in + } return } @@ -6302,6 +6338,22 @@ func (in *TokenRequiredClaim) DeepCopy() *TokenRequiredClaim { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TokenUserValidationRule) DeepCopyInto(out *TokenUserValidationRule) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TokenUserValidationRule. +func (in *TokenUserValidationRule) DeepCopy() *TokenUserValidationRule { + if in == nil { + return nil + } + out := new(TokenUserValidationRule) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Update) DeepCopyInto(out *Update) { *out = *in diff --git a/config/v1/zz_generated.featuregated-crd-manifests.yaml b/config/v1/zz_generated.featuregated-crd-manifests.yaml index 6d756e8f904..d8c2e2fd289 100644 --- a/config/v1/zz_generated.featuregated-crd-manifests.yaml +++ b/config/v1/zz_generated.featuregated-crd-manifests.yaml @@ -31,6 +31,7 @@ authentications.config.openshift.io: FeatureGates: - ExternalOIDC - ExternalOIDCWithUIDAndExtraClaimMappings + - ExternalOIDCWithUpstreamParity FilenameOperatorName: config-operator FilenameOperatorOrdering: "01" FilenameRunLevel: "0000_10" diff --git a/config/v1/zz_generated.featuregated-crd-manifests/authentications.config.openshift.io/ExternalOIDC.yaml b/config/v1/zz_generated.featuregated-crd-manifests/authentications.config.openshift.io/ExternalOIDC.yaml index 06d4e0f041c..2ed9bb1f668 100644 --- a/config/v1/zz_generated.featuregated-crd-manifests/authentications.config.openshift.io/ExternalOIDC.yaml +++ b/config/v1/zz_generated.featuregated-crd-manifests/authentications.config.openshift.io/ExternalOIDC.yaml @@ -197,19 +197,24 @@ spec: Validation rules are joined via an AND operation. items: + description: |- + TokenClaimValidationRule represents a validation rule based on token claims. + If type is RequiredClaim, requiredClaim must be set. + If type is Expression, expressionRule must be set. properties: + expressionRule: + description: |- + expressionRule contains the configuration for the "Expression" type. + Must be set if type == "Expression". + type: object requiredClaim: description: |- - requiredClaim is an optional field that configures the required claim - and value that the Kubernetes API server will use to validate if an incoming - JWT is valid for this identity provider. + requiredClaim allows configuring a required claim name and its expected value. + RequiredClaim is used when type is RequiredClaim. properties: claim: - description: |- - claim is a required field that configures the name of the required claim. - When taken from the JWT claims, claim must be a string value. - - claim must not be an empty string (""). + description: claim is a name of a required claim. + Only claims with string values are supported. minLength: 1 type: string requiredValue: @@ -227,22 +232,34 @@ spec: - requiredValue type: object type: + allOf: + - enum: + - RequiredClaim + - Expression + - enum: + - RequiredClaim + - Expression default: RequiredClaim description: |- type is an optional field that configures the type of the validation rule. - Allowed values are 'RequiredClaim' and omitted (not provided or an empty string). + Allowed values are "RequiredClaim" and "Expression". - When set to 'RequiredClaim', the Kubernetes API server - will be configured to validate that the incoming JWT - contains the required claim and that its value matches - the required value. + When set to 'RequiredClaim', the Kubernetes API server will be configured + to validate that the incoming JWT contains the required claim and that its + value matches the required value. - Defaults to 'RequiredClaim'. - enum: - - RequiredClaim + When set to 'Expression', the Kubernetes API server will be configured + to validate the incoming JWT against the configured CEL expression. + + Defaults to "RequiredClaim". type: string type: object + x-kubernetes-validations: + - message: requiredClaim must be set when type is 'RequiredClaim', + and forbidden otherwise + rule: 'has(self.type) && self.type == ''RequiredClaim'' + ? has(self.requiredClaim) : !has(self.requiredClaim)' type: array x-kubernetes-list-type: atomic issuer: diff --git a/config/v1/zz_generated.featuregated-crd-manifests/authentications.config.openshift.io/ExternalOIDCWithUIDAndExtraClaimMappings.yaml b/config/v1/zz_generated.featuregated-crd-manifests/authentications.config.openshift.io/ExternalOIDCWithUIDAndExtraClaimMappings.yaml index 34ed169f089..7892245c482 100644 --- a/config/v1/zz_generated.featuregated-crd-manifests/authentications.config.openshift.io/ExternalOIDCWithUIDAndExtraClaimMappings.yaml +++ b/config/v1/zz_generated.featuregated-crd-manifests/authentications.config.openshift.io/ExternalOIDCWithUIDAndExtraClaimMappings.yaml @@ -348,19 +348,24 @@ spec: Validation rules are joined via an AND operation. items: + description: |- + TokenClaimValidationRule represents a validation rule based on token claims. + If type is RequiredClaim, requiredClaim must be set. + If type is Expression, expressionRule must be set. properties: + expressionRule: + description: |- + expressionRule contains the configuration for the "Expression" type. + Must be set if type == "Expression". + type: object requiredClaim: description: |- - requiredClaim is an optional field that configures the required claim - and value that the Kubernetes API server will use to validate if an incoming - JWT is valid for this identity provider. + requiredClaim allows configuring a required claim name and its expected value. + RequiredClaim is used when type is RequiredClaim. properties: claim: - description: |- - claim is a required field that configures the name of the required claim. - When taken from the JWT claims, claim must be a string value. - - claim must not be an empty string (""). + description: claim is a name of a required claim. + Only claims with string values are supported. minLength: 1 type: string requiredValue: @@ -378,22 +383,34 @@ spec: - requiredValue type: object type: + allOf: + - enum: + - RequiredClaim + - Expression + - enum: + - RequiredClaim + - Expression default: RequiredClaim description: |- type is an optional field that configures the type of the validation rule. - Allowed values are 'RequiredClaim' and omitted (not provided or an empty string). + Allowed values are "RequiredClaim" and "Expression". - When set to 'RequiredClaim', the Kubernetes API server - will be configured to validate that the incoming JWT - contains the required claim and that its value matches - the required value. + When set to 'RequiredClaim', the Kubernetes API server will be configured + to validate that the incoming JWT contains the required claim and that its + value matches the required value. - Defaults to 'RequiredClaim'. - enum: - - RequiredClaim + When set to 'Expression', the Kubernetes API server will be configured + to validate the incoming JWT against the configured CEL expression. + + Defaults to "RequiredClaim". type: string type: object + x-kubernetes-validations: + - message: requiredClaim must be set when type is 'RequiredClaim', + and forbidden otherwise + rule: 'has(self.type) && self.type == ''RequiredClaim'' + ? has(self.requiredClaim) : !has(self.requiredClaim)' type: array x-kubernetes-list-type: atomic issuer: diff --git a/config/v1/zz_generated.featuregated-crd-manifests/authentications.config.openshift.io/ExternalOIDCWithUpstreamParity.yaml b/config/v1/zz_generated.featuregated-crd-manifests/authentications.config.openshift.io/ExternalOIDCWithUpstreamParity.yaml new file mode 100644 index 00000000000..258ac5b6b19 --- /dev/null +++ b/config/v1/zz_generated.featuregated-crd-manifests/authentications.config.openshift.io/ExternalOIDCWithUpstreamParity.yaml @@ -0,0 +1,664 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.openshift.io: https://github.com/openshift/api/pull/470 + api.openshift.io/filename-cvo-runlevel: "0000_10" + api.openshift.io/filename-operator: config-operator + api.openshift.io/filename-ordering: "01" + feature-gate.release.openshift.io/ExternalOIDCWithUpstreamParity: "true" + release.openshift.io/bootstrap-required: "true" + name: authentications.config.openshift.io +spec: + group: config.openshift.io + names: + kind: Authentication + listKind: AuthenticationList + plural: authentications + singular: authentication + scope: Cluster + versions: + - name: v1 + schema: + openAPIV3Schema: + description: |- + Authentication specifies cluster-wide settings for authentication (like OAuth and + webhook token authenticators). The canonical name of an instance is `cluster`. + + Compatibility level 1: Stable within a major release for a minimum of 12 months or 3 minor releases (whichever is longer). + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: spec holds user settable values for configuration + properties: + oauthMetadata: + description: |- + oauthMetadata contains the discovery endpoint data for OAuth 2.0 + Authorization Server Metadata for an external OAuth server. + This discovery document can be viewed from its served location: + oc get --raw '/.well-known/oauth-authorization-server' + For further details, see the IETF Draft: + https://tools.ietf.org/html/draft-ietf-oauth-discovery-04#section-2 + If oauthMetadata.name is non-empty, this value has precedence + over any metadata reference stored in status. + The key "oauthMetadata" is used to locate the data. + If specified and the config map or expected key is not found, no metadata is served. + If the specified metadata is not valid, no metadata is served. + The namespace for this config map is openshift-config. + properties: + name: + description: name is the metadata.name of the referenced config + map + type: string + required: + - name + type: object + oidcProviders: + description: |- + oidcProviders are OIDC identity providers that can issue tokens + for this cluster + Can only be set if "Type" is set to "OIDC". + + At most one provider can be configured. + items: + properties: + claimMappings: + description: |- + claimMappings is a required field that configures the rules to be used by + the Kubernetes API server for translating claims in a JWT token, issued + by the identity provider, to a cluster identity. + properties: + groups: + description: |- + groups is an optional field that configures how the groups of a cluster identity + should be constructed from the claims in a JWT token issued + by the identity provider. + When referencing a claim, if the claim is present in the JWT + token, its value must be a list of groups separated by a comma (','). + For example - '"example"' and '"exampleOne", "exampleTwo", "exampleThree"' are valid claim values. + properties: + claim: + description: |- + claim is a required field that configures the JWT token + claim whose value is assigned to the cluster identity + field associated with this mapping. + type: string + prefix: + description: |- + prefix is an optional field that configures the prefix that will be + applied to the cluster identity attribute during the process of mapping + JWT claims to cluster identity attributes. + + When omitted (""), no prefix is applied to the cluster identity attribute. + + Example: if `prefix` is set to "myoidc:" and the `claim` in JWT contains + an array of strings "a", "b" and "c", the mapping will result in an + array of string "myoidc:a", "myoidc:b" and "myoidc:c". + type: string + required: + - claim + type: object + username: + description: |- + username is a required field that configures how the username of a cluster identity + should be constructed from the claims in a JWT token issued by the identity provider. + properties: + claim: + description: |- + claim is a required field that configures the JWT token + claim whose value is assigned to the cluster identity + field associated with this mapping. + + claim must not be an empty string ("") and must not exceed 256 characters. + maxLength: 256 + minLength: 1 + type: string + prefix: + description: |- + prefix configures the prefix that should be prepended to the value + of the JWT claim. + + prefix must be set when prefixPolicy is set to 'Prefix' and must be unset otherwise. + properties: + prefixString: + description: |- + prefixString is a required field that configures the prefix that will + be applied to cluster identity username attribute + during the process of mapping JWT claims to cluster identity attributes. + + prefixString must not be an empty string (""). + minLength: 1 + type: string + required: + - prefixString + type: object + prefixPolicy: + description: |- + prefixPolicy is an optional field that configures how a prefix should be + applied to the value of the JWT claim specified in the 'claim' field. + + Allowed values are 'Prefix', 'NoPrefix', and omitted (not provided or an empty string). + + When set to 'Prefix', the value specified in the prefix field will be + prepended to the value of the JWT claim. + The prefix field must be set when prefixPolicy is 'Prefix'. + + When set to 'NoPrefix', no prefix will be prepended to the value + of the JWT claim. + + When omitted, this means no opinion and the platform is left to choose + any prefixes that are applied which is subject to change over time. + Currently, the platform prepends `{issuerURL}#` to the value of the JWT claim + when the claim is not 'email'. + As an example, consider the following scenario: + `prefix` is unset, `issuerURL` is set to `https://myoidc.tld`, + the JWT claims include "username":"userA" and "email":"userA@myoidc.tld", + and `claim` is set to: + - "username": the mapped value will be "https://myoidc.tld#userA" + - "email": the mapped value will be "userA@myoidc.tld" + enum: + - "" + - NoPrefix + - Prefix + type: string + required: + - claim + type: object + x-kubernetes-validations: + - message: prefix must be set if prefixPolicy is 'Prefix', + but must remain unset otherwise + rule: 'has(self.prefixPolicy) && self.prefixPolicy == + ''Prefix'' ? (has(self.prefix) && size(self.prefix.prefixString) + > 0) : !has(self.prefix)' + required: + - username + type: object + claimValidationRules: + description: |- + claimValidationRules is an optional field that configures the rules to + be used by the Kubernetes API server for validating the claims in a JWT + token issued by the identity provider. + + Validation rules are joined via an AND operation. + items: + description: |- + TokenClaimValidationRule represents a validation rule based on token claims. + If type is RequiredClaim, requiredClaim must be set. + If type is Expression, expressionRule must be set. + properties: + expressionRule: + description: |- + expressionRule contains the configuration for the "Expression" type. + Must be set if type == "Expression". + properties: + expression: + description: |- + expression is a CEL expression evaluated against token claims. + The expression must be a non-empty string and no longer than 4096 characters. + This field is required. + maxLength: 4096 + minLength: 1 + type: string + message: + description: |- + message allows configuring the human-readable message that is returned + from the Kubernetes API server when a token fails validation based on + the CEL expression defined in 'expression'. This field is optional. + maxLength: 256 + minLength: 1 + type: string + required: + - expression + type: object + requiredClaim: + description: |- + requiredClaim allows configuring a required claim name and its expected value. + RequiredClaim is used when type is RequiredClaim. + properties: + claim: + description: claim is a name of a required claim. + Only claims with string values are supported. + minLength: 1 + type: string + requiredValue: + description: |- + requiredValue is a required field that configures the value that 'claim' must + have when taken from the incoming JWT claims. + If the value in the JWT claims does not match, the token + will be rejected for authentication. + + requiredValue must not be an empty string (""). + minLength: 1 + type: string + required: + - claim + - requiredValue + type: object + type: + allOf: + - enum: + - RequiredClaim + - Expression + - enum: + - RequiredClaim + - Expression + default: RequiredClaim + description: |- + type is an optional field that configures the type of the validation rule. + + Allowed values are "RequiredClaim" and "Expression". + + When set to 'RequiredClaim', the Kubernetes API server will be configured + to validate that the incoming JWT contains the required claim and that its + value matches the required value. + + When set to 'Expression', the Kubernetes API server will be configured + to validate the incoming JWT against the configured CEL expression. + + Defaults to "RequiredClaim". + type: string + type: object + x-kubernetes-validations: + - message: expressionRule must be set when type is 'Expression', + and forbidden otherwise + rule: 'has(self.type) && self.type == ''Expression'' ? has(self.expressionRule) + : !has(self.expressionRule)' + - message: requiredClaim must be set when type is 'RequiredClaim', + and forbidden otherwise + rule: 'has(self.type) && self.type == ''RequiredClaim'' + ? has(self.requiredClaim) : !has(self.requiredClaim)' + type: array + x-kubernetes-list-type: atomic + issuer: + description: |- + issuer is a required field that configures how the platform interacts + with the identity provider and how tokens issued from the identity provider + are evaluated by the Kubernetes API server. + properties: + audienceMatchPolicy: + description: |- + audienceMatchPolicy specifies how token audiences are matched. + If omitted, the system applies a default policy. + Valid values are: + - "MatchAny": The token is accepted if any of its audiences match any of the configured audiences. + enum: + - MatchAny + - "" + type: string + audiences: + description: |- + audiences is a required field that configures the acceptable audiences + the JWT token, issued by the identity provider, must be issued to. + At least one of the entries must match the 'aud' claim in the JWT token. + + audiences must contain at least one entry and must not exceed ten entries. + items: + minLength: 1 + type: string + maxItems: 10 + minItems: 1 + type: array + x-kubernetes-list-type: set + discoveryURL: + description: |- + discoveryURL is an optional field that, if specified, overrides the default discovery endpoint + used to retrieve OIDC configuration metadata. By default, the discovery URL is derived from `url` + as "{url}/.well-known/openid-configuration". + + The discoveryURL must: + - Be a valid absolute URL. + - Use the HTTPS scheme. + - Not contain query parameters, user info, or fragments. + - Be different from the value of `url` (ignoring trailing slashes) + maxLength: 2048 + type: string + x-kubernetes-validations: + - message: discoveryURL must be a valid URL + rule: 'self.size() > 0 ? isURL(self) : true' + - message: discoveryURL must be a valid https URL + rule: 'self.size() > 0 ? (isURL(self) && url(self).getScheme() + == ''https'') : true' + - message: discoveryURL must not contain query parameters + rule: self.matches('^[^?]*$') + - message: discoveryURL must not contain fragments + rule: self.matches('^[^#]*$') + - message: discoveryURL must not contain user info + rule: self.matches('^[^@]*$') + issuerCertificateAuthority: + description: |- + issuerCertificateAuthority is an optional field that configures the + certificate authority, used by the Kubernetes API server, to validate + the connection to the identity provider when fetching discovery information. + + When not specified, the system trust is used. + + When specified, it must reference a ConfigMap in the openshift-config + namespace containing the PEM-encoded CA certificates under the 'ca-bundle.crt' + key in the data field of the ConfigMap. + properties: + name: + description: name is the metadata.name of the referenced + config map + type: string + required: + - name + type: object + issuerURL: + description: |- + issuerURL is a required field that configures the URL used to issue tokens + by the identity provider. + The Kubernetes API server determines how authentication tokens should be handled + by matching the 'iss' claim in the JWT to the issuerURL of configured identity providers. + + Must be at least 1 character and must not exceed 512 characters in length. + Must be a valid URL that uses the 'https' scheme and does not contain a query, fragment or user. + maxLength: 512 + minLength: 1 + type: string + x-kubernetes-validations: + - message: must be a valid URL + rule: isURL(self) + - message: must use the 'https' scheme + rule: isURL(self) && url(self).getScheme() == 'https' + - message: must not have a query + rule: isURL(self) && url(self).getQuery() == {} + - message: must not have a fragment + rule: self.find('#(.+)$') == '' + - message: must not have user info + rule: self.find('@') == '' + required: + - audiences + - issuerURL + type: object + x-kubernetes-validations: + - message: discoveryURL must be different from issuerURL + rule: 'self.?discoveryURL.orValue("").size() > 0 ? (self.issuerURL.size() + == 0 || self.discoveryURL.find(''^.+[^/]'') != self.issuerURL.find(''^.+[^/]'')) + : true' + name: + description: |- + name is a required field that configures the unique human-readable identifier + associated with the identity provider. + It is used to distinguish between multiple identity providers + and has no impact on token validation or authentication mechanics. + + name must not be an empty string (""). + minLength: 1 + type: string + oidcClients: + description: |- + oidcClients is an optional field that configures how on-cluster, + platform clients should request tokens from the identity provider. + oidcClients must not exceed 20 entries and entries must have unique namespace/name pairs. + items: + description: |- + OIDCClientConfig configures how platform clients + interact with identity providers as an authentication + method + properties: + clientID: + description: |- + clientID is a required field that configures the client identifier, from + the identity provider, that the platform component uses for authentication + requests made to the identity provider. + The identity provider must accept this identifier for platform components + to be able to use the identity provider as an authentication mode. + + clientID must not be an empty string (""). + minLength: 1 + type: string + clientSecret: + description: |- + clientSecret is an optional field that configures the client secret used + by the platform component when making authentication requests to the identity provider. + + When not specified, no client secret will be used when making authentication requests + to the identity provider. + + When specified, clientSecret references a Secret in the 'openshift-config' + namespace that contains the client secret in the 'clientSecret' key of the '.data' field. + The client secret will be used when making authentication requests to the identity provider. + + Public clients do not require a client secret but private + clients do require a client secret to work with the identity provider. + properties: + name: + description: name is the metadata.name of the referenced + secret + type: string + required: + - name + type: object + componentName: + description: |- + componentName is a required field that specifies the name of the platform + component being configured to use the identity provider as an authentication mode. + It is used in combination with componentNamespace as a unique identifier. + + componentName must not be an empty string ("") and must not exceed 256 characters in length. + maxLength: 256 + minLength: 1 + type: string + componentNamespace: + description: |- + componentNamespace is a required field that specifies the namespace in which the + platform component being configured to use the identity provider as an authentication + mode is running. + It is used in combination with componentName as a unique identifier. + + componentNamespace must not be an empty string ("") and must not exceed 63 characters in length. + maxLength: 63 + minLength: 1 + type: string + extraScopes: + description: |- + extraScopes is an optional field that configures the extra scopes that should + be requested by the platform component when making authentication requests to the + identity provider. + This is useful if you have configured claim mappings that requires specific + scopes to be requested beyond the standard OIDC scopes. + + When omitted, no additional scopes are requested. + items: + type: string + type: array + x-kubernetes-list-type: set + required: + - clientID + - componentName + - componentNamespace + type: object + maxItems: 20 + type: array + x-kubernetes-list-map-keys: + - componentNamespace + - componentName + x-kubernetes-list-type: map + userValidationRules: + description: |- + userValidationRules defines the set of rules used to validate claims in a user’s token. + These rules determine whether a token subject is considered valid based on its claims. + Each rule is evaluated independently. + See the TokenUserValidationRule type for more information on rule structure. + items: + description: |- + TokenUserValidationRule provides a CEL-based rule used to validate a token subject. + Each rule contains a CEL expression that is evaluated against the token’s claims. + properties: + expression: + description: |- + expression is a CEL expression that must evaluate + to true for the token to be accepted. The expression is evaluated against the token's + user information (e.g., username, groups). + + If the expression evaluates to false, the token is rejected. + See https://kubernetes.io/docs/reference/using-api/cel/ for CEL syntax. + At least one rule must evaluate to true for the token to be considered valid. + + This field must be non-empty and may not exceed 4096 characters. + maxLength: 4096 + minLength: 1 + type: string + message: + description: |- + message is an optional, human-readable message returned by the API server when + this validation rule fails. It can help clarify why a token was rejected. + maxLength: 256 + minLength: 1 + type: string + required: + - expression + type: object + maxItems: 64 + type: array + x-kubernetes-list-type: atomic + required: + - claimMappings + - issuer + - name + type: object + maxItems: 1 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + serviceAccountIssuer: + description: |- + serviceAccountIssuer is the identifier of the bound service account token + issuer. + The default is https://kubernetes.default.svc + WARNING: Updating this field will not result in immediate invalidation of all bound tokens with the + previous issuer value. Instead, the tokens issued by previous service account issuer will continue to + be trusted for a time period chosen by the platform (currently set to 24h). + This time period is subject to change over time. + This allows internal components to transition to use new service account issuer without service distruption. + type: string + type: + description: |- + type identifies the cluster managed, user facing authentication mode in use. + Specifically, it manages the component that responds to login attempts. + The default is IntegratedOAuth. + type: string + webhookTokenAuthenticator: + description: |- + webhookTokenAuthenticator configures a remote token reviewer. + These remote authentication webhooks can be used to verify bearer tokens + via the tokenreviews.authentication.k8s.io REST API. This is required to + honor bearer tokens that are provisioned by an external authentication service. + + Can only be set if "Type" is set to "None". + properties: + kubeConfig: + description: |- + kubeConfig references a secret that contains kube config file data which + describes how to access the remote webhook service. + The namespace for the referenced secret is openshift-config. + + For further details, see: + + https://kubernetes.io/docs/reference/access-authn-authz/authentication/#webhook-token-authentication + + The key "kubeConfig" is used to locate the data. + If the secret or expected key is not found, the webhook is not honored. + If the specified kube config data is not valid, the webhook is not honored. + properties: + name: + description: name is the metadata.name of the referenced secret + type: string + required: + - name + type: object + required: + - kubeConfig + type: object + webhookTokenAuthenticators: + description: webhookTokenAuthenticators is DEPRECATED, setting it + has no effect. + items: + description: |- + deprecatedWebhookTokenAuthenticator holds the necessary configuration options for a remote token authenticator. + It's the same as WebhookTokenAuthenticator but it's missing the 'required' validation on KubeConfig field. + properties: + kubeConfig: + description: |- + kubeConfig contains kube config file data which describes how to access the remote webhook service. + For further details, see: + https://kubernetes.io/docs/reference/access-authn-authz/authentication/#webhook-token-authentication + The key "kubeConfig" is used to locate the data. + If the secret or expected key is not found, the webhook is not honored. + If the specified kube config data is not valid, the webhook is not honored. + The namespace for this secret is determined by the point of use. + properties: + name: + description: name is the metadata.name of the referenced + secret + type: string + required: + - name + type: object + type: object + type: array + x-kubernetes-list-type: atomic + type: object + status: + description: status holds observed values from the cluster. They may not + be overridden. + properties: + integratedOAuthMetadata: + description: |- + integratedOAuthMetadata contains the discovery endpoint data for OAuth 2.0 + Authorization Server Metadata for the in-cluster integrated OAuth server. + This discovery document can be viewed from its served location: + oc get --raw '/.well-known/oauth-authorization-server' + For further details, see the IETF Draft: + https://tools.ietf.org/html/draft-ietf-oauth-discovery-04#section-2 + This contains the observed value based on cluster state. + An explicitly set value in spec.oauthMetadata has precedence over this field. + This field has no meaning if authentication spec.type is not set to IntegratedOAuth. + The key "oauthMetadata" is used to locate the data. + If the config map or expected key is not found, no metadata is served. + If the specified metadata is not valid, no metadata is served. + The namespace for this config map is openshift-config-managed. + properties: + name: + description: name is the metadata.name of the referenced config + map + type: string + required: + - name + type: object + type: object + required: + - spec + type: object + x-kubernetes-validations: + - message: all oidcClients in the oidcProviders must match their componentName + and componentNamespace to either a previously configured oidcClient or + they must exist in the status.oidcClients + rule: '!has(self.spec.oidcProviders) || self.spec.oidcProviders.all(p, !has(p.oidcClients) + || p.oidcClients.all(specC, self.status.oidcClients.exists(statusC, statusC.componentNamespace + == specC.componentNamespace && statusC.componentName == specC.componentName) + || (has(oldSelf.spec.oidcProviders) && oldSelf.spec.oidcProviders.exists(oldP, + oldP.name == p.name && has(oldP.oidcClients) && oldP.oidcClients.exists(oldC, + oldC.componentNamespace == specC.componentNamespace && oldC.componentName + == specC.componentName)))))' + served: true + storage: true + subresources: + status: {} diff --git a/config/v1/zz_generated.swagger_doc_generated.go b/config/v1/zz_generated.swagger_doc_generated.go index e3494151c66..2066dabc911 100644 --- a/config/v1/zz_generated.swagger_doc_generated.go +++ b/config/v1/zz_generated.swagger_doc_generated.go @@ -449,6 +449,7 @@ var map_OIDCProvider = map[string]string{ "oidcClients": "oidcClients is an optional field that configures how on-cluster, platform clients should request tokens from the identity provider. oidcClients must not exceed 20 entries and entries must have unique namespace/name pairs.", "claimMappings": "claimMappings is a required field that configures the rules to be used by the Kubernetes API server for translating claims in a JWT token, issued by the identity provider, to a cluster identity.", "claimValidationRules": "claimValidationRules is an optional field that configures the rules to be used by the Kubernetes API server for validating the claims in a JWT token issued by the identity provider.\n\nValidation rules are joined via an AND operation.", + "userValidationRules": "userValidationRules defines the set of rules used to validate claims in a user’s token. These rules determine whether a token subject is considered valid based on its claims. Each rule is evaluated independently. See the TokenUserValidationRule type for more information on rule structure.", } func (OIDCProvider) SwaggerDoc() map[string]string { @@ -495,18 +496,31 @@ func (TokenClaimOrExpressionMapping) SwaggerDoc() map[string]string { } var map_TokenClaimValidationRule = map[string]string{ - "type": "type is an optional field that configures the type of the validation rule.\n\nAllowed values are 'RequiredClaim' and omitted (not provided or an empty string).\n\nWhen set to 'RequiredClaim', the Kubernetes API server will be configured to validate that the incoming JWT contains the required claim and that its value matches the required value.\n\nDefaults to 'RequiredClaim'.", - "requiredClaim": "requiredClaim is an optional field that configures the required claim and value that the Kubernetes API server will use to validate if an incoming JWT is valid for this identity provider.", + "": "TokenClaimValidationRule represents a validation rule based on token claims. If type is RequiredClaim, requiredClaim must be set. If type is Expression, expressionRule must be set.", + "type": "type is an optional field that configures the type of the validation rule.\n\nAllowed values are \"RequiredClaim\" and \"Expression\".\n\nWhen set to 'RequiredClaim', the Kubernetes API server will be configured to validate that the incoming JWT contains the required claim and that its value matches the required value.\n\nWhen set to 'Expression', the Kubernetes API server will be configured to validate the incoming JWT against the configured CEL expression.\n\nDefaults to \"RequiredClaim\".", + "requiredClaim": "requiredClaim allows configuring a required claim name and its expected value. RequiredClaim is used when type is RequiredClaim.", + "expressionRule": "expressionRule contains the configuration for the \"Expression\" type. Must be set if type == \"Expression\".", } func (TokenClaimValidationRule) SwaggerDoc() map[string]string { return map_TokenClaimValidationRule } +var map_TokenExpressionRule = map[string]string{ + "expression": "expression is a CEL expression evaluated against token claims. The expression must be a non-empty string and no longer than 4096 characters. This field is required.", + "message": "message allows configuring the human-readable message that is returned from the Kubernetes API server when a token fails validation based on the CEL expression defined in 'expression'. This field is optional.", +} + +func (TokenExpressionRule) SwaggerDoc() map[string]string { + return map_TokenExpressionRule +} + var map_TokenIssuer = map[string]string{ "issuerURL": "issuerURL is a required field that configures the URL used to issue tokens by the identity provider. The Kubernetes API server determines how authentication tokens should be handled by matching the 'iss' claim in the JWT to the issuerURL of configured identity providers.\n\nMust be at least 1 character and must not exceed 512 characters in length. Must be a valid URL that uses the 'https' scheme and does not contain a query, fragment or user.", "audiences": "audiences is a required field that configures the acceptable audiences the JWT token, issued by the identity provider, must be issued to. At least one of the entries must match the 'aud' claim in the JWT token.\n\naudiences must contain at least one entry and must not exceed ten entries.", "issuerCertificateAuthority": "issuerCertificateAuthority is an optional field that configures the certificate authority, used by the Kubernetes API server, to validate the connection to the identity provider when fetching discovery information.\n\nWhen not specified, the system trust is used.\n\nWhen specified, it must reference a ConfigMap in the openshift-config namespace containing the PEM-encoded CA certificates under the 'ca-bundle.crt' key in the data field of the ConfigMap.", + "discoveryURL": "discoveryURL is an optional field that, if specified, overrides the default discovery endpoint used to retrieve OIDC configuration metadata. By default, the discovery URL is derived from `url` as \"{url}/.well-known/openid-configuration\".\n\nThe discoveryURL must:\n - Be a valid absolute URL.\n - Use the HTTPS scheme.\n - Not contain query parameters, user info, or fragments.\n - Be different from the value of `url` (ignoring trailing slashes)", + "audienceMatchPolicy": "audienceMatchPolicy specifies how token audiences are matched. If omitted, the system applies a default policy. Valid values are: - \"MatchAny\": The token is accepted if any of its audiences match any of the configured audiences.", } func (TokenIssuer) SwaggerDoc() map[string]string { @@ -514,7 +528,7 @@ func (TokenIssuer) SwaggerDoc() map[string]string { } var map_TokenRequiredClaim = map[string]string{ - "claim": "claim is a required field that configures the name of the required claim. When taken from the JWT claims, claim must be a string value.\n\nclaim must not be an empty string (\"\").", + "claim": "claim is a name of a required claim. Only claims with string values are supported.", "requiredValue": "requiredValue is a required field that configures the value that 'claim' must have when taken from the incoming JWT claims. If the value in the JWT claims does not match, the token will be rejected for authentication.\n\nrequiredValue must not be an empty string (\"\").", } @@ -522,6 +536,16 @@ func (TokenRequiredClaim) SwaggerDoc() map[string]string { return map_TokenRequiredClaim } +var map_TokenUserValidationRule = map[string]string{ + "": "TokenUserValidationRule provides a CEL-based rule used to validate a token subject. Each rule contains a CEL expression that is evaluated against the token’s claims.", + "expression": "expression is a CEL expression that must evaluate to true for the token to be accepted. The expression is evaluated against the token's user information (e.g., username, groups).\n\nIf the expression evaluates to false, the token is rejected. See https://kubernetes.io/docs/reference/using-api/cel/ for CEL syntax. At least one rule must evaluate to true for the token to be considered valid.\n\nThis field must be non-empty and may not exceed 4096 characters.", + "message": "message is an optional, human-readable message returned by the API server when this validation rule fails. It can help clarify why a token was rejected.", +} + +func (TokenUserValidationRule) SwaggerDoc() map[string]string { + return map_TokenUserValidationRule +} + var map_UsernameClaimMapping = map[string]string{ "claim": "claim is a required field that configures the JWT token claim whose value is assigned to the cluster identity field associated with this mapping.\n\nclaim must not be an empty string (\"\") and must not exceed 256 characters.", "prefixPolicy": "prefixPolicy is an optional field that configures how a prefix should be applied to the value of the JWT claim specified in the 'claim' field.\n\nAllowed values are 'Prefix', 'NoPrefix', and omitted (not provided or an empty string).\n\nWhen set to 'Prefix', the value specified in the prefix field will be prepended to the value of the JWT claim. The prefix field must be set when prefixPolicy is 'Prefix'.\n\nWhen set to 'NoPrefix', no prefix will be prepended to the value of the JWT claim.\n\nWhen omitted, this means no opinion and the platform is left to choose any prefixes that are applied which is subject to change over time. Currently, the platform prepends `{issuerURL}#` to the value of the JWT claim when the claim is not 'email'. As an example, consider the following scenario:\n `prefix` is unset, `issuerURL` is set to `https://myoidc.tld`,\n the JWT claims include \"username\":\"userA\" and \"email\":\"userA@myoidc.tld\",\n and `claim` is set to:\n - \"username\": the mapped value will be \"https://myoidc.tld#userA\"\n - \"email\": the mapped value will be \"userA@myoidc.tld\"", diff --git a/features.md b/features.md index 05172942208..81d9212c68e 100644 --- a/features.md +++ b/features.md @@ -40,6 +40,7 @@ | EtcdBackendQuota| | | Enabled | Enabled | Enabled | Enabled | | Example| | | Enabled | Enabled | Enabled | Enabled | | ExternalOIDCWithUIDAndExtraClaimMappings| | | Enabled | Enabled | Enabled | Enabled | +| ExternalOIDCWithUpstreamParity| | | Enabled | Enabled | Enabled | Enabled | | GCPClusterHostedDNS| | | Enabled | Enabled | Enabled | Enabled | | GCPCustomAPIEndpoints| | | Enabled | Enabled | Enabled | Enabled | | GCPCustomAPIEndpointsInstall| | | Enabled | Enabled | Enabled | Enabled | diff --git a/features/features.go b/features/features.go index 231bad62b67..50bfaa0f014 100644 --- a/features/features.go +++ b/features/features.go @@ -477,6 +477,15 @@ var ( enableForClusterProfile(Hypershift, configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). mustRegister() + FeatureGateExternalOIDCWithUpstreamParity = newFeatureGate("ExternalOIDCWithUpstreamParity"). + reportProblemsToJiraComponent("authentication"). + contactPerson("saldawam"). + productScope(ocpSpecific). + enhancementPR("https://github.com/openshift/enhancements/pull/1763"). + enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enableForClusterProfile(Hypershift, configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + mustRegister() + FeatureGateExample = newFeatureGate("Example"). reportProblemsToJiraComponent("cluster-config"). contactPerson("deads"). diff --git a/openapi/generated_openapi/zz_generated.openapi.go b/openapi/generated_openapi/zz_generated.openapi.go index 77124efe23c..89bb27e3757 100644 --- a/openapi/generated_openapi/zz_generated.openapi.go +++ b/openapi/generated_openapi/zz_generated.openapi.go @@ -404,8 +404,10 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/openshift/api/config/v1.TokenClaimOrExpressionMapping": schema_openshift_api_config_v1_TokenClaimOrExpressionMapping(ref), "github.com/openshift/api/config/v1.TokenClaimValidationRule": schema_openshift_api_config_v1_TokenClaimValidationRule(ref), "github.com/openshift/api/config/v1.TokenConfig": schema_openshift_api_config_v1_TokenConfig(ref), + "github.com/openshift/api/config/v1.TokenExpressionRule": schema_openshift_api_config_v1_TokenExpressionRule(ref), "github.com/openshift/api/config/v1.TokenIssuer": schema_openshift_api_config_v1_TokenIssuer(ref), "github.com/openshift/api/config/v1.TokenRequiredClaim": schema_openshift_api_config_v1_TokenRequiredClaim(ref), + "github.com/openshift/api/config/v1.TokenUserValidationRule": schema_openshift_api_config_v1_TokenUserValidationRule(ref), "github.com/openshift/api/config/v1.Update": schema_openshift_api_config_v1_Update(ref), "github.com/openshift/api/config/v1.UpdateHistory": schema_openshift_api_config_v1_UpdateHistory(ref), "github.com/openshift/api/config/v1.UsernameClaimMapping": schema_openshift_api_config_v1_UsernameClaimMapping(ref), @@ -17141,12 +17143,31 @@ func schema_openshift_api_config_v1_OIDCProvider(ref common.ReferenceCallback) c }, }, }, + "userValidationRules": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "userValidationRules defines the set of rules used to validate claims in a user’s token. These rules determine whether a token subject is considered valid based on its claims. Each rule is evaluated independently. See the TokenUserValidationRule type for more information on rule structure.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/openshift/api/config/v1.TokenUserValidationRule"), + }, + }, + }, + }, + }, }, Required: []string{"name", "issuer", "claimMappings"}, }, }, Dependencies: []string{ - "github.com/openshift/api/config/v1.OIDCClientConfig", "github.com/openshift/api/config/v1.TokenClaimMappings", "github.com/openshift/api/config/v1.TokenClaimValidationRule", "github.com/openshift/api/config/v1.TokenIssuer"}, + "github.com/openshift/api/config/v1.OIDCClientConfig", "github.com/openshift/api/config/v1.TokenClaimMappings", "github.com/openshift/api/config/v1.TokenClaimValidationRule", "github.com/openshift/api/config/v1.TokenIssuer", "github.com/openshift/api/config/v1.TokenUserValidationRule"}, } } @@ -20184,29 +20205,35 @@ func schema_openshift_api_config_v1_TokenClaimValidationRule(ref common.Referenc return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, + Description: "TokenClaimValidationRule represents a validation rule based on token claims. If type is RequiredClaim, requiredClaim must be set. If type is Expression, expressionRule must be set.", + Type: []string{"object"}, Properties: map[string]spec.Schema{ "type": { SchemaProps: spec.SchemaProps{ - Description: "type is an optional field that configures the type of the validation rule.\n\nAllowed values are 'RequiredClaim' and omitted (not provided or an empty string).\n\nWhen set to 'RequiredClaim', the Kubernetes API server will be configured to validate that the incoming JWT contains the required claim and that its value matches the required value.\n\nDefaults to 'RequiredClaim'.", + Description: "type is an optional field that configures the type of the validation rule.\n\nAllowed values are \"RequiredClaim\" and \"Expression\".\n\nWhen set to 'RequiredClaim', the Kubernetes API server will be configured to validate that the incoming JWT contains the required claim and that its value matches the required value.\n\nWhen set to 'Expression', the Kubernetes API server will be configured to validate the incoming JWT against the configured CEL expression.\n\nDefaults to \"RequiredClaim\".", Default: "", Type: []string{"string"}, Format: "", - Enum: []interface{}{}, }, }, "requiredClaim": { SchemaProps: spec.SchemaProps{ - Description: "requiredClaim is an optional field that configures the required claim and value that the Kubernetes API server will use to validate if an incoming JWT is valid for this identity provider.", + Description: "requiredClaim allows configuring a required claim name and its expected value. RequiredClaim is used when type is RequiredClaim.", Ref: ref("github.com/openshift/api/config/v1.TokenRequiredClaim"), }, }, + "expressionRule": { + SchemaProps: spec.SchemaProps{ + Description: "expressionRule contains the configuration for the \"Expression\" type. Must be set if type == \"Expression\".", + Ref: ref("github.com/openshift/api/config/v1.TokenExpressionRule"), + }, + }, }, Required: []string{"type"}, }, }, Dependencies: []string{ - "github.com/openshift/api/config/v1.TokenRequiredClaim"}, + "github.com/openshift/api/config/v1.TokenExpressionRule", "github.com/openshift/api/config/v1.TokenRequiredClaim"}, } } @@ -20245,6 +20272,33 @@ func schema_openshift_api_config_v1_TokenConfig(ref common.ReferenceCallback) co } } +func schema_openshift_api_config_v1_TokenExpressionRule(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "expression": { + SchemaProps: spec.SchemaProps{ + Description: "expression is a CEL expression evaluated against token claims. The expression must be a non-empty string and no longer than 4096 characters. This field is required.", + Type: []string{"string"}, + Format: "", + }, + }, + "message": { + SchemaProps: spec.SchemaProps{ + Description: "message allows configuring the human-readable message that is returned from the Kubernetes API server when a token fails validation based on the CEL expression defined in 'expression'. This field is optional.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"expression"}, + }, + }, + } +} + func schema_openshift_api_config_v1_TokenIssuer(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -20286,6 +20340,20 @@ func schema_openshift_api_config_v1_TokenIssuer(ref common.ReferenceCallback) co Ref: ref("github.com/openshift/api/config/v1.ConfigMapNameReference"), }, }, + "discoveryURL": { + SchemaProps: spec.SchemaProps{ + Description: "discoveryURL is an optional field that, if specified, overrides the default discovery endpoint used to retrieve OIDC configuration metadata. By default, the discovery URL is derived from `url` as \"{url}/.well-known/openid-configuration\".\n\nThe discoveryURL must:\n - Be a valid absolute URL.\n - Use the HTTPS scheme.\n - Not contain query parameters, user info, or fragments.\n - Be different from the value of `url` (ignoring trailing slashes)", + Type: []string{"string"}, + Format: "", + }, + }, + "audienceMatchPolicy": { + SchemaProps: spec.SchemaProps{ + Description: "audienceMatchPolicy specifies how token audiences are matched. If omitted, the system applies a default policy. Valid values are: - \"MatchAny\": The token is accepted if any of its audiences match any of the configured audiences.", + Type: []string{"string"}, + Format: "", + }, + }, }, Required: []string{"issuerURL", "audiences"}, }, @@ -20303,7 +20371,7 @@ func schema_openshift_api_config_v1_TokenRequiredClaim(ref common.ReferenceCallb Properties: map[string]spec.Schema{ "claim": { SchemaProps: spec.SchemaProps{ - Description: "claim is a required field that configures the name of the required claim. When taken from the JWT claims, claim must be a string value.\n\nclaim must not be an empty string (\"\").", + Description: "claim is a name of a required claim. Only claims with string values are supported.", Default: "", Type: []string{"string"}, Format: "", @@ -20324,6 +20392,34 @@ func schema_openshift_api_config_v1_TokenRequiredClaim(ref common.ReferenceCallb } } +func schema_openshift_api_config_v1_TokenUserValidationRule(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TokenUserValidationRule provides a CEL-based rule used to validate a token subject. Each rule contains a CEL expression that is evaluated against the token’s claims.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "expression": { + SchemaProps: spec.SchemaProps{ + Description: "expression is a CEL expression that must evaluate to true for the token to be accepted. The expression is evaluated against the token's user information (e.g., username, groups).\n\nIf the expression evaluates to false, the token is rejected. See https://kubernetes.io/docs/reference/using-api/cel/ for CEL syntax. At least one rule must evaluate to true for the token to be considered valid.\n\nThis field must be non-empty and may not exceed 4096 characters.", + Type: []string{"string"}, + Format: "", + }, + }, + "message": { + SchemaProps: spec.SchemaProps{ + Description: "message is an optional, human-readable message returned by the API server when this validation rule fails. It can help clarify why a token was rejected.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"expression"}, + }, + }, + } +} + func schema_openshift_api_config_v1_Update(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ diff --git a/openapi/openapi.json b/openapi/openapi.json index 169d0ceac1c..0305ed562bb 100644 --- a/openapi/openapi.json +++ b/openapi/openapi.json @@ -9152,6 +9152,15 @@ "componentName" ], "x-kubernetes-list-type": "map" + }, + "userValidationRules": { + "description": "userValidationRules defines the set of rules used to validate claims in a user’s token. These rules determine whether a token subject is considered valid based on its claims. Each rule is evaluated independently. See the TokenUserValidationRule type for more information on rule structure.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/com.github.openshift.api.config.v1.TokenUserValidationRule" + }, + "x-kubernetes-list-type": "atomic" } } }, @@ -10937,17 +10946,22 @@ } }, "com.github.openshift.api.config.v1.TokenClaimValidationRule": { + "description": "TokenClaimValidationRule represents a validation rule based on token claims. If type is RequiredClaim, requiredClaim must be set. If type is Expression, expressionRule must be set.", "type": "object", "required": [ "type" ], "properties": { + "expressionRule": { + "description": "expressionRule contains the configuration for the \"Expression\" type. Must be set if type == \"Expression\".", + "$ref": "#/definitions/com.github.openshift.api.config.v1.TokenExpressionRule" + }, "requiredClaim": { - "description": "requiredClaim is an optional field that configures the required claim and value that the Kubernetes API server will use to validate if an incoming JWT is valid for this identity provider.", + "description": "requiredClaim allows configuring a required claim name and its expected value. RequiredClaim is used when type is RequiredClaim.", "$ref": "#/definitions/com.github.openshift.api.config.v1.TokenRequiredClaim" }, "type": { - "description": "type is an optional field that configures the type of the validation rule.\n\nAllowed values are 'RequiredClaim' and omitted (not provided or an empty string).\n\nWhen set to 'RequiredClaim', the Kubernetes API server will be configured to validate that the incoming JWT contains the required claim and that its value matches the required value.\n\nDefaults to 'RequiredClaim'.", + "description": "type is an optional field that configures the type of the validation rule.\n\nAllowed values are \"RequiredClaim\" and \"Expression\".\n\nWhen set to 'RequiredClaim', the Kubernetes API server will be configured to validate that the incoming JWT contains the required claim and that its value matches the required value.\n\nWhen set to 'Expression', the Kubernetes API server will be configured to validate the incoming JWT against the configured CEL expression.\n\nDefaults to \"RequiredClaim\".", "type": "string", "default": "" } @@ -10973,6 +10987,22 @@ } } }, + "com.github.openshift.api.config.v1.TokenExpressionRule": { + "type": "object", + "required": [ + "expression" + ], + "properties": { + "expression": { + "description": "expression is a CEL expression evaluated against token claims. The expression must be a non-empty string and no longer than 4096 characters. This field is required.", + "type": "string" + }, + "message": { + "description": "message allows configuring the human-readable message that is returned from the Kubernetes API server when a token fails validation based on the CEL expression defined in 'expression'. This field is optional.", + "type": "string" + } + } + }, "com.github.openshift.api.config.v1.TokenIssuer": { "type": "object", "required": [ @@ -10980,6 +11010,10 @@ "audiences" ], "properties": { + "audienceMatchPolicy": { + "description": "audienceMatchPolicy specifies how token audiences are matched. If omitted, the system applies a default policy. Valid values are: - \"MatchAny\": The token is accepted if any of its audiences match any of the configured audiences.", + "type": "string" + }, "audiences": { "description": "audiences is a required field that configures the acceptable audiences the JWT token, issued by the identity provider, must be issued to. At least one of the entries must match the 'aud' claim in the JWT token.\n\naudiences must contain at least one entry and must not exceed ten entries.", "type": "array", @@ -10989,6 +11023,10 @@ }, "x-kubernetes-list-type": "set" }, + "discoveryURL": { + "description": "discoveryURL is an optional field that, if specified, overrides the default discovery endpoint used to retrieve OIDC configuration metadata. By default, the discovery URL is derived from `url` as \"{url}/.well-known/openid-configuration\".\n\nThe discoveryURL must:\n - Be a valid absolute URL.\n - Use the HTTPS scheme.\n - Not contain query parameters, user info, or fragments.\n - Be different from the value of `url` (ignoring trailing slashes)", + "type": "string" + }, "issuerCertificateAuthority": { "description": "issuerCertificateAuthority is an optional field that configures the certificate authority, used by the Kubernetes API server, to validate the connection to the identity provider when fetching discovery information.\n\nWhen not specified, the system trust is used.\n\nWhen specified, it must reference a ConfigMap in the openshift-config namespace containing the PEM-encoded CA certificates under the 'ca-bundle.crt' key in the data field of the ConfigMap.", "default": {}, @@ -11009,7 +11047,7 @@ ], "properties": { "claim": { - "description": "claim is a required field that configures the name of the required claim. When taken from the JWT claims, claim must be a string value.\n\nclaim must not be an empty string (\"\").", + "description": "claim is a name of a required claim. Only claims with string values are supported.", "type": "string", "default": "" }, @@ -11020,6 +11058,23 @@ } } }, + "com.github.openshift.api.config.v1.TokenUserValidationRule": { + "description": "TokenUserValidationRule provides a CEL-based rule used to validate a token subject. Each rule contains a CEL expression that is evaluated against the token’s claims.", + "type": "object", + "required": [ + "expression" + ], + "properties": { + "expression": { + "description": "expression is a CEL expression that must evaluate to true for the token to be accepted. The expression is evaluated against the token's user information (e.g., username, groups).\n\nIf the expression evaluates to false, the token is rejected. See https://kubernetes.io/docs/reference/using-api/cel/ for CEL syntax. At least one rule must evaluate to true for the token to be considered valid.\n\nThis field must be non-empty and may not exceed 4096 characters.", + "type": "string" + }, + "message": { + "description": "message is an optional, human-readable message returned by the API server when this validation rule fails. It can help clarify why a token was rejected.", + "type": "string" + } + } + }, "com.github.openshift.api.config.v1.Update": { "description": "Update represents an administrator update request.", "type": "object", diff --git a/payload-manifests/crds/0000_10_config-operator_01_authentications-Hypershift-CustomNoUpgrade.crd.yaml b/payload-manifests/crds/0000_10_config-operator_01_authentications-Hypershift-CustomNoUpgrade.crd.yaml index 4f67bf9e0ca..eff7329eeff 100644 --- a/payload-manifests/crds/0000_10_config-operator_01_authentications-Hypershift-CustomNoUpgrade.crd.yaml +++ b/payload-manifests/crds/0000_10_config-operator_01_authentications-Hypershift-CustomNoUpgrade.crd.yaml @@ -347,19 +347,43 @@ spec: Validation rules are joined via an AND operation. items: + description: |- + TokenClaimValidationRule represents a validation rule based on token claims. + If type is RequiredClaim, requiredClaim must be set. + If type is Expression, expressionRule must be set. properties: + expressionRule: + description: |- + expressionRule contains the configuration for the "Expression" type. + Must be set if type == "Expression". + properties: + expression: + description: |- + expression is a CEL expression evaluated against token claims. + The expression must be a non-empty string and no longer than 4096 characters. + This field is required. + maxLength: 4096 + minLength: 1 + type: string + message: + description: |- + message allows configuring the human-readable message that is returned + from the Kubernetes API server when a token fails validation based on + the CEL expression defined in 'expression'. This field is optional. + maxLength: 256 + minLength: 1 + type: string + required: + - expression + type: object requiredClaim: description: |- - requiredClaim is an optional field that configures the required claim - and value that the Kubernetes API server will use to validate if an incoming - JWT is valid for this identity provider. + requiredClaim allows configuring a required claim name and its expected value. + RequiredClaim is used when type is RequiredClaim. properties: claim: - description: |- - claim is a required field that configures the name of the required claim. - When taken from the JWT claims, claim must be a string value. - - claim must not be an empty string (""). + description: claim is a name of a required claim. + Only claims with string values are supported. minLength: 1 type: string requiredValue: @@ -377,22 +401,38 @@ spec: - requiredValue type: object type: + allOf: + - enum: + - RequiredClaim + - Expression + - enum: + - RequiredClaim + - Expression default: RequiredClaim description: |- type is an optional field that configures the type of the validation rule. - Allowed values are 'RequiredClaim' and omitted (not provided or an empty string). + Allowed values are "RequiredClaim" and "Expression". - When set to 'RequiredClaim', the Kubernetes API server - will be configured to validate that the incoming JWT - contains the required claim and that its value matches - the required value. + When set to 'RequiredClaim', the Kubernetes API server will be configured + to validate that the incoming JWT contains the required claim and that its + value matches the required value. - Defaults to 'RequiredClaim'. - enum: - - RequiredClaim + When set to 'Expression', the Kubernetes API server will be configured + to validate the incoming JWT against the configured CEL expression. + + Defaults to "RequiredClaim". type: string type: object + x-kubernetes-validations: + - message: expressionRule must be set when type is 'Expression', + and forbidden otherwise + rule: 'has(self.type) && self.type == ''Expression'' ? has(self.expressionRule) + : !has(self.expressionRule)' + - message: requiredClaim must be set when type is 'RequiredClaim', + and forbidden otherwise + rule: 'has(self.type) && self.type == ''RequiredClaim'' + ? has(self.requiredClaim) : !has(self.requiredClaim)' type: array x-kubernetes-list-type: atomic issuer: @@ -401,6 +441,16 @@ spec: with the identity provider and how tokens issued from the identity provider are evaluated by the Kubernetes API server. properties: + audienceMatchPolicy: + description: |- + audienceMatchPolicy specifies how token audiences are matched. + If omitted, the system applies a default policy. + Valid values are: + - "MatchAny": The token is accepted if any of its audiences match any of the configured audiences. + enum: + - MatchAny + - "" + type: string audiences: description: |- audiences is a required field that configures the acceptable audiences @@ -415,6 +465,31 @@ spec: minItems: 1 type: array x-kubernetes-list-type: set + discoveryURL: + description: |- + discoveryURL is an optional field that, if specified, overrides the default discovery endpoint + used to retrieve OIDC configuration metadata. By default, the discovery URL is derived from `url` + as "{url}/.well-known/openid-configuration". + + The discoveryURL must: + - Be a valid absolute URL. + - Use the HTTPS scheme. + - Not contain query parameters, user info, or fragments. + - Be different from the value of `url` (ignoring trailing slashes) + maxLength: 2048 + type: string + x-kubernetes-validations: + - message: discoveryURL must be a valid URL + rule: 'self.size() > 0 ? isURL(self) : true' + - message: discoveryURL must be a valid https URL + rule: 'self.size() > 0 ? (isURL(self) && url(self).getScheme() + == ''https'') : true' + - message: discoveryURL must not contain query parameters + rule: self.matches('^[^?]*$') + - message: discoveryURL must not contain fragments + rule: self.matches('^[^#]*$') + - message: discoveryURL must not contain user info + rule: self.matches('^[^@]*$') issuerCertificateAuthority: description: |- issuerCertificateAuthority is an optional field that configures the @@ -461,6 +536,11 @@ spec: - audiences - issuerURL type: object + x-kubernetes-validations: + - message: discoveryURL must be different from issuerURL + rule: 'self.?discoveryURL.orValue("").size() > 0 ? (self.issuerURL.size() + == 0 || self.discoveryURL.find(''^.+[^/]'') != self.issuerURL.find(''^.+[^/]'')) + : true' name: description: |- name is a required field that configures the unique human-readable identifier @@ -560,6 +640,44 @@ spec: - componentNamespace - componentName x-kubernetes-list-type: map + userValidationRules: + description: |- + userValidationRules defines the set of rules used to validate claims in a user’s token. + These rules determine whether a token subject is considered valid based on its claims. + Each rule is evaluated independently. + See the TokenUserValidationRule type for more information on rule structure. + items: + description: |- + TokenUserValidationRule provides a CEL-based rule used to validate a token subject. + Each rule contains a CEL expression that is evaluated against the token’s claims. + properties: + expression: + description: |- + expression is a CEL expression that must evaluate + to true for the token to be accepted. The expression is evaluated against the token's + user information (e.g., username, groups). + + If the expression evaluates to false, the token is rejected. + See https://kubernetes.io/docs/reference/using-api/cel/ for CEL syntax. + At least one rule must evaluate to true for the token to be considered valid. + + This field must be non-empty and may not exceed 4096 characters. + maxLength: 4096 + minLength: 1 + type: string + message: + description: |- + message is an optional, human-readable message returned by the API server when + this validation rule fails. It can help clarify why a token was rejected. + maxLength: 256 + minLength: 1 + type: string + required: + - expression + type: object + maxItems: 64 + type: array + x-kubernetes-list-type: atomic required: - claimMappings - issuer diff --git a/payload-manifests/crds/0000_10_config-operator_01_authentications-Hypershift-Default.crd.yaml b/payload-manifests/crds/0000_10_config-operator_01_authentications-Hypershift-Default.crd.yaml index 2a3b60571cb..12189002007 100644 --- a/payload-manifests/crds/0000_10_config-operator_01_authentications-Hypershift-Default.crd.yaml +++ b/payload-manifests/crds/0000_10_config-operator_01_authentications-Hypershift-Default.crd.yaml @@ -196,19 +196,24 @@ spec: Validation rules are joined via an AND operation. items: + description: |- + TokenClaimValidationRule represents a validation rule based on token claims. + If type is RequiredClaim, requiredClaim must be set. + If type is Expression, expressionRule must be set. properties: + expressionRule: + description: |- + expressionRule contains the configuration for the "Expression" type. + Must be set if type == "Expression". + type: object requiredClaim: description: |- - requiredClaim is an optional field that configures the required claim - and value that the Kubernetes API server will use to validate if an incoming - JWT is valid for this identity provider. + requiredClaim allows configuring a required claim name and its expected value. + RequiredClaim is used when type is RequiredClaim. properties: claim: - description: |- - claim is a required field that configures the name of the required claim. - When taken from the JWT claims, claim must be a string value. - - claim must not be an empty string (""). + description: claim is a name of a required claim. + Only claims with string values are supported. minLength: 1 type: string requiredValue: @@ -226,22 +231,34 @@ spec: - requiredValue type: object type: + allOf: + - enum: + - RequiredClaim + - Expression + - enum: + - RequiredClaim + - Expression default: RequiredClaim description: |- type is an optional field that configures the type of the validation rule. - Allowed values are 'RequiredClaim' and omitted (not provided or an empty string). + Allowed values are "RequiredClaim" and "Expression". - When set to 'RequiredClaim', the Kubernetes API server - will be configured to validate that the incoming JWT - contains the required claim and that its value matches - the required value. + When set to 'RequiredClaim', the Kubernetes API server will be configured + to validate that the incoming JWT contains the required claim and that its + value matches the required value. - Defaults to 'RequiredClaim'. - enum: - - RequiredClaim + When set to 'Expression', the Kubernetes API server will be configured + to validate the incoming JWT against the configured CEL expression. + + Defaults to "RequiredClaim". type: string type: object + x-kubernetes-validations: + - message: requiredClaim must be set when type is 'RequiredClaim', + and forbidden otherwise + rule: 'has(self.type) && self.type == ''RequiredClaim'' + ? has(self.requiredClaim) : !has(self.requiredClaim)' type: array x-kubernetes-list-type: atomic issuer: diff --git a/payload-manifests/crds/0000_10_config-operator_01_authentications-Hypershift-DevPreviewNoUpgrade.crd.yaml b/payload-manifests/crds/0000_10_config-operator_01_authentications-Hypershift-DevPreviewNoUpgrade.crd.yaml index 195efce400b..373dbfac764 100644 --- a/payload-manifests/crds/0000_10_config-operator_01_authentications-Hypershift-DevPreviewNoUpgrade.crd.yaml +++ b/payload-manifests/crds/0000_10_config-operator_01_authentications-Hypershift-DevPreviewNoUpgrade.crd.yaml @@ -347,19 +347,43 @@ spec: Validation rules are joined via an AND operation. items: + description: |- + TokenClaimValidationRule represents a validation rule based on token claims. + If type is RequiredClaim, requiredClaim must be set. + If type is Expression, expressionRule must be set. properties: + expressionRule: + description: |- + expressionRule contains the configuration for the "Expression" type. + Must be set if type == "Expression". + properties: + expression: + description: |- + expression is a CEL expression evaluated against token claims. + The expression must be a non-empty string and no longer than 4096 characters. + This field is required. + maxLength: 4096 + minLength: 1 + type: string + message: + description: |- + message allows configuring the human-readable message that is returned + from the Kubernetes API server when a token fails validation based on + the CEL expression defined in 'expression'. This field is optional. + maxLength: 256 + minLength: 1 + type: string + required: + - expression + type: object requiredClaim: description: |- - requiredClaim is an optional field that configures the required claim - and value that the Kubernetes API server will use to validate if an incoming - JWT is valid for this identity provider. + requiredClaim allows configuring a required claim name and its expected value. + RequiredClaim is used when type is RequiredClaim. properties: claim: - description: |- - claim is a required field that configures the name of the required claim. - When taken from the JWT claims, claim must be a string value. - - claim must not be an empty string (""). + description: claim is a name of a required claim. + Only claims with string values are supported. minLength: 1 type: string requiredValue: @@ -377,22 +401,38 @@ spec: - requiredValue type: object type: + allOf: + - enum: + - RequiredClaim + - Expression + - enum: + - RequiredClaim + - Expression default: RequiredClaim description: |- type is an optional field that configures the type of the validation rule. - Allowed values are 'RequiredClaim' and omitted (not provided or an empty string). + Allowed values are "RequiredClaim" and "Expression". - When set to 'RequiredClaim', the Kubernetes API server - will be configured to validate that the incoming JWT - contains the required claim and that its value matches - the required value. + When set to 'RequiredClaim', the Kubernetes API server will be configured + to validate that the incoming JWT contains the required claim and that its + value matches the required value. - Defaults to 'RequiredClaim'. - enum: - - RequiredClaim + When set to 'Expression', the Kubernetes API server will be configured + to validate the incoming JWT against the configured CEL expression. + + Defaults to "RequiredClaim". type: string type: object + x-kubernetes-validations: + - message: expressionRule must be set when type is 'Expression', + and forbidden otherwise + rule: 'has(self.type) && self.type == ''Expression'' ? has(self.expressionRule) + : !has(self.expressionRule)' + - message: requiredClaim must be set when type is 'RequiredClaim', + and forbidden otherwise + rule: 'has(self.type) && self.type == ''RequiredClaim'' + ? has(self.requiredClaim) : !has(self.requiredClaim)' type: array x-kubernetes-list-type: atomic issuer: @@ -401,6 +441,16 @@ spec: with the identity provider and how tokens issued from the identity provider are evaluated by the Kubernetes API server. properties: + audienceMatchPolicy: + description: |- + audienceMatchPolicy specifies how token audiences are matched. + If omitted, the system applies a default policy. + Valid values are: + - "MatchAny": The token is accepted if any of its audiences match any of the configured audiences. + enum: + - MatchAny + - "" + type: string audiences: description: |- audiences is a required field that configures the acceptable audiences @@ -415,6 +465,31 @@ spec: minItems: 1 type: array x-kubernetes-list-type: set + discoveryURL: + description: |- + discoveryURL is an optional field that, if specified, overrides the default discovery endpoint + used to retrieve OIDC configuration metadata. By default, the discovery URL is derived from `url` + as "{url}/.well-known/openid-configuration". + + The discoveryURL must: + - Be a valid absolute URL. + - Use the HTTPS scheme. + - Not contain query parameters, user info, or fragments. + - Be different from the value of `url` (ignoring trailing slashes) + maxLength: 2048 + type: string + x-kubernetes-validations: + - message: discoveryURL must be a valid URL + rule: 'self.size() > 0 ? isURL(self) : true' + - message: discoveryURL must be a valid https URL + rule: 'self.size() > 0 ? (isURL(self) && url(self).getScheme() + == ''https'') : true' + - message: discoveryURL must not contain query parameters + rule: self.matches('^[^?]*$') + - message: discoveryURL must not contain fragments + rule: self.matches('^[^#]*$') + - message: discoveryURL must not contain user info + rule: self.matches('^[^@]*$') issuerCertificateAuthority: description: |- issuerCertificateAuthority is an optional field that configures the @@ -461,6 +536,11 @@ spec: - audiences - issuerURL type: object + x-kubernetes-validations: + - message: discoveryURL must be different from issuerURL + rule: 'self.?discoveryURL.orValue("").size() > 0 ? (self.issuerURL.size() + == 0 || self.discoveryURL.find(''^.+[^/]'') != self.issuerURL.find(''^.+[^/]'')) + : true' name: description: |- name is a required field that configures the unique human-readable identifier @@ -560,6 +640,44 @@ spec: - componentNamespace - componentName x-kubernetes-list-type: map + userValidationRules: + description: |- + userValidationRules defines the set of rules used to validate claims in a user’s token. + These rules determine whether a token subject is considered valid based on its claims. + Each rule is evaluated independently. + See the TokenUserValidationRule type for more information on rule structure. + items: + description: |- + TokenUserValidationRule provides a CEL-based rule used to validate a token subject. + Each rule contains a CEL expression that is evaluated against the token’s claims. + properties: + expression: + description: |- + expression is a CEL expression that must evaluate + to true for the token to be accepted. The expression is evaluated against the token's + user information (e.g., username, groups). + + If the expression evaluates to false, the token is rejected. + See https://kubernetes.io/docs/reference/using-api/cel/ for CEL syntax. + At least one rule must evaluate to true for the token to be considered valid. + + This field must be non-empty and may not exceed 4096 characters. + maxLength: 4096 + minLength: 1 + type: string + message: + description: |- + message is an optional, human-readable message returned by the API server when + this validation rule fails. It can help clarify why a token was rejected. + maxLength: 256 + minLength: 1 + type: string + required: + - expression + type: object + maxItems: 64 + type: array + x-kubernetes-list-type: atomic required: - claimMappings - issuer diff --git a/payload-manifests/crds/0000_10_config-operator_01_authentications-Hypershift-TechPreviewNoUpgrade.crd.yaml b/payload-manifests/crds/0000_10_config-operator_01_authentications-Hypershift-TechPreviewNoUpgrade.crd.yaml index 4e8c79c3201..2723a655104 100644 --- a/payload-manifests/crds/0000_10_config-operator_01_authentications-Hypershift-TechPreviewNoUpgrade.crd.yaml +++ b/payload-manifests/crds/0000_10_config-operator_01_authentications-Hypershift-TechPreviewNoUpgrade.crd.yaml @@ -347,19 +347,43 @@ spec: Validation rules are joined via an AND operation. items: + description: |- + TokenClaimValidationRule represents a validation rule based on token claims. + If type is RequiredClaim, requiredClaim must be set. + If type is Expression, expressionRule must be set. properties: + expressionRule: + description: |- + expressionRule contains the configuration for the "Expression" type. + Must be set if type == "Expression". + properties: + expression: + description: |- + expression is a CEL expression evaluated against token claims. + The expression must be a non-empty string and no longer than 4096 characters. + This field is required. + maxLength: 4096 + minLength: 1 + type: string + message: + description: |- + message allows configuring the human-readable message that is returned + from the Kubernetes API server when a token fails validation based on + the CEL expression defined in 'expression'. This field is optional. + maxLength: 256 + minLength: 1 + type: string + required: + - expression + type: object requiredClaim: description: |- - requiredClaim is an optional field that configures the required claim - and value that the Kubernetes API server will use to validate if an incoming - JWT is valid for this identity provider. + requiredClaim allows configuring a required claim name and its expected value. + RequiredClaim is used when type is RequiredClaim. properties: claim: - description: |- - claim is a required field that configures the name of the required claim. - When taken from the JWT claims, claim must be a string value. - - claim must not be an empty string (""). + description: claim is a name of a required claim. + Only claims with string values are supported. minLength: 1 type: string requiredValue: @@ -377,22 +401,38 @@ spec: - requiredValue type: object type: + allOf: + - enum: + - RequiredClaim + - Expression + - enum: + - RequiredClaim + - Expression default: RequiredClaim description: |- type is an optional field that configures the type of the validation rule. - Allowed values are 'RequiredClaim' and omitted (not provided or an empty string). + Allowed values are "RequiredClaim" and "Expression". - When set to 'RequiredClaim', the Kubernetes API server - will be configured to validate that the incoming JWT - contains the required claim and that its value matches - the required value. + When set to 'RequiredClaim', the Kubernetes API server will be configured + to validate that the incoming JWT contains the required claim and that its + value matches the required value. - Defaults to 'RequiredClaim'. - enum: - - RequiredClaim + When set to 'Expression', the Kubernetes API server will be configured + to validate the incoming JWT against the configured CEL expression. + + Defaults to "RequiredClaim". type: string type: object + x-kubernetes-validations: + - message: expressionRule must be set when type is 'Expression', + and forbidden otherwise + rule: 'has(self.type) && self.type == ''Expression'' ? has(self.expressionRule) + : !has(self.expressionRule)' + - message: requiredClaim must be set when type is 'RequiredClaim', + and forbidden otherwise + rule: 'has(self.type) && self.type == ''RequiredClaim'' + ? has(self.requiredClaim) : !has(self.requiredClaim)' type: array x-kubernetes-list-type: atomic issuer: @@ -401,6 +441,16 @@ spec: with the identity provider and how tokens issued from the identity provider are evaluated by the Kubernetes API server. properties: + audienceMatchPolicy: + description: |- + audienceMatchPolicy specifies how token audiences are matched. + If omitted, the system applies a default policy. + Valid values are: + - "MatchAny": The token is accepted if any of its audiences match any of the configured audiences. + enum: + - MatchAny + - "" + type: string audiences: description: |- audiences is a required field that configures the acceptable audiences @@ -415,6 +465,31 @@ spec: minItems: 1 type: array x-kubernetes-list-type: set + discoveryURL: + description: |- + discoveryURL is an optional field that, if specified, overrides the default discovery endpoint + used to retrieve OIDC configuration metadata. By default, the discovery URL is derived from `url` + as "{url}/.well-known/openid-configuration". + + The discoveryURL must: + - Be a valid absolute URL. + - Use the HTTPS scheme. + - Not contain query parameters, user info, or fragments. + - Be different from the value of `url` (ignoring trailing slashes) + maxLength: 2048 + type: string + x-kubernetes-validations: + - message: discoveryURL must be a valid URL + rule: 'self.size() > 0 ? isURL(self) : true' + - message: discoveryURL must be a valid https URL + rule: 'self.size() > 0 ? (isURL(self) && url(self).getScheme() + == ''https'') : true' + - message: discoveryURL must not contain query parameters + rule: self.matches('^[^?]*$') + - message: discoveryURL must not contain fragments + rule: self.matches('^[^#]*$') + - message: discoveryURL must not contain user info + rule: self.matches('^[^@]*$') issuerCertificateAuthority: description: |- issuerCertificateAuthority is an optional field that configures the @@ -461,6 +536,11 @@ spec: - audiences - issuerURL type: object + x-kubernetes-validations: + - message: discoveryURL must be different from issuerURL + rule: 'self.?discoveryURL.orValue("").size() > 0 ? (self.issuerURL.size() + == 0 || self.discoveryURL.find(''^.+[^/]'') != self.issuerURL.find(''^.+[^/]'')) + : true' name: description: |- name is a required field that configures the unique human-readable identifier @@ -560,6 +640,44 @@ spec: - componentNamespace - componentName x-kubernetes-list-type: map + userValidationRules: + description: |- + userValidationRules defines the set of rules used to validate claims in a user’s token. + These rules determine whether a token subject is considered valid based on its claims. + Each rule is evaluated independently. + See the TokenUserValidationRule type for more information on rule structure. + items: + description: |- + TokenUserValidationRule provides a CEL-based rule used to validate a token subject. + Each rule contains a CEL expression that is evaluated against the token’s claims. + properties: + expression: + description: |- + expression is a CEL expression that must evaluate + to true for the token to be accepted. The expression is evaluated against the token's + user information (e.g., username, groups). + + If the expression evaluates to false, the token is rejected. + See https://kubernetes.io/docs/reference/using-api/cel/ for CEL syntax. + At least one rule must evaluate to true for the token to be considered valid. + + This field must be non-empty and may not exceed 4096 characters. + maxLength: 4096 + minLength: 1 + type: string + message: + description: |- + message is an optional, human-readable message returned by the API server when + this validation rule fails. It can help clarify why a token was rejected. + maxLength: 256 + minLength: 1 + type: string + required: + - expression + type: object + maxItems: 64 + type: array + x-kubernetes-list-type: atomic required: - claimMappings - issuer diff --git a/payload-manifests/crds/0000_10_config-operator_01_authentications-SelfManagedHA-CustomNoUpgrade.crd.yaml b/payload-manifests/crds/0000_10_config-operator_01_authentications-SelfManagedHA-CustomNoUpgrade.crd.yaml index 72c798fae70..91b11806a68 100644 --- a/payload-manifests/crds/0000_10_config-operator_01_authentications-SelfManagedHA-CustomNoUpgrade.crd.yaml +++ b/payload-manifests/crds/0000_10_config-operator_01_authentications-SelfManagedHA-CustomNoUpgrade.crd.yaml @@ -347,19 +347,43 @@ spec: Validation rules are joined via an AND operation. items: + description: |- + TokenClaimValidationRule represents a validation rule based on token claims. + If type is RequiredClaim, requiredClaim must be set. + If type is Expression, expressionRule must be set. properties: + expressionRule: + description: |- + expressionRule contains the configuration for the "Expression" type. + Must be set if type == "Expression". + properties: + expression: + description: |- + expression is a CEL expression evaluated against token claims. + The expression must be a non-empty string and no longer than 4096 characters. + This field is required. + maxLength: 4096 + minLength: 1 + type: string + message: + description: |- + message allows configuring the human-readable message that is returned + from the Kubernetes API server when a token fails validation based on + the CEL expression defined in 'expression'. This field is optional. + maxLength: 256 + minLength: 1 + type: string + required: + - expression + type: object requiredClaim: description: |- - requiredClaim is an optional field that configures the required claim - and value that the Kubernetes API server will use to validate if an incoming - JWT is valid for this identity provider. + requiredClaim allows configuring a required claim name and its expected value. + RequiredClaim is used when type is RequiredClaim. properties: claim: - description: |- - claim is a required field that configures the name of the required claim. - When taken from the JWT claims, claim must be a string value. - - claim must not be an empty string (""). + description: claim is a name of a required claim. + Only claims with string values are supported. minLength: 1 type: string requiredValue: @@ -377,22 +401,38 @@ spec: - requiredValue type: object type: + allOf: + - enum: + - RequiredClaim + - Expression + - enum: + - RequiredClaim + - Expression default: RequiredClaim description: |- type is an optional field that configures the type of the validation rule. - Allowed values are 'RequiredClaim' and omitted (not provided or an empty string). + Allowed values are "RequiredClaim" and "Expression". - When set to 'RequiredClaim', the Kubernetes API server - will be configured to validate that the incoming JWT - contains the required claim and that its value matches - the required value. + When set to 'RequiredClaim', the Kubernetes API server will be configured + to validate that the incoming JWT contains the required claim and that its + value matches the required value. - Defaults to 'RequiredClaim'. - enum: - - RequiredClaim + When set to 'Expression', the Kubernetes API server will be configured + to validate the incoming JWT against the configured CEL expression. + + Defaults to "RequiredClaim". type: string type: object + x-kubernetes-validations: + - message: expressionRule must be set when type is 'Expression', + and forbidden otherwise + rule: 'has(self.type) && self.type == ''Expression'' ? has(self.expressionRule) + : !has(self.expressionRule)' + - message: requiredClaim must be set when type is 'RequiredClaim', + and forbidden otherwise + rule: 'has(self.type) && self.type == ''RequiredClaim'' + ? has(self.requiredClaim) : !has(self.requiredClaim)' type: array x-kubernetes-list-type: atomic issuer: @@ -401,6 +441,16 @@ spec: with the identity provider and how tokens issued from the identity provider are evaluated by the Kubernetes API server. properties: + audienceMatchPolicy: + description: |- + audienceMatchPolicy specifies how token audiences are matched. + If omitted, the system applies a default policy. + Valid values are: + - "MatchAny": The token is accepted if any of its audiences match any of the configured audiences. + enum: + - MatchAny + - "" + type: string audiences: description: |- audiences is a required field that configures the acceptable audiences @@ -415,6 +465,31 @@ spec: minItems: 1 type: array x-kubernetes-list-type: set + discoveryURL: + description: |- + discoveryURL is an optional field that, if specified, overrides the default discovery endpoint + used to retrieve OIDC configuration metadata. By default, the discovery URL is derived from `url` + as "{url}/.well-known/openid-configuration". + + The discoveryURL must: + - Be a valid absolute URL. + - Use the HTTPS scheme. + - Not contain query parameters, user info, or fragments. + - Be different from the value of `url` (ignoring trailing slashes) + maxLength: 2048 + type: string + x-kubernetes-validations: + - message: discoveryURL must be a valid URL + rule: 'self.size() > 0 ? isURL(self) : true' + - message: discoveryURL must be a valid https URL + rule: 'self.size() > 0 ? (isURL(self) && url(self).getScheme() + == ''https'') : true' + - message: discoveryURL must not contain query parameters + rule: self.matches('^[^?]*$') + - message: discoveryURL must not contain fragments + rule: self.matches('^[^#]*$') + - message: discoveryURL must not contain user info + rule: self.matches('^[^@]*$') issuerCertificateAuthority: description: |- issuerCertificateAuthority is an optional field that configures the @@ -461,6 +536,11 @@ spec: - audiences - issuerURL type: object + x-kubernetes-validations: + - message: discoveryURL must be different from issuerURL + rule: 'self.?discoveryURL.orValue("").size() > 0 ? (self.issuerURL.size() + == 0 || self.discoveryURL.find(''^.+[^/]'') != self.issuerURL.find(''^.+[^/]'')) + : true' name: description: |- name is a required field that configures the unique human-readable identifier @@ -560,6 +640,44 @@ spec: - componentNamespace - componentName x-kubernetes-list-type: map + userValidationRules: + description: |- + userValidationRules defines the set of rules used to validate claims in a user’s token. + These rules determine whether a token subject is considered valid based on its claims. + Each rule is evaluated independently. + See the TokenUserValidationRule type for more information on rule structure. + items: + description: |- + TokenUserValidationRule provides a CEL-based rule used to validate a token subject. + Each rule contains a CEL expression that is evaluated against the token’s claims. + properties: + expression: + description: |- + expression is a CEL expression that must evaluate + to true for the token to be accepted. The expression is evaluated against the token's + user information (e.g., username, groups). + + If the expression evaluates to false, the token is rejected. + See https://kubernetes.io/docs/reference/using-api/cel/ for CEL syntax. + At least one rule must evaluate to true for the token to be considered valid. + + This field must be non-empty and may not exceed 4096 characters. + maxLength: 4096 + minLength: 1 + type: string + message: + description: |- + message is an optional, human-readable message returned by the API server when + this validation rule fails. It can help clarify why a token was rejected. + maxLength: 256 + minLength: 1 + type: string + required: + - expression + type: object + maxItems: 64 + type: array + x-kubernetes-list-type: atomic required: - claimMappings - issuer diff --git a/payload-manifests/crds/0000_10_config-operator_01_authentications-SelfManagedHA-DevPreviewNoUpgrade.crd.yaml b/payload-manifests/crds/0000_10_config-operator_01_authentications-SelfManagedHA-DevPreviewNoUpgrade.crd.yaml index 998e804191f..ecb533f94de 100644 --- a/payload-manifests/crds/0000_10_config-operator_01_authentications-SelfManagedHA-DevPreviewNoUpgrade.crd.yaml +++ b/payload-manifests/crds/0000_10_config-operator_01_authentications-SelfManagedHA-DevPreviewNoUpgrade.crd.yaml @@ -347,19 +347,43 @@ spec: Validation rules are joined via an AND operation. items: + description: |- + TokenClaimValidationRule represents a validation rule based on token claims. + If type is RequiredClaim, requiredClaim must be set. + If type is Expression, expressionRule must be set. properties: + expressionRule: + description: |- + expressionRule contains the configuration for the "Expression" type. + Must be set if type == "Expression". + properties: + expression: + description: |- + expression is a CEL expression evaluated against token claims. + The expression must be a non-empty string and no longer than 4096 characters. + This field is required. + maxLength: 4096 + minLength: 1 + type: string + message: + description: |- + message allows configuring the human-readable message that is returned + from the Kubernetes API server when a token fails validation based on + the CEL expression defined in 'expression'. This field is optional. + maxLength: 256 + minLength: 1 + type: string + required: + - expression + type: object requiredClaim: description: |- - requiredClaim is an optional field that configures the required claim - and value that the Kubernetes API server will use to validate if an incoming - JWT is valid for this identity provider. + requiredClaim allows configuring a required claim name and its expected value. + RequiredClaim is used when type is RequiredClaim. properties: claim: - description: |- - claim is a required field that configures the name of the required claim. - When taken from the JWT claims, claim must be a string value. - - claim must not be an empty string (""). + description: claim is a name of a required claim. + Only claims with string values are supported. minLength: 1 type: string requiredValue: @@ -377,22 +401,38 @@ spec: - requiredValue type: object type: + allOf: + - enum: + - RequiredClaim + - Expression + - enum: + - RequiredClaim + - Expression default: RequiredClaim description: |- type is an optional field that configures the type of the validation rule. - Allowed values are 'RequiredClaim' and omitted (not provided or an empty string). + Allowed values are "RequiredClaim" and "Expression". - When set to 'RequiredClaim', the Kubernetes API server - will be configured to validate that the incoming JWT - contains the required claim and that its value matches - the required value. + When set to 'RequiredClaim', the Kubernetes API server will be configured + to validate that the incoming JWT contains the required claim and that its + value matches the required value. - Defaults to 'RequiredClaim'. - enum: - - RequiredClaim + When set to 'Expression', the Kubernetes API server will be configured + to validate the incoming JWT against the configured CEL expression. + + Defaults to "RequiredClaim". type: string type: object + x-kubernetes-validations: + - message: expressionRule must be set when type is 'Expression', + and forbidden otherwise + rule: 'has(self.type) && self.type == ''Expression'' ? has(self.expressionRule) + : !has(self.expressionRule)' + - message: requiredClaim must be set when type is 'RequiredClaim', + and forbidden otherwise + rule: 'has(self.type) && self.type == ''RequiredClaim'' + ? has(self.requiredClaim) : !has(self.requiredClaim)' type: array x-kubernetes-list-type: atomic issuer: @@ -401,6 +441,16 @@ spec: with the identity provider and how tokens issued from the identity provider are evaluated by the Kubernetes API server. properties: + audienceMatchPolicy: + description: |- + audienceMatchPolicy specifies how token audiences are matched. + If omitted, the system applies a default policy. + Valid values are: + - "MatchAny": The token is accepted if any of its audiences match any of the configured audiences. + enum: + - MatchAny + - "" + type: string audiences: description: |- audiences is a required field that configures the acceptable audiences @@ -415,6 +465,31 @@ spec: minItems: 1 type: array x-kubernetes-list-type: set + discoveryURL: + description: |- + discoveryURL is an optional field that, if specified, overrides the default discovery endpoint + used to retrieve OIDC configuration metadata. By default, the discovery URL is derived from `url` + as "{url}/.well-known/openid-configuration". + + The discoveryURL must: + - Be a valid absolute URL. + - Use the HTTPS scheme. + - Not contain query parameters, user info, or fragments. + - Be different from the value of `url` (ignoring trailing slashes) + maxLength: 2048 + type: string + x-kubernetes-validations: + - message: discoveryURL must be a valid URL + rule: 'self.size() > 0 ? isURL(self) : true' + - message: discoveryURL must be a valid https URL + rule: 'self.size() > 0 ? (isURL(self) && url(self).getScheme() + == ''https'') : true' + - message: discoveryURL must not contain query parameters + rule: self.matches('^[^?]*$') + - message: discoveryURL must not contain fragments + rule: self.matches('^[^#]*$') + - message: discoveryURL must not contain user info + rule: self.matches('^[^@]*$') issuerCertificateAuthority: description: |- issuerCertificateAuthority is an optional field that configures the @@ -461,6 +536,11 @@ spec: - audiences - issuerURL type: object + x-kubernetes-validations: + - message: discoveryURL must be different from issuerURL + rule: 'self.?discoveryURL.orValue("").size() > 0 ? (self.issuerURL.size() + == 0 || self.discoveryURL.find(''^.+[^/]'') != self.issuerURL.find(''^.+[^/]'')) + : true' name: description: |- name is a required field that configures the unique human-readable identifier @@ -560,6 +640,44 @@ spec: - componentNamespace - componentName x-kubernetes-list-type: map + userValidationRules: + description: |- + userValidationRules defines the set of rules used to validate claims in a user’s token. + These rules determine whether a token subject is considered valid based on its claims. + Each rule is evaluated independently. + See the TokenUserValidationRule type for more information on rule structure. + items: + description: |- + TokenUserValidationRule provides a CEL-based rule used to validate a token subject. + Each rule contains a CEL expression that is evaluated against the token’s claims. + properties: + expression: + description: |- + expression is a CEL expression that must evaluate + to true for the token to be accepted. The expression is evaluated against the token's + user information (e.g., username, groups). + + If the expression evaluates to false, the token is rejected. + See https://kubernetes.io/docs/reference/using-api/cel/ for CEL syntax. + At least one rule must evaluate to true for the token to be considered valid. + + This field must be non-empty and may not exceed 4096 characters. + maxLength: 4096 + minLength: 1 + type: string + message: + description: |- + message is an optional, human-readable message returned by the API server when + this validation rule fails. It can help clarify why a token was rejected. + maxLength: 256 + minLength: 1 + type: string + required: + - expression + type: object + maxItems: 64 + type: array + x-kubernetes-list-type: atomic required: - claimMappings - issuer diff --git a/payload-manifests/crds/0000_10_config-operator_01_authentications-SelfManagedHA-TechPreviewNoUpgrade.crd.yaml b/payload-manifests/crds/0000_10_config-operator_01_authentications-SelfManagedHA-TechPreviewNoUpgrade.crd.yaml index 75446be6cca..95ddd24093e 100644 --- a/payload-manifests/crds/0000_10_config-operator_01_authentications-SelfManagedHA-TechPreviewNoUpgrade.crd.yaml +++ b/payload-manifests/crds/0000_10_config-operator_01_authentications-SelfManagedHA-TechPreviewNoUpgrade.crd.yaml @@ -347,19 +347,43 @@ spec: Validation rules are joined via an AND operation. items: + description: |- + TokenClaimValidationRule represents a validation rule based on token claims. + If type is RequiredClaim, requiredClaim must be set. + If type is Expression, expressionRule must be set. properties: + expressionRule: + description: |- + expressionRule contains the configuration for the "Expression" type. + Must be set if type == "Expression". + properties: + expression: + description: |- + expression is a CEL expression evaluated against token claims. + The expression must be a non-empty string and no longer than 4096 characters. + This field is required. + maxLength: 4096 + minLength: 1 + type: string + message: + description: |- + message allows configuring the human-readable message that is returned + from the Kubernetes API server when a token fails validation based on + the CEL expression defined in 'expression'. This field is optional. + maxLength: 256 + minLength: 1 + type: string + required: + - expression + type: object requiredClaim: description: |- - requiredClaim is an optional field that configures the required claim - and value that the Kubernetes API server will use to validate if an incoming - JWT is valid for this identity provider. + requiredClaim allows configuring a required claim name and its expected value. + RequiredClaim is used when type is RequiredClaim. properties: claim: - description: |- - claim is a required field that configures the name of the required claim. - When taken from the JWT claims, claim must be a string value. - - claim must not be an empty string (""). + description: claim is a name of a required claim. + Only claims with string values are supported. minLength: 1 type: string requiredValue: @@ -377,22 +401,38 @@ spec: - requiredValue type: object type: + allOf: + - enum: + - RequiredClaim + - Expression + - enum: + - RequiredClaim + - Expression default: RequiredClaim description: |- type is an optional field that configures the type of the validation rule. - Allowed values are 'RequiredClaim' and omitted (not provided or an empty string). + Allowed values are "RequiredClaim" and "Expression". - When set to 'RequiredClaim', the Kubernetes API server - will be configured to validate that the incoming JWT - contains the required claim and that its value matches - the required value. + When set to 'RequiredClaim', the Kubernetes API server will be configured + to validate that the incoming JWT contains the required claim and that its + value matches the required value. - Defaults to 'RequiredClaim'. - enum: - - RequiredClaim + When set to 'Expression', the Kubernetes API server will be configured + to validate the incoming JWT against the configured CEL expression. + + Defaults to "RequiredClaim". type: string type: object + x-kubernetes-validations: + - message: expressionRule must be set when type is 'Expression', + and forbidden otherwise + rule: 'has(self.type) && self.type == ''Expression'' ? has(self.expressionRule) + : !has(self.expressionRule)' + - message: requiredClaim must be set when type is 'RequiredClaim', + and forbidden otherwise + rule: 'has(self.type) && self.type == ''RequiredClaim'' + ? has(self.requiredClaim) : !has(self.requiredClaim)' type: array x-kubernetes-list-type: atomic issuer: @@ -401,6 +441,16 @@ spec: with the identity provider and how tokens issued from the identity provider are evaluated by the Kubernetes API server. properties: + audienceMatchPolicy: + description: |- + audienceMatchPolicy specifies how token audiences are matched. + If omitted, the system applies a default policy. + Valid values are: + - "MatchAny": The token is accepted if any of its audiences match any of the configured audiences. + enum: + - MatchAny + - "" + type: string audiences: description: |- audiences is a required field that configures the acceptable audiences @@ -415,6 +465,31 @@ spec: minItems: 1 type: array x-kubernetes-list-type: set + discoveryURL: + description: |- + discoveryURL is an optional field that, if specified, overrides the default discovery endpoint + used to retrieve OIDC configuration metadata. By default, the discovery URL is derived from `url` + as "{url}/.well-known/openid-configuration". + + The discoveryURL must: + - Be a valid absolute URL. + - Use the HTTPS scheme. + - Not contain query parameters, user info, or fragments. + - Be different from the value of `url` (ignoring trailing slashes) + maxLength: 2048 + type: string + x-kubernetes-validations: + - message: discoveryURL must be a valid URL + rule: 'self.size() > 0 ? isURL(self) : true' + - message: discoveryURL must be a valid https URL + rule: 'self.size() > 0 ? (isURL(self) && url(self).getScheme() + == ''https'') : true' + - message: discoveryURL must not contain query parameters + rule: self.matches('^[^?]*$') + - message: discoveryURL must not contain fragments + rule: self.matches('^[^#]*$') + - message: discoveryURL must not contain user info + rule: self.matches('^[^@]*$') issuerCertificateAuthority: description: |- issuerCertificateAuthority is an optional field that configures the @@ -461,6 +536,11 @@ spec: - audiences - issuerURL type: object + x-kubernetes-validations: + - message: discoveryURL must be different from issuerURL + rule: 'self.?discoveryURL.orValue("").size() > 0 ? (self.issuerURL.size() + == 0 || self.discoveryURL.find(''^.+[^/]'') != self.issuerURL.find(''^.+[^/]'')) + : true' name: description: |- name is a required field that configures the unique human-readable identifier @@ -560,6 +640,44 @@ spec: - componentNamespace - componentName x-kubernetes-list-type: map + userValidationRules: + description: |- + userValidationRules defines the set of rules used to validate claims in a user’s token. + These rules determine whether a token subject is considered valid based on its claims. + Each rule is evaluated independently. + See the TokenUserValidationRule type for more information on rule structure. + items: + description: |- + TokenUserValidationRule provides a CEL-based rule used to validate a token subject. + Each rule contains a CEL expression that is evaluated against the token’s claims. + properties: + expression: + description: |- + expression is a CEL expression that must evaluate + to true for the token to be accepted. The expression is evaluated against the token's + user information (e.g., username, groups). + + If the expression evaluates to false, the token is rejected. + See https://kubernetes.io/docs/reference/using-api/cel/ for CEL syntax. + At least one rule must evaluate to true for the token to be considered valid. + + This field must be non-empty and may not exceed 4096 characters. + maxLength: 4096 + minLength: 1 + type: string + message: + description: |- + message is an optional, human-readable message returned by the API server when + this validation rule fails. It can help clarify why a token was rejected. + maxLength: 256 + minLength: 1 + type: string + required: + - expression + type: object + maxItems: 64 + type: array + x-kubernetes-list-type: atomic required: - claimMappings - issuer diff --git a/payload-manifests/featuregates/featureGate-Hypershift-Default.yaml b/payload-manifests/featuregates/featureGate-Hypershift-Default.yaml index a807ed9434d..7f6c1d96620 100644 --- a/payload-manifests/featuregates/featureGate-Hypershift-Default.yaml +++ b/payload-manifests/featuregates/featureGate-Hypershift-Default.yaml @@ -100,6 +100,9 @@ { "name": "ExternalOIDCWithUIDAndExtraClaimMappings" }, + { + "name": "ExternalOIDCWithUpstreamParity" + }, { "name": "ExternalSnapshotMetadata" }, diff --git a/payload-manifests/featuregates/featureGate-Hypershift-DevPreviewNoUpgrade.yaml b/payload-manifests/featuregates/featureGate-Hypershift-DevPreviewNoUpgrade.yaml index 60b0c1407f9..d02e528dad8 100644 --- a/payload-manifests/featuregates/featureGate-Hypershift-DevPreviewNoUpgrade.yaml +++ b/payload-manifests/featuregates/featureGate-Hypershift-DevPreviewNoUpgrade.yaml @@ -155,6 +155,9 @@ { "name": "ExternalOIDCWithUIDAndExtraClaimMappings" }, + { + "name": "ExternalOIDCWithUpstreamParity" + }, { "name": "ExternalSnapshotMetadata" }, diff --git a/payload-manifests/featuregates/featureGate-Hypershift-TechPreviewNoUpgrade.yaml b/payload-manifests/featuregates/featureGate-Hypershift-TechPreviewNoUpgrade.yaml index 4b16de057ba..2708e9dc44e 100644 --- a/payload-manifests/featuregates/featureGate-Hypershift-TechPreviewNoUpgrade.yaml +++ b/payload-manifests/featuregates/featureGate-Hypershift-TechPreviewNoUpgrade.yaml @@ -161,6 +161,9 @@ { "name": "ExternalOIDCWithUIDAndExtraClaimMappings" }, + { + "name": "ExternalOIDCWithUpstreamParity" + }, { "name": "GCPClusterHostedDNS" }, diff --git a/payload-manifests/featuregates/featureGate-SelfManagedHA-Default.yaml b/payload-manifests/featuregates/featureGate-SelfManagedHA-Default.yaml index 2b94acf2bbe..779714a5d26 100644 --- a/payload-manifests/featuregates/featureGate-SelfManagedHA-Default.yaml +++ b/payload-manifests/featuregates/featureGate-SelfManagedHA-Default.yaml @@ -103,6 +103,9 @@ { "name": "ExternalOIDCWithUIDAndExtraClaimMappings" }, + { + "name": "ExternalOIDCWithUpstreamParity" + }, { "name": "ExternalSnapshotMetadata" }, diff --git a/payload-manifests/featuregates/featureGate-SelfManagedHA-DevPreviewNoUpgrade.yaml b/payload-manifests/featuregates/featureGate-SelfManagedHA-DevPreviewNoUpgrade.yaml index 37477a73dfe..10848e23c59 100644 --- a/payload-manifests/featuregates/featureGate-SelfManagedHA-DevPreviewNoUpgrade.yaml +++ b/payload-manifests/featuregates/featureGate-SelfManagedHA-DevPreviewNoUpgrade.yaml @@ -137,6 +137,9 @@ { "name": "ExternalOIDCWithUIDAndExtraClaimMappings" }, + { + "name": "ExternalOIDCWithUpstreamParity" + }, { "name": "ExternalSnapshotMetadata" }, diff --git a/payload-manifests/featuregates/featureGate-SelfManagedHA-TechPreviewNoUpgrade.yaml b/payload-manifests/featuregates/featureGate-SelfManagedHA-TechPreviewNoUpgrade.yaml index 4b73d1e8062..bfb0310e483 100644 --- a/payload-manifests/featuregates/featureGate-SelfManagedHA-TechPreviewNoUpgrade.yaml +++ b/payload-manifests/featuregates/featureGate-SelfManagedHA-TechPreviewNoUpgrade.yaml @@ -143,6 +143,9 @@ { "name": "ExternalOIDCWithUIDAndExtraClaimMappings" }, + { + "name": "ExternalOIDCWithUpstreamParity" + }, { "name": "GCPClusterHostedDNS" },