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
Implement CEL for StructuredAuthenticationConfig #121078
Implement CEL for StructuredAuthenticationConfig #121078
Conversation
@aramase: You must be a member of the kubernetes/milestone-maintainers GitHub team to set the milestone. If you believe you should be able to issue the /milestone command, please contact your Milestone Maintainers Team and have them propose you as an additional delegate for this responsibility. In response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. |
569c36f
to
790305f
Compare
@aramase: The label(s) In response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. |
/label api-review |
c258db8
to
38283b9
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Initial API review on 10-12-23
// These allow invariants to be applied to incoming identities such as preventing the | ||
// use of the system: prefix that is commonly used by Kubernetes components. | ||
// +optional | ||
UserInfoValidationRules []UserInfoValidationRule `json:"userInfoValidationRules,omitempty"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The CEL variable that contains the final user info should be called user
UserInfoValidationRules []UserInfoValidationRule `json:"userInfoValidationRules,omitempty"` | |
UserValidationRules []UserValidationRule `json:"userValidationRules,omitempty"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Specifically that all validation rules are logically ANDed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Internally, we may want to combine all the CEL expressions by wrapping them in parentheses and using logical ANDs (after we benchmark to see if it worth it). On a failure, we would run the individual expressions so that we could provide the correct error message.
staging/src/k8s.io/apiserver/pkg/apis/apiserver/v1alpha1/types.go
Outdated
Show resolved
Hide resolved
staging/src/k8s.io/apiserver/pkg/apis/apiserver/v1alpha1/types.go
Outdated
Show resolved
Hide resolved
staging/src/k8s.io/apiserver/pkg/apis/apiserver/validation/validation.go
Outdated
Show resolved
Hide resolved
staging/src/k8s.io/apiserver/pkg/apis/apiserver/validation/validation.go
Outdated
Show resolved
Hide resolved
staging/src/k8s.io/apiserver/pkg/apis/apiserver/validation/validation.go
Outdated
Show resolved
Hide resolved
staging/src/k8s.io/apiserver/pkg/authentication/cel/interface.go
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just did a quick pass, will save time for another pass on imp details and tests. Thank you!
staging/src/k8s.io/apiserver/pkg/apis/apiserver/validation/validation.go
Outdated
Show resolved
Hide resolved
staging/src/k8s.io/apiserver/pkg/apis/apiserver/validation/validation.go
Outdated
Show resolved
Hide resolved
staging/src/k8s.io/apiserver/pkg/apis/apiserver/validation/validation.go
Outdated
Show resolved
Hide resolved
staging/src/k8s.io/apiserver/plugin/pkg/authenticator/token/oidc/oidc.go
Outdated
Show resolved
Hide resolved
Hello! |
Yes, this PR is still targeted for v1.29. |
staging/src/k8s.io/apiserver/pkg/apis/apiserver/v1alpha1/types.go
Outdated
Show resolved
Hide resolved
staging/src/k8s.io/apiserver/pkg/apis/apiserver/v1alpha1/types.go
Outdated
Show resolved
Hide resolved
staging/src/k8s.io/apiserver/pkg/apis/apiserver/v1alpha1/types.go
Outdated
Show resolved
Hide resolved
staging/src/k8s.io/apiserver/pkg/apis/apiserver/v1alpha1/types.go
Outdated
Show resolved
Hide resolved
|
||
// CELMapper is a struct that holds the compiled expressions for | ||
// username, groups, uid, extra, claimValidation and userValidation | ||
type CELMapper struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
non-blocking since this doesn't touch the config API layer, but ClaimsMapper is a really confusing interface... each instance held in CELMapper
is only valid to call one of the methods. It's weird to have to put the documentation about which uses of this interface should call which methods on the interface doc.
interaces like these would be clearer (even if the names get a little silly by the end):
Username StringMapper
Groups StringArrayMapper
UID StringMapper
Extra MapStringStringArrayMapper
ClaimValidationRules BoolMapper
UserValidationRules BoolMapper
type BoolMapper interface {
MapBool(context.Context, *unstructured.Unstructured) (bool, error)
}
type StringMapper interface {
MapString(context.Context, *unstructured.Unstructured) (string, error)
}
type StringArrayMapper interface {
MapStringArray(context.Context, *unstructured.Unstructured) ([]string, error)
}
type MapStringStringArrayMapper interface {
MapMapStringStringArray(context.Context, *unstructured.Unstructured) (map[string][]string, error)
}
specific implementations of those could wrap an interface like
EvalSingle(context.Context, *unstructured.Unstructured) (EvaluationResult, error)
or
EvalMultiple(context.Context, *unstructured.Unstructured) ([]EvaluationResult, error)
and type-check / accumulate
I think this could be cleaned up in 1.30 to be a lot more composable
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added this to the follow-up items list for v1.30. I'll get this cleaned up.
return nil, err | ||
authenticator := &Authenticator{} | ||
|
||
celMapper, fieldErr := apiservervalidation.ValidateJWTAuthenticator(opts.JWTAuthenticator) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: if we're doing more than validation, rename to CompileAndValidateJWTAuthenticator?
will celMapper be nil if there are no CEL rules? will fieldErr be non-nil if the structuredauthn feature is off and JWTAuthenticator contains cel rules?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
will celMapper be nil if there are no CEL rules? will fieldErr be non-nil if the structuredauthn feature is off and JWTAuthenticator contains cel rules?
celMapper
is not nil when there are no CEL rules. The subfields will be empty if there are no expressions defined for the field.- fieldErr will be non-nil if feature is off and any of the fields in
JWTAuthenticator
contain expressions. We have validation in all the fields that support an expression.
compiler := authenticationcel.NewCompiler(environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion())) | ||
mapper := &authenticationcel.CELMapper{} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if the feature's not enabled, probably don't need to construct these, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm.. I can't tell if celMapper is conditional on the feature gate (will be nil if the feature's off) or on whether CEL expressions were used for various things (subfields will be nil if there's no CEL group expression, etc) or both.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if the feature's not enabled, probably don't need to construct these, right?
right now I'm passing the struct to different validate funcs that set the subfields instead of returning subfields, so we're initializing this.
hmm.. I can't tell if celMapper is conditional on the feature gate (will be nil if the feature's off) or on whether CEL expressions were used for various things (subfields will be nil if there's no CEL group expression, etc) or both.
In the current implementation, if the feature gate is not set, the subfields that contain an expression will be nil but celMapper is not nil.
We can return nil from validateJWTAuthenticator
if feature gate is not set.
staging/src/k8s.io/apiserver/plugin/pkg/authenticator/token/oidc/oidc.go
Show resolved
Hide resolved
e851239
to
96d94ab
Compare
dca4357
to
905a09c
Compare
staging/src/k8s.io/apiserver/pkg/apis/apiserver/v1alpha1/types.go
Outdated
Show resolved
Hide resolved
Signed-off-by: Anish Ramasekar <anish.ramasekar@gmail.com>
905a09c
to
9d1cf54
Compare
staging/src/k8s.io/apiserver/pkg/apis/apiserver/validation/validation.go
Outdated
Show resolved
Hide resolved
} | ||
} | ||
|
||
compilationResults, err := validatePrefixClaimOrExpression(compiler, m.Username, fldPath.Child("username"), true, structuredAuthnFeatureEnabled) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to strengthen in 1.30: if we're using the same validatePrefixClaimOrExpression
function to construct the CEL mapper for both username and groups, how is it doing any kind of type checking that the result is a string (for username) or a string / string array / null (for groups)?
a username or groups expression of "true"
should fail type checking, but I don't think it would, I think it would fail at runtime
obviously, things returning claims directly we can't typecheck until runtime since claims are dynamic, but things returning cel literals can be typechecked
staging/src/k8s.io/apiserver/pkg/apis/apiserver/validation/validation.go
Outdated
Show resolved
Hide resolved
staging/src/k8s.io/apiserver/pkg/apis/apiserver/validation/validation.go
Outdated
Show resolved
Hide resolved
|
||
if err != nil { | ||
allErrs = append(allErrs, err) | ||
} else if structuredAuthnFeatureEnabled { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we shouldn't be able to get here if structuredAuthnFeatureEnabled is false, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we do compile the CEL expression even when feature gate isn’t enabled and don’t short circuit on error. This is done to aggregate all errors as part of validation.
Leaving this as-is based on the slack conversation.
if m.Groups.Prefix != nil && len(m.Groups.Claim) == 0 { | ||
allErrs = append(allErrs, field.Required(fldPath.Child("groups", "claim"), "non-empty claim name is required when prefix is set")) | ||
|
||
if structuredAuthnFeatureEnabled && len(compilationResults) > 0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it shouldn't be possible to have non-zero compilation results and the feature be off, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be possible for non-zero compilation results when the feature is off as we perform validation and compiling of expression always. This was done to aggregate all errors from validation and return to user.
staging/src/k8s.io/apiserver/plugin/pkg/authenticator/token/oidc/oidc.go
Outdated
Show resolved
Hide resolved
staging/src/k8s.io/apiserver/plugin/pkg/authenticator/token/oidc/oidc.go
Outdated
Show resolved
Hide resolved
switch val.Type().TypeName() { | ||
case celgo.StringType.TypeName(): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this needs cleanup in 1.30... I suspect there are more efficient ways to switch on the types natively in go, possibly like:
switch t := val.Value().(type) {
case nilType:
... no-op
case stringType:
if len(t) > 0 {
append
}
case []interface{}:
... iterate, safely cast, skip nil and empty string, append non-empty string, error on other types
case []ref.Val{}:
... iterate, safely cast, skip nil and empty string, append non-empty string, error on other types
default:
... error, unexpected type
}
|
||
extra := make(map[string][]string, len(evalResult)) | ||
for _, result := range evalResult { | ||
extraMapping, ok := result.ExpressionAccessor.(*authenticationcel.ExtraMappingCondition) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this cast is a code smell to clean up in 1.30 before beta... we're encapsulating the key information in the accessor, carrying it along to the evalresults, and then digging it back out here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed! Have a task in #121553 to try and use generics.
Signed-off-by: Anish Ramasekar <anish.ramasekar@gmail.com>
Signed-off-by: Anish Ramasekar <anish.ramasekar@gmail.com>
9d1cf54
to
cc190e0
Compare
/approve @enj has lgtm |
/lgtm |
LGTM label has been added. Git tree hash: b34c77509b763f829284ef76394d17d3538ccba4
|
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: aramase, enj, liggitt The full list of commands accepted by this bot can be found here. The pull request process is described here
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
JWTAuthenticator
inAuthenticationConfiguration
v1alpha1fixes #119235
/kind feature
/sig auth
/triage accepted
/milestone v1.29
/priority important-soon