apiserver/authconfig: wire CEL compiler through lower layers to allow sharing
Signed-off-by: Dr. Stefan Schimanski <stefan.schimanski@gmail.com> Kubernetes-commit: 4024390d8c8a19056ab7ced95eef5cce43c8096d
This commit is contained in:
parent
b907ccabbe
commit
4b46916a7b
|
@ -38,14 +38,13 @@ import (
|
|||
authenticationcel "k8s.io/apiserver/pkg/authentication/cel"
|
||||
authorizationcel "k8s.io/apiserver/pkg/authorization/cel"
|
||||
"k8s.io/apiserver/pkg/cel"
|
||||
"k8s.io/apiserver/pkg/cel/environment"
|
||||
"k8s.io/apiserver/pkg/features"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
"k8s.io/client-go/util/cert"
|
||||
)
|
||||
|
||||
// ValidateAuthenticationConfiguration validates a given AuthenticationConfiguration.
|
||||
func ValidateAuthenticationConfiguration(c *api.AuthenticationConfiguration, disallowedIssuers []string) field.ErrorList {
|
||||
func ValidateAuthenticationConfiguration(compiler authenticationcel.Compiler, c *api.AuthenticationConfiguration, disallowedIssuers []string) field.ErrorList {
|
||||
root := field.NewPath("jwt")
|
||||
var allErrs field.ErrorList
|
||||
|
||||
|
@ -62,7 +61,7 @@ func ValidateAuthenticationConfiguration(c *api.AuthenticationConfiguration, dis
|
|||
seenDiscoveryURLs := sets.New[string]()
|
||||
for i, a := range c.JWT {
|
||||
fldPath := root.Index(i)
|
||||
_, errs := validateJWTAuthenticator(a, fldPath, sets.New(disallowedIssuers...), utilfeature.DefaultFeatureGate.Enabled(features.StructuredAuthenticationConfiguration))
|
||||
_, errs := validateJWTAuthenticator(compiler, a, fldPath, sets.New(disallowedIssuers...), utilfeature.DefaultFeatureGate.Enabled(features.StructuredAuthenticationConfiguration))
|
||||
allErrs = append(allErrs, errs...)
|
||||
|
||||
if seenIssuers.Has(a.Issuer.URL) {
|
||||
|
@ -93,15 +92,13 @@ func ValidateAuthenticationConfiguration(c *api.AuthenticationConfiguration, dis
|
|||
// CompileAndValidateJWTAuthenticator validates a given JWTAuthenticator and returns a CELMapper with the compiled
|
||||
// CEL expressions for claim mappings and validation rules.
|
||||
// This is exported for use in oidc package.
|
||||
func CompileAndValidateJWTAuthenticator(authenticator api.JWTAuthenticator, disallowedIssuers []string) (authenticationcel.CELMapper, field.ErrorList) {
|
||||
return validateJWTAuthenticator(authenticator, nil, sets.New(disallowedIssuers...), utilfeature.DefaultFeatureGate.Enabled(features.StructuredAuthenticationConfiguration))
|
||||
func CompileAndValidateJWTAuthenticator(compiler authenticationcel.Compiler, authenticator api.JWTAuthenticator, disallowedIssuers []string) (authenticationcel.CELMapper, field.ErrorList) {
|
||||
return validateJWTAuthenticator(compiler, authenticator, nil, sets.New(disallowedIssuers...), utilfeature.DefaultFeatureGate.Enabled(features.StructuredAuthenticationConfiguration))
|
||||
}
|
||||
|
||||
func validateJWTAuthenticator(authenticator api.JWTAuthenticator, fldPath *field.Path, disallowedIssuers sets.Set[string], structuredAuthnFeatureEnabled bool) (authenticationcel.CELMapper, field.ErrorList) {
|
||||
func validateJWTAuthenticator(compiler authenticationcel.Compiler, authenticator api.JWTAuthenticator, fldPath *field.Path, disallowedIssuers sets.Set[string], structuredAuthnFeatureEnabled bool) (authenticationcel.CELMapper, field.ErrorList) {
|
||||
var allErrs field.ErrorList
|
||||
|
||||
// strictCost is set to true which enables the strict cost for CEL validation.
|
||||
compiler := authenticationcel.NewCompiler(environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion(), true))
|
||||
state := &validationState{}
|
||||
|
||||
allErrs = append(allErrs, validateIssuer(authenticator.Issuer, disallowedIssuers, fldPath.Child("issuer"), structuredAuthnFeatureEnabled)...)
|
||||
|
@ -616,7 +613,7 @@ func compileUserCELExpression(compiler authenticationcel.Compiler, expression au
|
|||
}
|
||||
|
||||
// ValidateAuthorizationConfiguration validates a given AuthorizationConfiguration.
|
||||
func ValidateAuthorizationConfiguration(fldPath *field.Path, c *api.AuthorizationConfiguration, knownTypes sets.String, repeatableTypes sets.String) field.ErrorList {
|
||||
func ValidateAuthorizationConfiguration(compiler authorizationcel.Compiler, fldPath *field.Path, c *api.AuthorizationConfiguration, knownTypes sets.String, repeatableTypes sets.String) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
|
||||
if len(c.Authorizers) == 0 {
|
||||
|
@ -657,7 +654,7 @@ func ValidateAuthorizationConfiguration(fldPath *field.Path, c *api.Authorizatio
|
|||
allErrs = append(allErrs, field.Required(fldPath.Child("webhook"), "required when type=Webhook"))
|
||||
continue
|
||||
}
|
||||
allErrs = append(allErrs, ValidateWebhookConfiguration(fldPath, a.Webhook)...)
|
||||
allErrs = append(allErrs, ValidateWebhookConfiguration(compiler, fldPath, a.Webhook)...)
|
||||
default:
|
||||
if a.Webhook != nil {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("webhook"), "non-null", "may only be specified when type=Webhook"))
|
||||
|
@ -668,7 +665,7 @@ func ValidateAuthorizationConfiguration(fldPath *field.Path, c *api.Authorizatio
|
|||
return allErrs
|
||||
}
|
||||
|
||||
func ValidateWebhookConfiguration(fldPath *field.Path, c *api.WebhookConfiguration) field.ErrorList {
|
||||
func ValidateWebhookConfiguration(compiler authorizationcel.Compiler, fldPath *field.Path, c *api.WebhookConfiguration) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
|
||||
if c.Timeout.Duration == 0 {
|
||||
|
@ -740,7 +737,7 @@ func ValidateWebhookConfiguration(fldPath *field.Path, c *api.WebhookConfigurati
|
|||
allErrs = append(allErrs, field.NotSupported(fldPath.Child("connectionInfo", "type"), c.ConnectionInfo, []string{api.AuthorizationWebhookConnectionInfoTypeInCluster, api.AuthorizationWebhookConnectionInfoTypeKubeConfigFile}))
|
||||
}
|
||||
|
||||
_, errs := compileMatchConditions(c.MatchConditions, fldPath, utilfeature.DefaultFeatureGate.Enabled(features.StructuredAuthorizationConfiguration))
|
||||
_, errs := compileMatchConditions(compiler, c.MatchConditions, fldPath, utilfeature.DefaultFeatureGate.Enabled(features.StructuredAuthorizationConfiguration))
|
||||
allErrs = append(allErrs, errs...)
|
||||
|
||||
return allErrs
|
||||
|
@ -748,11 +745,11 @@ func ValidateWebhookConfiguration(fldPath *field.Path, c *api.WebhookConfigurati
|
|||
|
||||
// ValidateAndCompileMatchConditions validates a given webhook's matchConditions.
|
||||
// This is exported for use in authz package.
|
||||
func ValidateAndCompileMatchConditions(matchConditions []api.WebhookMatchCondition) (*authorizationcel.CELMatcher, field.ErrorList) {
|
||||
return compileMatchConditions(matchConditions, nil, utilfeature.DefaultFeatureGate.Enabled(features.StructuredAuthorizationConfiguration))
|
||||
func ValidateAndCompileMatchConditions(compiler authorizationcel.Compiler, matchConditions []api.WebhookMatchCondition) (*authorizationcel.CELMatcher, field.ErrorList) {
|
||||
return compileMatchConditions(compiler, matchConditions, nil, utilfeature.DefaultFeatureGate.Enabled(features.StructuredAuthorizationConfiguration))
|
||||
}
|
||||
|
||||
func compileMatchConditions(matchConditions []api.WebhookMatchCondition, fldPath *field.Path, structuredAuthzFeatureEnabled bool) (*authorizationcel.CELMatcher, field.ErrorList) {
|
||||
func compileMatchConditions(compiler authorizationcel.Compiler, matchConditions []api.WebhookMatchCondition, fldPath *field.Path, structuredAuthzFeatureEnabled bool) (*authorizationcel.CELMatcher, field.ErrorList) {
|
||||
var allErrs field.ErrorList
|
||||
// should fail when match conditions are used without feature enabled
|
||||
if len(matchConditions) > 0 && !structuredAuthzFeatureEnabled {
|
||||
|
@ -763,8 +760,6 @@ func compileMatchConditions(matchConditions []api.WebhookMatchCondition, fldPath
|
|||
return nil, allErrs
|
||||
}
|
||||
|
||||
// strictCost is set to true which enables the strict cost for CEL validation.
|
||||
compiler := authorizationcel.NewCompiler(environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion(), true))
|
||||
seenExpressions := sets.NewString()
|
||||
var compilationResults []authorizationcel.CompilationResult
|
||||
var usesFieldSelector, usesLabelSelector bool
|
||||
|
|
|
@ -34,7 +34,7 @@ import (
|
|||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
api "k8s.io/apiserver/pkg/apis/apiserver"
|
||||
authenticationcel "k8s.io/apiserver/pkg/authentication/cel"
|
||||
"k8s.io/apiserver/pkg/cel/environment"
|
||||
authorizationcel "k8s.io/apiserver/pkg/authorization/cel"
|
||||
"k8s.io/apiserver/pkg/features"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
certutil "k8s.io/client-go/util/cert"
|
||||
|
@ -42,10 +42,6 @@ import (
|
|||
"k8s.io/utils/pointer"
|
||||
)
|
||||
|
||||
var (
|
||||
compiler = authenticationcel.NewCompiler(environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion(), true))
|
||||
)
|
||||
|
||||
func TestValidateAuthenticationConfiguration(t *testing.T) {
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.StructuredAuthenticationConfiguration, true)
|
||||
|
||||
|
@ -619,7 +615,7 @@ func TestValidateAuthenticationConfiguration(t *testing.T) {
|
|||
|
||||
for _, tt := range testCases {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := ValidateAuthenticationConfiguration(tt.in, tt.disallowedIssuers).ToAggregate()
|
||||
got := ValidateAuthenticationConfiguration(authenticationcel.NewDefaultCompiler(), tt.in, tt.disallowedIssuers).ToAggregate()
|
||||
if d := cmp.Diff(tt.want, errString(got)); d != "" {
|
||||
t.Fatalf("AuthenticationConfiguration validation mismatch (-want +got):\n%s", d)
|
||||
}
|
||||
|
@ -1044,7 +1040,7 @@ func TestValidateClaimValidationRules(t *testing.T) {
|
|||
for _, tt := range testCases {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
state := &validationState{}
|
||||
got := validateClaimValidationRules(compiler, state, tt.in, fldPath, tt.structuredAuthnFeatureEnabled).ToAggregate()
|
||||
got := validateClaimValidationRules(authenticationcel.NewDefaultCompiler(), state, tt.in, fldPath, tt.structuredAuthnFeatureEnabled).ToAggregate()
|
||||
if d := cmp.Diff(tt.want, errString(got)); d != "" {
|
||||
t.Fatalf("ClaimValidationRules validation mismatch (-want +got):\n%s", d)
|
||||
}
|
||||
|
@ -1572,7 +1568,7 @@ func TestValidateClaimMappings(t *testing.T) {
|
|||
for _, tt := range testCases {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
state := &validationState{usesEmailVerifiedClaim: tt.usesEmailVerifiedClaim}
|
||||
got := validateClaimMappings(compiler, state, tt.in, fldPath, tt.structuredAuthnFeatureEnabled).ToAggregate()
|
||||
got := validateClaimMappings(authenticationcel.NewDefaultCompiler(), state, tt.in, fldPath, tt.structuredAuthnFeatureEnabled).ToAggregate()
|
||||
if d := cmp.Diff(tt.want, errString(got)); d != "" {
|
||||
fmt.Println(errString(got))
|
||||
t.Fatalf("ClaimMappings validation mismatch (-want +got):\n%s", d)
|
||||
|
@ -1661,7 +1657,7 @@ func TestValidateUserValidationRules(t *testing.T) {
|
|||
for _, tt := range testCases {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
state := &validationState{}
|
||||
got := validateUserValidationRules(compiler, state, tt.in, fldPath, tt.structuredAuthnFeatureEnabled).ToAggregate()
|
||||
got := validateUserValidationRules(authenticationcel.NewDefaultCompiler(), state, tt.in, fldPath, tt.structuredAuthnFeatureEnabled).ToAggregate()
|
||||
if d := cmp.Diff(tt.want, errString(got)); d != "" {
|
||||
t.Fatalf("UserValidationRules validation mismatch (-want +got):\n%s", d)
|
||||
}
|
||||
|
@ -2458,7 +2454,7 @@ func TestValidateAuthorizationConfiguration(t *testing.T) {
|
|||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
errList := ValidateAuthorizationConfiguration(nil, &test.configuration, test.knownTypes, test.repeatableTypes)
|
||||
errList := ValidateAuthorizationConfiguration(authorizationcel.NewDefaultCompiler(), nil, &test.configuration, test.knownTypes, test.repeatableTypes)
|
||||
if len(errList) != len(test.expectedErrList) {
|
||||
t.Errorf("expected %d errs, got %d, errors %v", len(test.expectedErrList), len(errList), errList)
|
||||
}
|
||||
|
@ -2568,7 +2564,7 @@ func TestValidateAndCompileMatchConditions(t *testing.T) {
|
|||
for _, tt := range testCases {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.StructuredAuthorizationConfiguration, tt.featureEnabled)
|
||||
celMatcher, errList := ValidateAndCompileMatchConditions(tt.matchConditions)
|
||||
celMatcher, errList := ValidateAndCompileMatchConditions(authorizationcel.NewDefaultCompiler(), tt.matchConditions)
|
||||
if len(tt.expectedErr) == 0 && len(tt.matchConditions) > 0 && len(errList) == 0 && celMatcher == nil {
|
||||
t.Errorf("celMatcher should not be nil when there are matchCondition and no error returned")
|
||||
}
|
||||
|
|
|
@ -39,6 +39,12 @@ type compiler struct {
|
|||
varEnvs map[string]*environment.EnvSet
|
||||
}
|
||||
|
||||
// NewDefaultCompiler returns a new Compiler following the default compatibility version.
|
||||
// Note: the compiler construction depends on feature gates and the compatibility version to be initialized.
|
||||
func NewDefaultCompiler() Compiler {
|
||||
return NewCompiler(environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion(), true))
|
||||
}
|
||||
|
||||
// NewCompiler returns a new Compiler.
|
||||
func NewCompiler(env *environment.EnvSet) Compiler {
|
||||
return &compiler{
|
||||
|
|
|
@ -23,7 +23,6 @@ import (
|
|||
|
||||
authenticationv1 "k8s.io/api/authentication/v1"
|
||||
apiservercel "k8s.io/apiserver/pkg/cel"
|
||||
"k8s.io/apiserver/pkg/cel/environment"
|
||||
)
|
||||
|
||||
func TestCompileClaimsExpression(t *testing.T) {
|
||||
|
@ -57,12 +56,10 @@ func TestCompileClaimsExpression(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
compiler := NewCompiler(environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion(), true))
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
for _, expressionAccessor := range tc.expressionAccessors {
|
||||
_, err := compiler.CompileClaimsExpression(expressionAccessor)
|
||||
_, err := NewDefaultCompiler().CompileClaimsExpression(expressionAccessor)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
@ -86,12 +83,10 @@ func TestCompileUserExpression(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
compiler := NewCompiler(environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion(), true))
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
for _, expressionAccessor := range tc.expressionAccessors {
|
||||
_, err := compiler.CompileUserExpression(expressionAccessor)
|
||||
_, err := NewDefaultCompiler().CompileUserExpression(expressionAccessor)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
@ -135,12 +130,10 @@ func TestCompileClaimsExpressionError(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
compiler := NewCompiler(environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion(), true))
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
for _, expressionAccessor := range tc.expressionAccessors {
|
||||
_, err := compiler.CompileClaimsExpression(expressionAccessor)
|
||||
_, err := NewDefaultCompiler().CompileClaimsExpression(expressionAccessor)
|
||||
if err == nil {
|
||||
t.Errorf("expected error but got nil")
|
||||
}
|
||||
|
@ -205,12 +198,10 @@ func TestCompileUserExpressionError(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
compiler := NewCompiler(environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion(), true))
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
for _, expressionAccessor := range tc.expressionAccessors {
|
||||
_, err := compiler.CompileUserExpression(expressionAccessor)
|
||||
_, err := NewDefaultCompiler().CompileUserExpression(expressionAccessor)
|
||||
if err == nil {
|
||||
t.Errorf("expected error but got nil")
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ import (
|
|||
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||
authorizationcel "k8s.io/apiserver/pkg/authorization/cel"
|
||||
"k8s.io/apiserver/plugin/pkg/authorizer/webhook"
|
||||
authorizationclient "k8s.io/client-go/kubernetes/typed/authorization/v1"
|
||||
)
|
||||
|
@ -31,6 +32,9 @@ import (
|
|||
type DelegatingAuthorizerConfig struct {
|
||||
SubjectAccessReviewClient authorizationclient.AuthorizationV1Interface
|
||||
|
||||
// Compiler is the CEL compiler to use for evaluating policies. If nil, a default compiler will be used.
|
||||
Compiler authorizationcel.Compiler
|
||||
|
||||
// AllowCacheTTL is the length of time that a successful authorization response will be cached
|
||||
AllowCacheTTL time.Duration
|
||||
|
||||
|
@ -48,6 +52,10 @@ func (c DelegatingAuthorizerConfig) New() (authorizer.Authorizer, error) {
|
|||
if c.WebhookRetryBackoff == nil {
|
||||
return nil, errors.New("retry backoff parameters for delegating authorization webhook has not been specified")
|
||||
}
|
||||
compiler := c.Compiler
|
||||
if compiler == nil {
|
||||
compiler = authorizationcel.NewDefaultCompiler()
|
||||
}
|
||||
|
||||
return webhook.NewFromInterface(
|
||||
c.SubjectAccessReviewClient,
|
||||
|
@ -56,5 +64,6 @@ func (c DelegatingAuthorizerConfig) New() (authorizer.Authorizer, error) {
|
|||
*c.WebhookRetryBackoff,
|
||||
authorizer.DecisionNoOpinion,
|
||||
NewDelegatingAuthorizerMetrics(),
|
||||
compiler,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -65,6 +65,12 @@ type compiler struct {
|
|||
envSet *environment.EnvSet
|
||||
}
|
||||
|
||||
// NewDefaultCompiler returns a new Compiler following the default compatibility version.
|
||||
// Note: the compiler construction depends on feature gates and the compatibility version to be initialized.
|
||||
func NewDefaultCompiler() Compiler {
|
||||
return NewCompiler(environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion(), true))
|
||||
}
|
||||
|
||||
// NewCompiler returns a new Compiler.
|
||||
func NewCompiler(env *environment.EnvSet) Compiler {
|
||||
return &compiler{
|
||||
|
|
|
@ -25,7 +25,6 @@ import (
|
|||
v1 "k8s.io/api/authorization/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
apiservercel "k8s.io/apiserver/pkg/cel"
|
||||
"k8s.io/apiserver/pkg/cel/environment"
|
||||
genericfeatures "k8s.io/apiserver/pkg/features"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
||||
|
@ -100,7 +99,10 @@ func TestCompileCELExpression(t *testing.T) {
|
|||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.AuthorizeWithSelectors, tc.authorizeWithSelectorsEnabled)
|
||||
compiler := NewCompiler(environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion(), true))
|
||||
|
||||
// create new compiler because it depends on the feature gate
|
||||
compiler := NewDefaultCompiler()
|
||||
|
||||
_, err := compiler.CompileCELExpression(&SubjectAccessReviewMatchCondition{
|
||||
Expression: tc.expression,
|
||||
})
|
||||
|
|
|
@ -86,6 +86,12 @@ type Options struct {
|
|||
// Optional http.Client used to make all requests to the remote issuer. Mutually exclusive with CAContentProvider.
|
||||
Client *http.Client
|
||||
|
||||
// Optional CEL compiler used to compile the CEL expressions. This is useful to use a shared instance
|
||||
// of the compiler as these compilers holding a CEL environment are expensive to create. If not provided,
|
||||
// a default compiler will be created.
|
||||
// Note: the compiler construction depends on feature gates and the compatibility version to be initialized.
|
||||
Compiler authenticationcel.Compiler
|
||||
|
||||
// SupportedSigningAlgs sets the accepted set of JOSE signing algorithms that
|
||||
// can be used by the provider to sign tokens.
|
||||
//
|
||||
|
@ -245,7 +251,11 @@ type AuthenticatorTokenWithHealthCheck interface {
|
|||
// Thus, once the lifecycleCtx is canceled, the authenticator must not be used.
|
||||
// A caller may check if the authenticator is healthy by calling the HealthCheck method.
|
||||
func New(lifecycleCtx context.Context, opts Options) (AuthenticatorTokenWithHealthCheck, error) {
|
||||
celMapper, fieldErr := apiservervalidation.CompileAndValidateJWTAuthenticator(opts.JWTAuthenticator, opts.DisallowedIssuers)
|
||||
compiler := opts.Compiler
|
||||
if compiler == nil {
|
||||
compiler = authenticationcel.NewDefaultCompiler()
|
||||
}
|
||||
celMapper, fieldErr := apiservervalidation.CompileAndValidateJWTAuthenticator(compiler, opts.JWTAuthenticator, opts.DisallowedIssuers)
|
||||
if err := fieldErr.ToAggregate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -122,7 +122,7 @@ func TestAuthorizerMetrics(t *testing.T) {
|
|||
defer server.Close()
|
||||
|
||||
fakeAuthzMetrics := &fakeAuthorizerMetrics{}
|
||||
wh, err := newV1Authorizer(server.URL, scenario.clientCert, scenario.clientKey, scenario.clientCA, 0, fakeAuthzMetrics, []apiserver.WebhookMatchCondition{}, "")
|
||||
wh, err := newV1Authorizer(server.URL, scenario.clientCert, scenario.clientKey, scenario.clientCA, 0, fakeAuthzMetrics, cel.NewDefaultCompiler(), []apiserver.WebhookMatchCondition{}, "")
|
||||
if err != nil {
|
||||
t.Error("failed to create client")
|
||||
return
|
||||
|
|
|
@ -82,8 +82,8 @@ type WebhookAuthorizer struct {
|
|||
}
|
||||
|
||||
// NewFromInterface creates a WebhookAuthorizer using the given subjectAccessReview client
|
||||
func NewFromInterface(subjectAccessReview authorizationv1client.AuthorizationV1Interface, authorizedTTL, unauthorizedTTL time.Duration, retryBackoff wait.Backoff, decisionOnError authorizer.Decision, metrics metrics.AuthorizerMetrics) (*WebhookAuthorizer, error) {
|
||||
return newWithBackoff(&subjectAccessReviewV1Client{subjectAccessReview.RESTClient()}, authorizedTTL, unauthorizedTTL, retryBackoff, decisionOnError, nil, metrics, "")
|
||||
func NewFromInterface(subjectAccessReview authorizationv1client.AuthorizationV1Interface, authorizedTTL, unauthorizedTTL time.Duration, retryBackoff wait.Backoff, decisionOnError authorizer.Decision, metrics metrics.AuthorizerMetrics, compiler authorizationcel.Compiler) (*WebhookAuthorizer, error) {
|
||||
return newWithBackoff(&subjectAccessReviewV1Client{subjectAccessReview.RESTClient()}, authorizedTTL, unauthorizedTTL, retryBackoff, decisionOnError, nil, metrics, compiler, "")
|
||||
}
|
||||
|
||||
// New creates a new WebhookAuthorizer from the provided kubeconfig file.
|
||||
|
@ -105,18 +105,18 @@ func NewFromInterface(subjectAccessReview authorizationv1client.AuthorizationV1I
|
|||
//
|
||||
// For additional HTTP configuration, refer to the kubeconfig documentation
|
||||
// https://kubernetes.io/docs/user-guide/kubeconfig-file/.
|
||||
func New(config *rest.Config, version string, authorizedTTL, unauthorizedTTL time.Duration, retryBackoff wait.Backoff, decisionOnError authorizer.Decision, matchConditions []apiserver.WebhookMatchCondition, name string, metrics metrics.AuthorizerMetrics) (*WebhookAuthorizer, error) {
|
||||
func New(config *rest.Config, version string, authorizedTTL, unauthorizedTTL time.Duration, retryBackoff wait.Backoff, decisionOnError authorizer.Decision, matchConditions []apiserver.WebhookMatchCondition, name string, metrics metrics.AuthorizerMetrics, compiler authorizationcel.Compiler) (*WebhookAuthorizer, error) {
|
||||
subjectAccessReview, err := subjectAccessReviewInterfaceFromConfig(config, version, retryBackoff)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newWithBackoff(subjectAccessReview, authorizedTTL, unauthorizedTTL, retryBackoff, decisionOnError, matchConditions, metrics, name)
|
||||
return newWithBackoff(subjectAccessReview, authorizedTTL, unauthorizedTTL, retryBackoff, decisionOnError, matchConditions, metrics, compiler, name)
|
||||
}
|
||||
|
||||
// newWithBackoff allows tests to skip the sleep.
|
||||
func newWithBackoff(subjectAccessReview subjectAccessReviewer, authorizedTTL, unauthorizedTTL time.Duration, retryBackoff wait.Backoff, decisionOnError authorizer.Decision, matchConditions []apiserver.WebhookMatchCondition, am metrics.AuthorizerMetrics, name string) (*WebhookAuthorizer, error) {
|
||||
func newWithBackoff(subjectAccessReview subjectAccessReviewer, authorizedTTL, unauthorizedTTL time.Duration, retryBackoff wait.Backoff, decisionOnError authorizer.Decision, matchConditions []apiserver.WebhookMatchCondition, am metrics.AuthorizerMetrics, compiler authorizationcel.Compiler, name string) (*WebhookAuthorizer, error) {
|
||||
// compile all expressions once in validation and save the results to be used for eval later
|
||||
cm, fieldErr := apiservervalidation.ValidateAndCompileMatchConditions(matchConditions)
|
||||
cm, fieldErr := apiservervalidation.ValidateAndCompileMatchConditions(compiler, matchConditions)
|
||||
if err := fieldErr.ToAggregate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -34,8 +34,6 @@ import (
|
|||
"text/template"
|
||||
"time"
|
||||
|
||||
utiltesting "k8s.io/client-go/util/testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
|
||||
authorizationv1 "k8s.io/api/authorization/v1"
|
||||
|
@ -47,12 +45,13 @@ import (
|
|||
"k8s.io/apiserver/pkg/apis/apiserver"
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||
celmetrics "k8s.io/apiserver/pkg/authorization/cel"
|
||||
authorizationcel "k8s.io/apiserver/pkg/authorization/cel"
|
||||
"k8s.io/apiserver/pkg/features"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
webhookutil "k8s.io/apiserver/pkg/util/webhook"
|
||||
"k8s.io/apiserver/plugin/pkg/authorizer/webhook/metrics"
|
||||
v1 "k8s.io/client-go/tools/clientcmd/api/v1"
|
||||
utiltesting "k8s.io/client-go/util/testing"
|
||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
||||
"k8s.io/component-base/metrics/legacyregistry"
|
||||
"k8s.io/component-base/metrics/testutil"
|
||||
|
@ -217,7 +216,7 @@ current-context: default
|
|||
if err != nil {
|
||||
return fmt.Errorf("error building sar client: %v", err)
|
||||
}
|
||||
_, err = newWithBackoff(sarClient, 0, 0, testRetryBackoff, authorizer.DecisionNoOpinion, []apiserver.WebhookMatchCondition{}, noopAuthorizerMetrics(), "")
|
||||
_, err = newWithBackoff(sarClient, 0, 0, testRetryBackoff, authorizer.DecisionNoOpinion, []apiserver.WebhookMatchCondition{}, noopAuthorizerMetrics(), authorizationcel.NewDefaultCompiler(), "")
|
||||
return err
|
||||
}()
|
||||
if err != nil && !tt.wantErr {
|
||||
|
@ -337,7 +336,7 @@ func (m *mockV1Service) HTTPStatusCode() int { return m.statusCode }
|
|||
|
||||
// newV1Authorizer creates a temporary kubeconfig file from the provided arguments and attempts to load
|
||||
// a new WebhookAuthorizer from it.
|
||||
func newV1Authorizer(callbackURL string, clientCert, clientKey, ca []byte, cacheTime time.Duration, metrics metrics.AuthorizerMetrics, expressions []apiserver.WebhookMatchCondition, authzName string) (*WebhookAuthorizer, error) {
|
||||
func newV1Authorizer(callbackURL string, clientCert, clientKey, ca []byte, cacheTime time.Duration, metrics metrics.AuthorizerMetrics, compiler authorizationcel.Compiler, expressions []apiserver.WebhookMatchCondition, authzName string) (*WebhookAuthorizer, error) {
|
||||
tempfile, err := ioutil.TempFile("", "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -367,7 +366,7 @@ func newV1Authorizer(callbackURL string, clientCert, clientKey, ca []byte, cache
|
|||
if err != nil {
|
||||
return nil, fmt.Errorf("error building sar client: %v", err)
|
||||
}
|
||||
return newWithBackoff(sarClient, cacheTime, cacheTime, testRetryBackoff, authorizer.DecisionNoOpinion, expressions, metrics, authzName)
|
||||
return newWithBackoff(sarClient, cacheTime, cacheTime, testRetryBackoff, authorizer.DecisionNoOpinion, expressions, metrics, compiler, authzName)
|
||||
}
|
||||
|
||||
func TestV1TLSConfig(t *testing.T) {
|
||||
|
@ -426,7 +425,7 @@ func TestV1TLSConfig(t *testing.T) {
|
|||
}
|
||||
defer server.Close()
|
||||
|
||||
wh, err := newV1Authorizer(server.URL, tt.clientCert, tt.clientKey, tt.clientCA, 0, noopAuthorizerMetrics(), []apiserver.WebhookMatchCondition{}, "")
|
||||
wh, err := newV1Authorizer(server.URL, tt.clientCert, tt.clientKey, tt.clientCA, 0, noopAuthorizerMetrics(), authorizationcel.NewDefaultCompiler(), []apiserver.WebhookMatchCondition{}, "")
|
||||
if err != nil {
|
||||
t.Errorf("%s: failed to create client: %v", tt.test, err)
|
||||
return
|
||||
|
@ -491,7 +490,7 @@ func TestV1Webhook(t *testing.T) {
|
|||
}
|
||||
defer s.Close()
|
||||
|
||||
wh, err := newV1Authorizer(s.URL, clientCert, clientKey, caCert, 0, noopAuthorizerMetrics(), []apiserver.WebhookMatchCondition{}, "")
|
||||
wh, err := newV1Authorizer(s.URL, clientCert, clientKey, caCert, 0, noopAuthorizerMetrics(), authorizationcel.NewDefaultCompiler(), []apiserver.WebhookMatchCondition{}, "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -598,7 +597,7 @@ func TestV1WebhookCache(t *testing.T) {
|
|||
},
|
||||
}
|
||||
// Create an authorizer that caches successful responses "forever" (100 days).
|
||||
wh, err := newV1Authorizer(s.URL, clientCert, clientKey, caCert, 2400*time.Hour, noopAuthorizerMetrics(), expressions, "")
|
||||
wh, err := newV1Authorizer(s.URL, clientCert, clientKey, caCert, 2400*time.Hour, noopAuthorizerMetrics(), authorizationcel.NewDefaultCompiler(), expressions, "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -693,7 +692,6 @@ func TestV1WebhookCache(t *testing.T) {
|
|||
|
||||
// TestStructuredAuthzConfigFeatureEnablement verifies cel expressions can only be used when feature is enabled
|
||||
func TestStructuredAuthzConfigFeatureEnablement(t *testing.T) {
|
||||
|
||||
service := new(mockV1Service)
|
||||
service.statusCode = 200
|
||||
service.Allow()
|
||||
|
@ -804,7 +802,11 @@ func TestStructuredAuthzConfigFeatureEnablement(t *testing.T) {
|
|||
t.Run(test.name, func(t *testing.T) {
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.StructuredAuthorizationConfiguration, test.featureEnabled)
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.AuthorizeWithSelectors, test.selectorEnabled)
|
||||
wh, err := newV1Authorizer(s.URL, clientCert, clientKey, caCert, 0, noopAuthorizerMetrics(), test.expressions, "")
|
||||
|
||||
// create new compiler because it depends on the feature gate
|
||||
compiler := authorizationcel.NewDefaultCompiler()
|
||||
|
||||
wh, err := newV1Authorizer(s.URL, clientCert, clientKey, caCert, 0, noopAuthorizerMetrics(), compiler, test.expressions, "")
|
||||
if test.expectedCompileErr && err == nil {
|
||||
t.Fatalf("%d: Expected compile error", i)
|
||||
} else if !test.expectedCompileErr && err != nil {
|
||||
|
@ -910,13 +912,13 @@ func TestWebhookMetrics(t *testing.T) {
|
|||
|
||||
for _, tt := range testCases {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
celmetrics.ResetMetricsForTest()
|
||||
defer celmetrics.ResetMetricsForTest()
|
||||
wh1, err := newV1Authorizer(s.URL, clientCert, clientKey, caCert, 0, celAuthorizerMetrics(), tt.expressions1, "wh1.example.com")
|
||||
authorizationcel.ResetMetricsForTest()
|
||||
defer authorizationcel.ResetMetricsForTest()
|
||||
wh1, err := newV1Authorizer(s.URL, clientCert, clientKey, caCert, 0, celAuthorizerMetrics(), authorizationcel.NewDefaultCompiler(), tt.expressions1, "wh1.example.com")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
wh2, err := newV1Authorizer(s.URL, clientCert, clientKey, caCert, 0, celAuthorizerMetrics(), tt.expressions2, "wh2.example.com")
|
||||
wh2, err := newV1Authorizer(s.URL, clientCert, clientKey, caCert, 0, celAuthorizerMetrics(), authorizationcel.NewDefaultCompiler(), tt.expressions2, "wh2.example.com")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -1092,7 +1094,7 @@ func benchmarkNewWebhookAuthorizer(b *testing.B, expressions []apiserver.Webhook
|
|||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
// Create an authorizer with or without expressions to compile
|
||||
_, err := newV1Authorizer(s.URL, clientCert, clientKey, caCert, 0, noopAuthorizerMetrics(), expressions, "")
|
||||
_, err := newV1Authorizer(s.URL, clientCert, clientKey, caCert, 0, noopAuthorizerMetrics(), authorizationcel.NewDefaultCompiler(), expressions, "")
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
@ -1122,7 +1124,7 @@ func benchmarkWebhookAuthorize(b *testing.B, expressions []apiserver.WebhookMatc
|
|||
defer s.Close()
|
||||
featuregatetesting.SetFeatureGateDuringTest(b, utilfeature.DefaultFeatureGate, features.StructuredAuthorizationConfiguration, featureEnabled)
|
||||
// Create an authorizer with or without expressions to compile
|
||||
wh, err := newV1Authorizer(s.URL, clientCert, clientKey, caCert, 0, noopAuthorizerMetrics(), expressions, "")
|
||||
wh, err := newV1Authorizer(s.URL, clientCert, clientKey, caCert, 0, noopAuthorizerMetrics(), authorizationcel.NewDefaultCompiler(), expressions, "")
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
@ -1409,7 +1411,7 @@ func TestV1WebhookMatchConditions(t *testing.T) {
|
|||
|
||||
for i, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
wh, err := newV1Authorizer(s.URL, clientCert, clientKey, caCert, 0, noopAuthorizerMetrics(), test.expressions, "")
|
||||
wh, err := newV1Authorizer(s.URL, clientCert, clientKey, caCert, 0, noopAuthorizerMetrics(), authorizationcel.NewDefaultCompiler(), test.expressions, "")
|
||||
if len(test.expectedCompileErr) > 0 && err == nil {
|
||||
t.Fatalf("%d: Expected compile error", i)
|
||||
} else if len(test.expectedCompileErr) == 0 && err != nil {
|
||||
|
@ -1448,12 +1450,12 @@ func noopAuthorizerMetrics() metrics.AuthorizerMetrics {
|
|||
|
||||
func celAuthorizerMetrics() metrics.AuthorizerMetrics {
|
||||
return celAuthorizerMetricsType{
|
||||
MatcherMetrics: celmetrics.NewMatcherMetrics(),
|
||||
MatcherMetrics: authorizationcel.NewMatcherMetrics(),
|
||||
}
|
||||
}
|
||||
|
||||
type celAuthorizerMetricsType struct {
|
||||
metrics.NoopRequestMetrics
|
||||
metrics.NoopWebhookMetrics
|
||||
celmetrics.MatcherMetrics
|
||||
authorizationcel.MatcherMetrics
|
||||
}
|
||||
|
|
|
@ -41,6 +41,7 @@ import (
|
|||
authzconfig "k8s.io/apiserver/pkg/apis/apiserver"
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||
authorizationcel "k8s.io/apiserver/pkg/authorization/cel"
|
||||
webhookutil "k8s.io/apiserver/pkg/util/webhook"
|
||||
v1 "k8s.io/client-go/tools/clientcmd/api/v1"
|
||||
)
|
||||
|
@ -197,7 +198,7 @@ current-context: default
|
|||
if err != nil {
|
||||
return fmt.Errorf("error building sar client: %v", err)
|
||||
}
|
||||
_, err = newWithBackoff(sarClient, 0, 0, testRetryBackoff, authorizer.DecisionNoOpinion, []authzconfig.WebhookMatchCondition{}, noopAuthorizerMetrics(), "")
|
||||
_, err = newWithBackoff(sarClient, 0, 0, testRetryBackoff, authorizer.DecisionNoOpinion, []authzconfig.WebhookMatchCondition{}, noopAuthorizerMetrics(), authorizationcel.NewDefaultCompiler(), "")
|
||||
return err
|
||||
}()
|
||||
if err != nil && !tt.wantErr {
|
||||
|
@ -340,7 +341,7 @@ func newV1beta1Authorizer(callbackURL string, clientCert, clientKey, ca []byte,
|
|||
if err != nil {
|
||||
return nil, fmt.Errorf("error building sar client: %v", err)
|
||||
}
|
||||
return newWithBackoff(sarClient, cacheTime, cacheTime, testRetryBackoff, authorizer.DecisionNoOpinion, []authzconfig.WebhookMatchCondition{}, noopAuthorizerMetrics(), "")
|
||||
return newWithBackoff(sarClient, cacheTime, cacheTime, testRetryBackoff, authorizer.DecisionNoOpinion, []authzconfig.WebhookMatchCondition{}, noopAuthorizerMetrics(), authorizationcel.NewDefaultCompiler(), "")
|
||||
}
|
||||
|
||||
func TestV1beta1TLSConfig(t *testing.T) {
|
||||
|
|
Loading…
Reference in New Issue