KEP-3221: Promote StructuredAuthorizationConfiguration to GA

This commit is contained in:
Jordan Liggitt 2024-10-17 21:48:30 -04:00
parent ded7ad554e
commit ad808e609a
No known key found for this signature in database
13 changed files with 496 additions and 81 deletions

View File

@ -308,6 +308,7 @@ var defaultVersionedKubernetesFeatureGates = map[featuregate.Feature]featuregate
genericfeatures.StructuredAuthorizationConfiguration: {
{Version: version.MustParse("1.29"), Default: false, PreRelease: featuregate.Alpha},
{Version: version.MustParse("1.30"), Default: true, PreRelease: featuregate.Beta},
{Version: version.MustParse("1.32"), Default: true, PreRelease: featuregate.GA, LockToDefault: true},
},
genericfeatures.UnauthenticatedHTTP2DOSMitigation: {

View File

@ -265,6 +265,45 @@ apiVersion: apiserver.config.k8s.io/v1beta1
kind: AuthorizationConfiguration
authorizers:
- type: Webhook
`),
expectConfig: &api.AuthorizationConfiguration{
Authorizers: []api.AuthorizerConfiguration{{Type: "Webhook"}},
},
},
{
name: "v1 - json",
data: []byte(`{
"apiVersion":"apiserver.config.k8s.io/v1",
"kind":"AuthorizationConfiguration",
"authorizers":[{"type":"Webhook"}]}`),
expectConfig: &api.AuthorizationConfiguration{
Authorizers: []api.AuthorizerConfiguration{{Type: "Webhook"}},
},
},
{
name: "v1 - defaults",
data: []byte(`{
"apiVersion":"apiserver.config.k8s.io/v1",
"kind":"AuthorizationConfiguration",
"authorizers":[{"type":"Webhook","name":"default","webhook":{}}]}`),
expectConfig: &api.AuthorizationConfiguration{
Authorizers: []api.AuthorizerConfiguration{{
Type: "Webhook",
Name: "default",
Webhook: &api.WebhookConfiguration{
AuthorizedTTL: metav1.Duration{Duration: 5 * time.Minute},
UnauthorizedTTL: metav1.Duration{Duration: 30 * time.Second},
},
}},
},
},
{
name: "v1 - yaml",
data: []byte(`
apiVersion: apiserver.config.k8s.io/v1
kind: AuthorizationConfiguration
authorizers:
- type: Webhook
`),
expectConfig: &api.AuthorizationConfiguration{
Authorizers: []api.AuthorizerConfiguration{{Type: "Webhook"}},

View File

@ -48,3 +48,12 @@ func SetDefaults_KMSConfiguration(obj *KMSConfiguration) {
obj.CacheSize = &defaultCacheSize
}
}
func SetDefaults_WebhookConfiguration(obj *WebhookConfiguration) {
if obj.AuthorizedTTL.Duration == 0 {
obj.AuthorizedTTL.Duration = 5 * time.Minute
}
if obj.UnauthorizedTTL.Duration == 0 {
obj.UnauthorizedTTL.Duration = 30 * time.Second
}
}

View File

@ -47,6 +47,7 @@ func init() {
func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(SchemeGroupVersion,
&AdmissionConfiguration{},
&AuthorizationConfiguration{},
&EncryptionConfiguration{},
)
// also register into the v1 group as EncryptionConfig (due to a docs bug)

View File

@ -48,3 +48,122 @@ type AdmissionPluginConfiguration struct {
// +optional
Configuration *runtime.Unknown `json:"configuration"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type AuthorizationConfiguration struct {
metav1.TypeMeta
// Authorizers is an ordered list of authorizers to
// authorize requests against.
// This is similar to the --authorization-modes kube-apiserver flag
// Must be at least one.
Authorizers []AuthorizerConfiguration `json:"authorizers"`
}
const (
TypeWebhook AuthorizerType = "Webhook"
FailurePolicyNoOpinion string = "NoOpinion"
FailurePolicyDeny string = "Deny"
AuthorizationWebhookConnectionInfoTypeKubeConfigFile string = "KubeConfigFile"
AuthorizationWebhookConnectionInfoTypeInCluster string = "InClusterConfig"
)
type AuthorizerType string
type AuthorizerConfiguration struct {
// Type refers to the type of the authorizer
// "Webhook" is supported in the generic API server
// Other API servers may support additional authorizer
// types like Node, RBAC, ABAC, etc.
Type string `json:"type"`
// Name used to describe the webhook
// This is explicitly used in monitoring machinery for metrics
// Note: Names must be DNS1123 labels like `myauthorizername` or
// subdomains like `myauthorizer.example.domain`
// Required, with no default
Name string `json:"name"`
// Webhook defines the configuration for a Webhook authorizer
// Must be defined when Type=Webhook
// Must not be defined when Type!=Webhook
Webhook *WebhookConfiguration `json:"webhook,omitempty"`
}
type WebhookConfiguration struct {
// The duration to cache 'authorized' responses from the webhook
// authorizer.
// Same as setting `--authorization-webhook-cache-authorized-ttl` flag
// Default: 5m0s
AuthorizedTTL metav1.Duration `json:"authorizedTTL"`
// The duration to cache 'unauthorized' responses from the webhook
// authorizer.
// Same as setting `--authorization-webhook-cache-unauthorized-ttl` flag
// Default: 30s
UnauthorizedTTL metav1.Duration `json:"unauthorizedTTL"`
// Timeout for the webhook request
// Maximum allowed value is 30s.
// Required, no default value.
Timeout metav1.Duration `json:"timeout"`
// The API version of the authorization.k8s.io SubjectAccessReview to
// send to and expect from the webhook.
// Same as setting `--authorization-webhook-version` flag
// Valid values: v1beta1, v1
// Required, no default value
SubjectAccessReviewVersion string `json:"subjectAccessReviewVersion"`
// MatchConditionSubjectAccessReviewVersion specifies the SubjectAccessReview
// version the CEL expressions are evaluated against
// Valid values: v1
// Required, no default value
MatchConditionSubjectAccessReviewVersion string `json:"matchConditionSubjectAccessReviewVersion"`
// Controls the authorization decision when a webhook request fails to
// complete or returns a malformed response or errors evaluating
// matchConditions.
// Valid values:
// - NoOpinion: continue to subsequent authorizers to see if one of
// them allows the request
// - Deny: reject the request without consulting subsequent authorizers
// Required, with no default.
FailurePolicy string `json:"failurePolicy"`
// ConnectionInfo defines how we talk to the webhook
ConnectionInfo WebhookConnectionInfo `json:"connectionInfo"`
// matchConditions is a list of conditions that must be met for a request to be sent to this
// webhook. An empty list of matchConditions matches all requests.
// There are a maximum of 64 match conditions allowed.
//
// The exact matching logic is (in order):
// 1. If at least one matchCondition evaluates to FALSE, then the webhook is skipped.
// 2. If ALL matchConditions evaluate to TRUE, then the webhook is called.
// 3. If at least one matchCondition evaluates to an error (but none are FALSE):
// - If failurePolicy=Deny, then the webhook rejects the request
// - If failurePolicy=NoOpinion, then the error is ignored and the webhook is skipped
MatchConditions []WebhookMatchCondition `json:"matchConditions"`
}
type WebhookConnectionInfo struct {
// Controls how the webhook should communicate with the server.
// Valid values:
// - KubeConfigFile: use the file specified in kubeConfigFile to locate the
// server.
// - InClusterConfig: use the in-cluster configuration to call the
// SubjectAccessReview API hosted by kube-apiserver. This mode is not
// allowed for kube-apiserver.
Type string `json:"type"`
// Path to KubeConfigFile for connection info
// Required, if connectionInfo.Type is KubeConfig
KubeConfigFile *string `json:"kubeConfigFile"`
}
type WebhookMatchCondition struct {
// expression represents the expression which will be evaluated by CEL. Must evaluate to bool.
// CEL expressions have access to the contents of the SubjectAccessReview in v1 version.
// If version specified by subjectAccessReviewVersion in the request variable is v1beta1,
// the contents would be converted to the v1 version before evaluating the CEL expression.
//
// Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
Expression string `json:"expression"`
}

View File

@ -67,6 +67,26 @@ func RegisterConversions(s *runtime.Scheme) error {
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*AuthorizationConfiguration)(nil), (*apiserver.AuthorizationConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1_AuthorizationConfiguration_To_apiserver_AuthorizationConfiguration(a.(*AuthorizationConfiguration), b.(*apiserver.AuthorizationConfiguration), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*apiserver.AuthorizationConfiguration)(nil), (*AuthorizationConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_apiserver_AuthorizationConfiguration_To_v1_AuthorizationConfiguration(a.(*apiserver.AuthorizationConfiguration), b.(*AuthorizationConfiguration), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*AuthorizerConfiguration)(nil), (*apiserver.AuthorizerConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1_AuthorizerConfiguration_To_apiserver_AuthorizerConfiguration(a.(*AuthorizerConfiguration), b.(*apiserver.AuthorizerConfiguration), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*apiserver.AuthorizerConfiguration)(nil), (*AuthorizerConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_apiserver_AuthorizerConfiguration_To_v1_AuthorizerConfiguration(a.(*apiserver.AuthorizerConfiguration), b.(*AuthorizerConfiguration), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*EncryptionConfiguration)(nil), (*apiserver.EncryptionConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1_EncryptionConfiguration_To_apiserver_EncryptionConfiguration(a.(*EncryptionConfiguration), b.(*apiserver.EncryptionConfiguration), scope)
}); err != nil {
@ -137,6 +157,36 @@ func RegisterConversions(s *runtime.Scheme) error {
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*WebhookConfiguration)(nil), (*apiserver.WebhookConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1_WebhookConfiguration_To_apiserver_WebhookConfiguration(a.(*WebhookConfiguration), b.(*apiserver.WebhookConfiguration), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*apiserver.WebhookConfiguration)(nil), (*WebhookConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_apiserver_WebhookConfiguration_To_v1_WebhookConfiguration(a.(*apiserver.WebhookConfiguration), b.(*WebhookConfiguration), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*WebhookConnectionInfo)(nil), (*apiserver.WebhookConnectionInfo)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1_WebhookConnectionInfo_To_apiserver_WebhookConnectionInfo(a.(*WebhookConnectionInfo), b.(*apiserver.WebhookConnectionInfo), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*apiserver.WebhookConnectionInfo)(nil), (*WebhookConnectionInfo)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_apiserver_WebhookConnectionInfo_To_v1_WebhookConnectionInfo(a.(*apiserver.WebhookConnectionInfo), b.(*WebhookConnectionInfo), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*WebhookMatchCondition)(nil), (*apiserver.WebhookMatchCondition)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1_WebhookMatchCondition_To_apiserver_WebhookMatchCondition(a.(*WebhookMatchCondition), b.(*apiserver.WebhookMatchCondition), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*apiserver.WebhookMatchCondition)(nil), (*WebhookMatchCondition)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_apiserver_WebhookMatchCondition_To_v1_WebhookMatchCondition(a.(*apiserver.WebhookMatchCondition), b.(*WebhookMatchCondition), scope)
}); err != nil {
return err
}
return nil
}
@ -204,6 +254,50 @@ func Convert_apiserver_AdmissionPluginConfiguration_To_v1_AdmissionPluginConfigu
return autoConvert_apiserver_AdmissionPluginConfiguration_To_v1_AdmissionPluginConfiguration(in, out, s)
}
func autoConvert_v1_AuthorizationConfiguration_To_apiserver_AuthorizationConfiguration(in *AuthorizationConfiguration, out *apiserver.AuthorizationConfiguration, s conversion.Scope) error {
out.Authorizers = *(*[]apiserver.AuthorizerConfiguration)(unsafe.Pointer(&in.Authorizers))
return nil
}
// Convert_v1_AuthorizationConfiguration_To_apiserver_AuthorizationConfiguration is an autogenerated conversion function.
func Convert_v1_AuthorizationConfiguration_To_apiserver_AuthorizationConfiguration(in *AuthorizationConfiguration, out *apiserver.AuthorizationConfiguration, s conversion.Scope) error {
return autoConvert_v1_AuthorizationConfiguration_To_apiserver_AuthorizationConfiguration(in, out, s)
}
func autoConvert_apiserver_AuthorizationConfiguration_To_v1_AuthorizationConfiguration(in *apiserver.AuthorizationConfiguration, out *AuthorizationConfiguration, s conversion.Scope) error {
out.Authorizers = *(*[]AuthorizerConfiguration)(unsafe.Pointer(&in.Authorizers))
return nil
}
// Convert_apiserver_AuthorizationConfiguration_To_v1_AuthorizationConfiguration is an autogenerated conversion function.
func Convert_apiserver_AuthorizationConfiguration_To_v1_AuthorizationConfiguration(in *apiserver.AuthorizationConfiguration, out *AuthorizationConfiguration, s conversion.Scope) error {
return autoConvert_apiserver_AuthorizationConfiguration_To_v1_AuthorizationConfiguration(in, out, s)
}
func autoConvert_v1_AuthorizerConfiguration_To_apiserver_AuthorizerConfiguration(in *AuthorizerConfiguration, out *apiserver.AuthorizerConfiguration, s conversion.Scope) error {
out.Type = apiserver.AuthorizerType(in.Type)
out.Name = in.Name
out.Webhook = (*apiserver.WebhookConfiguration)(unsafe.Pointer(in.Webhook))
return nil
}
// Convert_v1_AuthorizerConfiguration_To_apiserver_AuthorizerConfiguration is an autogenerated conversion function.
func Convert_v1_AuthorizerConfiguration_To_apiserver_AuthorizerConfiguration(in *AuthorizerConfiguration, out *apiserver.AuthorizerConfiguration, s conversion.Scope) error {
return autoConvert_v1_AuthorizerConfiguration_To_apiserver_AuthorizerConfiguration(in, out, s)
}
func autoConvert_apiserver_AuthorizerConfiguration_To_v1_AuthorizerConfiguration(in *apiserver.AuthorizerConfiguration, out *AuthorizerConfiguration, s conversion.Scope) error {
out.Type = string(in.Type)
out.Name = in.Name
out.Webhook = (*WebhookConfiguration)(unsafe.Pointer(in.Webhook))
return nil
}
// Convert_apiserver_AuthorizerConfiguration_To_v1_AuthorizerConfiguration is an autogenerated conversion function.
func Convert_apiserver_AuthorizerConfiguration_To_v1_AuthorizerConfiguration(in *apiserver.AuthorizerConfiguration, out *AuthorizerConfiguration, s conversion.Scope) error {
return autoConvert_apiserver_AuthorizerConfiguration_To_v1_AuthorizerConfiguration(in, out, s)
}
func autoConvert_v1_EncryptionConfiguration_To_apiserver_EncryptionConfiguration(in *EncryptionConfiguration, out *apiserver.EncryptionConfiguration, s conversion.Scope) error {
out.Resources = *(*[]apiserver.ResourceConfiguration)(unsafe.Pointer(&in.Resources))
return nil
@ -361,3 +455,83 @@ func autoConvert_apiserver_SecretboxConfiguration_To_v1_SecretboxConfiguration(i
func Convert_apiserver_SecretboxConfiguration_To_v1_SecretboxConfiguration(in *apiserver.SecretboxConfiguration, out *SecretboxConfiguration, s conversion.Scope) error {
return autoConvert_apiserver_SecretboxConfiguration_To_v1_SecretboxConfiguration(in, out, s)
}
func autoConvert_v1_WebhookConfiguration_To_apiserver_WebhookConfiguration(in *WebhookConfiguration, out *apiserver.WebhookConfiguration, s conversion.Scope) error {
out.AuthorizedTTL = in.AuthorizedTTL
out.UnauthorizedTTL = in.UnauthorizedTTL
out.Timeout = in.Timeout
out.SubjectAccessReviewVersion = in.SubjectAccessReviewVersion
out.MatchConditionSubjectAccessReviewVersion = in.MatchConditionSubjectAccessReviewVersion
out.FailurePolicy = in.FailurePolicy
if err := Convert_v1_WebhookConnectionInfo_To_apiserver_WebhookConnectionInfo(&in.ConnectionInfo, &out.ConnectionInfo, s); err != nil {
return err
}
out.MatchConditions = *(*[]apiserver.WebhookMatchCondition)(unsafe.Pointer(&in.MatchConditions))
return nil
}
// Convert_v1_WebhookConfiguration_To_apiserver_WebhookConfiguration is an autogenerated conversion function.
func Convert_v1_WebhookConfiguration_To_apiserver_WebhookConfiguration(in *WebhookConfiguration, out *apiserver.WebhookConfiguration, s conversion.Scope) error {
return autoConvert_v1_WebhookConfiguration_To_apiserver_WebhookConfiguration(in, out, s)
}
func autoConvert_apiserver_WebhookConfiguration_To_v1_WebhookConfiguration(in *apiserver.WebhookConfiguration, out *WebhookConfiguration, s conversion.Scope) error {
out.AuthorizedTTL = in.AuthorizedTTL
out.UnauthorizedTTL = in.UnauthorizedTTL
out.Timeout = in.Timeout
out.SubjectAccessReviewVersion = in.SubjectAccessReviewVersion
out.MatchConditionSubjectAccessReviewVersion = in.MatchConditionSubjectAccessReviewVersion
out.FailurePolicy = in.FailurePolicy
if err := Convert_apiserver_WebhookConnectionInfo_To_v1_WebhookConnectionInfo(&in.ConnectionInfo, &out.ConnectionInfo, s); err != nil {
return err
}
out.MatchConditions = *(*[]WebhookMatchCondition)(unsafe.Pointer(&in.MatchConditions))
return nil
}
// Convert_apiserver_WebhookConfiguration_To_v1_WebhookConfiguration is an autogenerated conversion function.
func Convert_apiserver_WebhookConfiguration_To_v1_WebhookConfiguration(in *apiserver.WebhookConfiguration, out *WebhookConfiguration, s conversion.Scope) error {
return autoConvert_apiserver_WebhookConfiguration_To_v1_WebhookConfiguration(in, out, s)
}
func autoConvert_v1_WebhookConnectionInfo_To_apiserver_WebhookConnectionInfo(in *WebhookConnectionInfo, out *apiserver.WebhookConnectionInfo, s conversion.Scope) error {
out.Type = in.Type
out.KubeConfigFile = (*string)(unsafe.Pointer(in.KubeConfigFile))
return nil
}
// Convert_v1_WebhookConnectionInfo_To_apiserver_WebhookConnectionInfo is an autogenerated conversion function.
func Convert_v1_WebhookConnectionInfo_To_apiserver_WebhookConnectionInfo(in *WebhookConnectionInfo, out *apiserver.WebhookConnectionInfo, s conversion.Scope) error {
return autoConvert_v1_WebhookConnectionInfo_To_apiserver_WebhookConnectionInfo(in, out, s)
}
func autoConvert_apiserver_WebhookConnectionInfo_To_v1_WebhookConnectionInfo(in *apiserver.WebhookConnectionInfo, out *WebhookConnectionInfo, s conversion.Scope) error {
out.Type = in.Type
out.KubeConfigFile = (*string)(unsafe.Pointer(in.KubeConfigFile))
return nil
}
// Convert_apiserver_WebhookConnectionInfo_To_v1_WebhookConnectionInfo is an autogenerated conversion function.
func Convert_apiserver_WebhookConnectionInfo_To_v1_WebhookConnectionInfo(in *apiserver.WebhookConnectionInfo, out *WebhookConnectionInfo, s conversion.Scope) error {
return autoConvert_apiserver_WebhookConnectionInfo_To_v1_WebhookConnectionInfo(in, out, s)
}
func autoConvert_v1_WebhookMatchCondition_To_apiserver_WebhookMatchCondition(in *WebhookMatchCondition, out *apiserver.WebhookMatchCondition, s conversion.Scope) error {
out.Expression = in.Expression
return nil
}
// Convert_v1_WebhookMatchCondition_To_apiserver_WebhookMatchCondition is an autogenerated conversion function.
func Convert_v1_WebhookMatchCondition_To_apiserver_WebhookMatchCondition(in *WebhookMatchCondition, out *apiserver.WebhookMatchCondition, s conversion.Scope) error {
return autoConvert_v1_WebhookMatchCondition_To_apiserver_WebhookMatchCondition(in, out, s)
}
func autoConvert_apiserver_WebhookMatchCondition_To_v1_WebhookMatchCondition(in *apiserver.WebhookMatchCondition, out *WebhookMatchCondition, s conversion.Scope) error {
out.Expression = in.Expression
return nil
}
// Convert_apiserver_WebhookMatchCondition_To_v1_WebhookMatchCondition is an autogenerated conversion function.
func Convert_apiserver_WebhookMatchCondition_To_v1_WebhookMatchCondition(in *apiserver.WebhookMatchCondition, out *WebhookMatchCondition, s conversion.Scope) error {
return autoConvert_apiserver_WebhookMatchCondition_To_v1_WebhookMatchCondition(in, out, s)
}

View File

@ -100,6 +100,59 @@ func (in *AdmissionPluginConfiguration) DeepCopy() *AdmissionPluginConfiguration
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AuthorizationConfiguration) DeepCopyInto(out *AuthorizationConfiguration) {
*out = *in
out.TypeMeta = in.TypeMeta
if in.Authorizers != nil {
in, out := &in.Authorizers, &out.Authorizers
*out = make([]AuthorizerConfiguration, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AuthorizationConfiguration.
func (in *AuthorizationConfiguration) DeepCopy() *AuthorizationConfiguration {
if in == nil {
return nil
}
out := new(AuthorizationConfiguration)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *AuthorizationConfiguration) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AuthorizerConfiguration) DeepCopyInto(out *AuthorizerConfiguration) {
*out = *in
if in.Webhook != nil {
in, out := &in.Webhook, &out.Webhook
*out = new(WebhookConfiguration)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AuthorizerConfiguration.
func (in *AuthorizerConfiguration) DeepCopy() *AuthorizerConfiguration {
if in == nil {
return nil
}
out := new(AuthorizerConfiguration)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *EncryptionConfiguration) DeepCopyInto(out *EncryptionConfiguration) {
*out = *in
@ -279,3 +332,65 @@ func (in *SecretboxConfiguration) DeepCopy() *SecretboxConfiguration {
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *WebhookConfiguration) DeepCopyInto(out *WebhookConfiguration) {
*out = *in
out.AuthorizedTTL = in.AuthorizedTTL
out.UnauthorizedTTL = in.UnauthorizedTTL
out.Timeout = in.Timeout
in.ConnectionInfo.DeepCopyInto(&out.ConnectionInfo)
if in.MatchConditions != nil {
in, out := &in.MatchConditions, &out.MatchConditions
*out = make([]WebhookMatchCondition, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WebhookConfiguration.
func (in *WebhookConfiguration) DeepCopy() *WebhookConfiguration {
if in == nil {
return nil
}
out := new(WebhookConfiguration)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *WebhookConnectionInfo) DeepCopyInto(out *WebhookConnectionInfo) {
*out = *in
if in.KubeConfigFile != nil {
in, out := &in.KubeConfigFile, &out.KubeConfigFile
*out = new(string)
**out = **in
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WebhookConnectionInfo.
func (in *WebhookConnectionInfo) DeepCopy() *WebhookConnectionInfo {
if in == nil {
return nil
}
out := new(WebhookConnectionInfo)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *WebhookMatchCondition) DeepCopyInto(out *WebhookMatchCondition) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WebhookMatchCondition.
func (in *WebhookMatchCondition) DeepCopy() *WebhookMatchCondition {
if in == nil {
return nil
}
out := new(WebhookMatchCondition)
in.DeepCopyInto(out)
return out
}

View File

@ -29,10 +29,20 @@ import (
// Public to allow building arbitrary schemes.
// All generated defaulters are covering - they call all nested defaulters.
func RegisterDefaults(scheme *runtime.Scheme) error {
scheme.AddTypeDefaultingFunc(&AuthorizationConfiguration{}, func(obj interface{}) { SetObjectDefaults_AuthorizationConfiguration(obj.(*AuthorizationConfiguration)) })
scheme.AddTypeDefaultingFunc(&EncryptionConfiguration{}, func(obj interface{}) { SetObjectDefaults_EncryptionConfiguration(obj.(*EncryptionConfiguration)) })
return nil
}
func SetObjectDefaults_AuthorizationConfiguration(in *AuthorizationConfiguration) {
for i := range in.Authorizers {
a := &in.Authorizers[i]
if a.Webhook != nil {
SetDefaults_WebhookConfiguration(a.Webhook)
}
}
}
func SetObjectDefaults_EncryptionConfiguration(in *EncryptionConfiguration) {
for i := range in.Resources {
a := &in.Resources[i]

View File

@ -1686,8 +1686,6 @@ type (
)
func TestValidateAuthorizationConfiguration(t *testing.T) {
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.StructuredAuthorizationConfiguration, true)
badKubeConfigFile := "../some/relative/path/kubeconfig"
tempKubeConfigFile, err := os.CreateTemp("/tmp", "kubeconfig")
@ -2481,7 +2479,6 @@ func TestValidateAndCompileMatchConditions(t *testing.T) {
testCases := []struct {
name string
matchConditions []api.WebhookMatchCondition
featureEnabled bool
expectedErr string
}{
{
@ -2494,26 +2491,11 @@ func TestValidateAndCompileMatchConditions(t *testing.T) {
Expression: "request.user == 'admin'",
},
},
featureEnabled: true,
expectedErr: "",
},
{
name: "should fail when match conditions are used without feature enabled",
matchConditions: []api.WebhookMatchCondition{
{
Expression: "has(request.resourceAttributes) && request.resourceAttributes.namespace == 'kube-system'",
},
{
Expression: "request.user == 'admin'",
},
},
featureEnabled: false,
expectedErr: `matchConditions: Invalid value: "": matchConditions are not supported when StructuredAuthorizationConfiguration feature gate is disabled`,
},
{
name: "no matchConditions should not require feature enablement",
matchConditions: []api.WebhookMatchCondition{},
featureEnabled: false,
expectedErr: "",
},
{
@ -2523,7 +2505,6 @@ func TestValidateAndCompileMatchConditions(t *testing.T) {
Expression: " ",
},
},
featureEnabled: true,
expectedErr: "matchConditions[0].expression: Required value",
},
{
@ -2536,7 +2517,6 @@ func TestValidateAndCompileMatchConditions(t *testing.T) {
Expression: "request.user == 'admin'",
},
},
featureEnabled: true,
expectedErr: `matchConditions[1].expression: Duplicate value: "request.user == 'admin'"`,
},
{
@ -2546,7 +2526,6 @@ func TestValidateAndCompileMatchConditions(t *testing.T) {
Expression: "test",
},
},
featureEnabled: true,
expectedErr: "matchConditions[0].expression: Invalid value: \"test\": compilation failed: ERROR: <input>:1:1: undeclared reference to 'test' (in container '')\n | test\n | ^",
},
{
@ -2556,14 +2535,12 @@ func TestValidateAndCompileMatchConditions(t *testing.T) {
Expression: "request.user = 'test'",
},
},
featureEnabled: true,
expectedErr: "matchConditions[0].expression: Invalid value: \"request.user = 'test'\": compilation failed: ERROR: <input>:1:14: Syntax error: token recognition error at: '= '\n | request.user = 'test'\n | .............^\nERROR: <input>:1:16: Syntax error: extraneous input ''test'' expecting <EOF>\n | request.user = 'test'\n | ...............^",
},
}
for _, tt := range testCases {
t.Run(tt.name, func(t *testing.T) {
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.StructuredAuthorizationConfiguration, tt.featureEnabled)
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")

View File

@ -373,6 +373,7 @@ var defaultVersionedKubernetesFeatureGates = map[featuregate.Feature]featuregate
StructuredAuthorizationConfiguration: {
{Version: version.MustParse("1.29"), Default: false, PreRelease: featuregate.Alpha},
{Version: version.MustParse("1.30"), Default: true, PreRelease: featuregate.Beta},
{Version: version.MustParse("1.32"), Default: true, PreRelease: featuregate.GA, LockToDefault: true},
},
UnauthenticatedHTTP2DOSMitigation: {

View File

@ -590,7 +590,6 @@ func TestV1WebhookCache(t *testing.T) {
t.Fatal(err)
}
defer s.Close()
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.StructuredAuthorizationConfiguration, true)
expressions := []apiserver.WebhookMatchCondition{
{
Expression: "has(request.resourceAttributes) && request.resourceAttributes.namespace == 'kittensandponies'",
@ -711,7 +710,6 @@ func TestStructuredAuthzConfigFeatureEnablement(t *testing.T) {
expectedEvalErr bool
expectedDecision authorizer.Decision
expressions []apiserver.WebhookMatchCondition
featureEnabled bool
selectorEnabled bool
}
aliceAttr := authorizer.AttributesRecord{
@ -746,20 +744,6 @@ func TestStructuredAuthzConfigFeatureEnablement(t *testing.T) {
expectedCompileErr: false,
expectedDecision: authorizer.DecisionAllow,
expressions: []apiserver.WebhookMatchCondition{},
featureEnabled: false,
},
{
name: "should fail when match conditions are used without feature enabled",
attr: aliceAttr,
allow: false,
expectedCompileErr: true,
expectedDecision: authorizer.DecisionNoOpinion,
expressions: []apiserver.WebhookMatchCondition{
{
Expression: "request.user == 'alice'",
},
},
featureEnabled: false,
},
{
name: "feature enabled, match all against all expressions",
@ -793,14 +777,12 @@ func TestStructuredAuthzConfigFeatureEnablement(t *testing.T) {
Expression: "request.resourceAttributes.labelSelector.?requirements.orValue([]).exists(r, r.key=='baz' && r.operator=='In' && ('qux' in r.values))",
},
},
featureEnabled: true,
selectorEnabled: true,
},
}
for i, test := range tests {
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)
// create new compiler because it depends on the feature gate
@ -837,7 +819,6 @@ func TestWebhookMetrics(t *testing.T) {
t.Fatal(err)
}
defer s.Close()
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.StructuredAuthorizationConfiguration, true)
aliceAttr := authorizer.AttributesRecord{
User: &user.DefaultInfo{
@ -934,23 +915,13 @@ func TestWebhookMetrics(t *testing.T) {
}
}
func BenchmarkNoCELExpressionFeatureOff(b *testing.B) {
expressions := []apiserver.WebhookMatchCondition{}
b.Run("compile", func(b *testing.B) {
benchmarkNewWebhookAuthorizer(b, expressions, false)
})
b.Run("authorize", func(b *testing.B) {
benchmarkWebhookAuthorize(b, expressions, false)
})
}
func BenchmarkNoCELExpressionFeatureOn(b *testing.B) {
expressions := []apiserver.WebhookMatchCondition{}
b.Run("compile", func(b *testing.B) {
benchmarkNewWebhookAuthorizer(b, expressions, true)
benchmarkNewWebhookAuthorizer(b, expressions)
})
b.Run("authorize", func(b *testing.B) {
benchmarkWebhookAuthorize(b, expressions, true)
benchmarkWebhookAuthorize(b, expressions)
})
}
func BenchmarkWithOneCELExpressions(b *testing.B) {
@ -960,10 +931,10 @@ func BenchmarkWithOneCELExpressions(b *testing.B) {
},
}
b.Run("compile", func(b *testing.B) {
benchmarkNewWebhookAuthorizer(b, expressions, true)
benchmarkNewWebhookAuthorizer(b, expressions)
})
b.Run("authorize", func(b *testing.B) {
benchmarkWebhookAuthorize(b, expressions, true)
benchmarkWebhookAuthorize(b, expressions)
})
}
func BenchmarkWithOneCELExpressionsFalse(b *testing.B) {
@ -973,10 +944,10 @@ func BenchmarkWithOneCELExpressionsFalse(b *testing.B) {
},
}
b.Run("compile", func(b *testing.B) {
benchmarkNewWebhookAuthorizer(b, expressions, true)
benchmarkNewWebhookAuthorizer(b, expressions)
})
b.Run("authorize", func(b *testing.B) {
benchmarkWebhookAuthorize(b, expressions, true)
benchmarkWebhookAuthorize(b, expressions)
})
}
func BenchmarkWithTwoCELExpressions(b *testing.B) {
@ -989,10 +960,10 @@ func BenchmarkWithTwoCELExpressions(b *testing.B) {
},
}
b.Run("compile", func(b *testing.B) {
benchmarkNewWebhookAuthorizer(b, expressions, true)
benchmarkNewWebhookAuthorizer(b, expressions)
})
b.Run("authorize", func(b *testing.B) {
benchmarkWebhookAuthorize(b, expressions, true)
benchmarkWebhookAuthorize(b, expressions)
})
}
func BenchmarkWithTwoCELExpressionsFalse(b *testing.B) {
@ -1005,10 +976,10 @@ func BenchmarkWithTwoCELExpressionsFalse(b *testing.B) {
},
}
b.Run("compile", func(b *testing.B) {
benchmarkNewWebhookAuthorizer(b, expressions, true)
benchmarkNewWebhookAuthorizer(b, expressions)
})
b.Run("authorize", func(b *testing.B) {
benchmarkWebhookAuthorize(b, expressions, true)
benchmarkWebhookAuthorize(b, expressions)
})
}
func BenchmarkWithManyCELExpressions(b *testing.B) {
@ -1039,10 +1010,10 @@ func BenchmarkWithManyCELExpressions(b *testing.B) {
},
}
b.Run("compile", func(b *testing.B) {
benchmarkNewWebhookAuthorizer(b, expressions, true)
benchmarkNewWebhookAuthorizer(b, expressions)
})
b.Run("authorize", func(b *testing.B) {
benchmarkWebhookAuthorize(b, expressions, true)
benchmarkWebhookAuthorize(b, expressions)
})
}
func BenchmarkWithManyCELExpressionsFalse(b *testing.B) {
@ -1073,14 +1044,14 @@ func BenchmarkWithManyCELExpressionsFalse(b *testing.B) {
},
}
b.Run("compile", func(b *testing.B) {
benchmarkNewWebhookAuthorizer(b, expressions, true)
benchmarkNewWebhookAuthorizer(b, expressions)
})
b.Run("authorize", func(b *testing.B) {
benchmarkWebhookAuthorize(b, expressions, true)
benchmarkWebhookAuthorize(b, expressions)
})
}
func benchmarkNewWebhookAuthorizer(b *testing.B, expressions []apiserver.WebhookMatchCondition, featureEnabled bool) {
func benchmarkNewWebhookAuthorizer(b *testing.B, expressions []apiserver.WebhookMatchCondition) {
service := new(mockV1Service)
service.statusCode = 200
service.Allow()
@ -1089,7 +1060,6 @@ func benchmarkNewWebhookAuthorizer(b *testing.B, expressions []apiserver.Webhook
b.Fatal(err)
}
defer s.Close()
featuregatetesting.SetFeatureGateDuringTest(b, utilfeature.DefaultFeatureGate, features.StructuredAuthorizationConfiguration, featureEnabled)
b.ResetTimer()
for i := 0; i < b.N; i++ {
@ -1102,7 +1072,7 @@ func benchmarkNewWebhookAuthorizer(b *testing.B, expressions []apiserver.Webhook
b.StopTimer()
}
func benchmarkWebhookAuthorize(b *testing.B, expressions []apiserver.WebhookMatchCondition, featureEnabled bool) {
func benchmarkWebhookAuthorize(b *testing.B, expressions []apiserver.WebhookMatchCondition) {
attr := authorizer.AttributesRecord{
User: &user.DefaultInfo{
Name: "alice",
@ -1122,7 +1092,6 @@ func benchmarkWebhookAuthorize(b *testing.B, expressions []apiserver.WebhookMatc
b.Fatal(err)
}
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(), authorizationcel.NewDefaultCompiler(), expressions, "")
if err != nil {
@ -1142,7 +1111,6 @@ func benchmarkWebhookAuthorize(b *testing.B, expressions []apiserver.WebhookMatc
// TestV1WebhookMatchConditions verifies cel expressions are compiled and evaluated correctly
func TestV1WebhookMatchConditions(t *testing.T) {
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.StructuredAuthorizationConfiguration, true)
service := new(mockV1Service)
service.statusCode = 200
service.Allow()

View File

@ -1182,6 +1182,10 @@
lockToDefault: false
preRelease: Beta
version: "1.30"
- default: true
lockToDefault: true
preRelease: GA
version: "1.32"
- name: SupplementalGroupsPolicy
versionedSpecs:
- default: false

View File

@ -54,8 +54,6 @@ import (
)
func TestAuthzConfig(t *testing.T) {
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.StructuredAuthorizationConfiguration, true)
dir := t.TempDir()
configFileName := filepath.Join(dir, "config.yaml")
if err := atomicWriteFile(configFileName, []byte(`
@ -126,7 +124,6 @@ authorizers:
func TestMultiWebhookAuthzConfig(t *testing.T) {
authzmetrics.ResetMetricsForTest()
defer authzmetrics.ResetMetricsForTest()
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.StructuredAuthorizationConfiguration, true)
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.AuthorizeWithSelectors, true)
dir := t.TempDir()
@ -415,7 +412,7 @@ users:
configFileName := filepath.Join(dir, "config.yaml")
if err := atomicWriteFile(configFileName, []byte(`
apiVersion: apiserver.config.k8s.io/v1alpha1
apiVersion: apiserver.config.k8s.io/v1beta1
kind: AuthorizationConfiguration
authorizers:
- type: Webhook
@ -803,7 +800,7 @@ authorizers:
// write good config with different webhook
if err := atomicWriteFile(configFileName, []byte(`
apiVersion: apiserver.config.k8s.io/v1beta1
apiVersion: apiserver.config.k8s.io/v1
kind: AuthorizationConfiguration
authorizers:
- type: Webhook