-
Notifications
You must be signed in to change notification settings - Fork 111
[WIP]: CNTRLPLANE-312: Add generation logic for new API fields in the Authentication CR #810
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -178,7 +178,7 @@ func (c *externalOIDCController) generateAuthConfig(auth configv1.Authentication | |
| func generateJWTForProvider(provider configv1.OIDCProvider, configMapLister corev1listers.ConfigMapLister, featureGates featuregates.FeatureGate, serviceAccountIssuer string) (apiserverv1beta1.JWTAuthenticator, error) { | ||
| out := apiserverv1beta1.JWTAuthenticator{} | ||
|
|
||
| issuer, err := generateIssuer(provider.Issuer, configMapLister, serviceAccountIssuer) | ||
| issuer, err := generateIssuer(provider.Issuer, featureGates, configMapLister, serviceAccountIssuer) | ||
| if err != nil { | ||
| return apiserverv1beta1.JWTAuthenticator{}, fmt.Errorf("generating issuer for provider %q: %v", provider.Name, err) | ||
| } | ||
|
|
@@ -188,19 +188,27 @@ func generateJWTForProvider(provider configv1.OIDCProvider, configMapLister core | |
| return apiserverv1beta1.JWTAuthenticator{}, fmt.Errorf("generating claimMappings for provider %q: %v", provider.Name, err) | ||
| } | ||
|
|
||
| claimValidationRules, err := generateClaimValidationRules(provider.ClaimValidationRules...) | ||
| claimValidationRules, err := generateClaimValidationRules(featureGates, provider.ClaimValidationRules...) | ||
| if err != nil { | ||
| return apiserverv1beta1.JWTAuthenticator{}, fmt.Errorf("generating claimValidationRules for provider %q: %v", provider.Name, err) | ||
| } | ||
|
|
||
| var userValidationRules []apiserverv1beta1.UserValidationRule | ||
| if featureGates.Enabled(features.FeatureGateExternalOIDCWithUpstreamParity) { | ||
| userValidationRules, err = generateUserValidationRules(featureGates, provider.UserValidationRules) | ||
| if err != nil { | ||
| return apiserverv1beta1.JWTAuthenticator{}, fmt.Errorf("generating userValidationRules for provider %q: %v", provider.Name, err) | ||
| } | ||
| } | ||
| out.Issuer = issuer | ||
| out.ClaimMappings = claimMappings | ||
| out.ClaimValidationRules = claimValidationRules | ||
| out.UserValidationRules = userValidationRules | ||
|
|
||
| return out, nil | ||
| } | ||
|
|
||
| func generateIssuer(issuer configv1.TokenIssuer, configMapLister corev1listers.ConfigMapLister, serviceAccountIssuer string) (apiserverv1beta1.Issuer, error) { | ||
| func generateIssuer(issuer configv1.TokenIssuer, featureGates featuregates.FeatureGate, configMapLister corev1listers.ConfigMapLister, serviceAccountIssuer string) (apiserverv1beta1.Issuer, error) { | ||
| out := apiserverv1beta1.Issuer{} | ||
|
|
||
| if len(serviceAccountIssuer) > 0 { | ||
|
|
@@ -215,6 +223,11 @@ func generateIssuer(issuer configv1.TokenIssuer, configMapLister corev1listers.C | |
| for _, audience := range issuer.Audiences { | ||
| out.Audiences = append(out.Audiences, string(audience)) | ||
| } | ||
| if featureGates.Enabled(features.FeatureGateExternalOIDCWithUpstreamParity) { | ||
| if issuer.DiscoveryURL != "" { | ||
| out.DiscoveryURL = &issuer.DiscoveryURL | ||
| } | ||
| } | ||
|
|
||
| if len(issuer.CertificateAuthority.Name) > 0 { | ||
| ca, err := getCertificateAuthorityFromConfigMap(issuer.CertificateAuthority.Name, configMapLister) | ||
|
|
@@ -394,11 +407,11 @@ func generateExtraMapping(extraMapping configv1.ExtraMapping) (apiserverv1beta1. | |
| return out, nil | ||
| } | ||
|
|
||
| func generateClaimValidationRules(claimValidationRules ...configv1.TokenClaimValidationRule) ([]apiserverv1beta1.ClaimValidationRule, error) { | ||
| func generateClaimValidationRules(featureGates featuregates.FeatureGate, claimValidationRules ...configv1.TokenClaimValidationRule) ([]apiserverv1beta1.ClaimValidationRule, error) { | ||
| out := []apiserverv1beta1.ClaimValidationRule{} | ||
| errs := []error{} | ||
| for _, claimValidationRule := range claimValidationRules { | ||
| rule, err := generateClaimValidationRule(claimValidationRule) | ||
| rule, err := generateClaimValidationRule(claimValidationRule, featureGates) | ||
| if err != nil { | ||
| errs = append(errs, fmt.Errorf("generating claimValidationRule: %v", err)) | ||
| continue | ||
|
|
@@ -414,27 +427,84 @@ func generateClaimValidationRules(claimValidationRules ...configv1.TokenClaimVal | |
| return out, nil | ||
| } | ||
|
|
||
| func generateClaimValidationRule(claimValidationRule configv1.TokenClaimValidationRule) (apiserverv1beta1.ClaimValidationRule, error) { | ||
| func generateClaimValidationRule(claimValidationRule configv1.TokenClaimValidationRule, featureGates featuregates.FeatureGate) (apiserverv1beta1.ClaimValidationRule, error) { | ||
| out := apiserverv1beta1.ClaimValidationRule{} | ||
|
|
||
| // Currently, the authentications.config.openshift.io CRD only allows setting a claim and required value for the | ||
| // validation rule and does not allow setting a CEL expression and message like the upstream. | ||
| // This is likely to change in the near future to also allow setting a CEL expression. | ||
| switch claimValidationRule.Type { | ||
| case configv1.TokenValidationRuleTypeRequiredClaim: | ||
| case configv1.TokenValidationRuleRequiredClaim: | ||
| if claimValidationRule.RequiredClaim == nil { | ||
| return apiserverv1beta1.ClaimValidationRule{}, fmt.Errorf("claimValidationRule.type is %s and requiredClaim is not set", configv1.TokenValidationRuleTypeRequiredClaim) | ||
| return apiserverv1beta1.ClaimValidationRule{}, fmt.Errorf("claimValidationRule.type is %s and requiredClaim is not set", configv1.TokenValidationRuleRequiredClaim) | ||
| } | ||
|
|
||
| out.Claim = claimValidationRule.RequiredClaim.Claim | ||
| out.RequiredValue = claimValidationRule.RequiredClaim.RequiredValue | ||
| case configv1.TokenValidationRuleExpression: | ||
| if !featureGates.Enabled(features.FeatureGateExternalOIDCWithUpstreamParity) { | ||
| // skip CEL expression handling if the feature is disabled | ||
| return apiserverv1beta1.ClaimValidationRule{}, fmt.Errorf( | ||
| "TokenValidationRuleExpression is not enabled without the feature gate") | ||
| } | ||
| if len(claimValidationRule.Expression.Expression) == 0 { | ||
| return apiserverv1beta1.ClaimValidationRule{}, fmt.Errorf("claimValidationRule.type is %s and expression is not set", configv1.TokenValidationRuleExpression) | ||
| } | ||
|
|
||
| // validate CEL expression | ||
| if err := validateCELExpression(&authenticationcel.ClaimMappingExpression{ | ||
| Expression: claimValidationRule.Expression.Expression, | ||
| }); err != nil { | ||
| return apiserverv1beta1.ClaimValidationRule{}, fmt.Errorf("invalid CEL expression: %v", err) | ||
| } | ||
|
Comment on lines
+454
to
+459
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As a note, we should update https://github.com/openshift/kubernetes/blob/891f5bb0306166d5625b89fc8dc86bbc8c85f549/openshift-kube-apiserver/admission/customresourcevalidation/authentication/validate_authentication.go#L240 to perform admission time validation of these expressions. It doesn't hurt to also include the runtime validations here, but admission validations should be sufficient here because we are embedding that in the kube-apiserver
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not entirely sure what you mean here, can you explain more in details on what needs to be done ?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I mean that we will also need to take an action item to update our admission plugin that we inject into the Kubernetes API server to validate that the CEL expressions here compile successfully. There is nothing that needs to be done in this PR to address this comment.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see, make sense, will make a note on that! |
||
|
|
||
| out.Expression = claimValidationRule.Expression.Expression | ||
| out.Message = claimValidationRule.Expression.Message | ||
|
|
||
| default: | ||
| return apiserverv1beta1.ClaimValidationRule{}, fmt.Errorf("unknown claimValidationRule type %q", claimValidationRule.Type) | ||
| } | ||
|
|
||
| return out, nil | ||
| } | ||
|
|
||
| func generateUserValidationRule(rule configv1.TokenUserValidationRule, featureGates featuregates.FeatureGate) (apiserverv1beta1.UserValidationRule, error) { | ||
| if len(rule.Expression) == 0 { | ||
| return apiserverv1beta1.UserValidationRule{}, fmt.Errorf("userValidationRule expression must be non-empty") | ||
| } | ||
|
|
||
| // Optional: only enable user validation rules if a feature gate is active | ||
| if !featureGates.Enabled(features.FeatureGateExternalOIDCWithUpstreamParity) { | ||
| return apiserverv1beta1.UserValidationRule{}, fmt.Errorf( | ||
| "userValidationRule cannot be used without the feature gate") | ||
| } | ||
|
|
||
| return apiserverv1beta1.UserValidationRule{ | ||
| Expression: rule.Expression, | ||
| Message: rule.Message, | ||
| }, nil | ||
| } | ||
|
|
||
| func generateUserValidationRules(featureGates featuregates.FeatureGate, rules []configv1.TokenUserValidationRule) ([]apiserverv1beta1.UserValidationRule, error) { | ||
| out := []apiserverv1beta1.UserValidationRule{} | ||
| errs := []error{} | ||
|
|
||
| for _, r := range rules { | ||
| uvr, err := generateUserValidationRule(r, featureGates) | ||
| if err != nil { | ||
| errs = append(errs, fmt.Errorf("generating userValidationRule: %v", err)) | ||
| continue | ||
| } | ||
| out = append(out, uvr) | ||
| } | ||
|
|
||
| if len(errs) > 0 { | ||
| return nil, errors.Join(errs...) | ||
| } | ||
|
|
||
| return out, nil | ||
| } | ||
|
|
||
| // getExpectedApplyConfig serializes the input authConfig into JSON and creates an apply configuration | ||
| // for a configmap with the serialized authConfig in the right key. | ||
| func getExpectedApplyConfig(authConfig apiserverv1beta1.AuthenticationConfiguration) (*corev1ac.ConfigMapApplyConfiguration, error) { | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.