mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-01 15:58:37 +00:00
Bootstrap API Types for Structured Authorization Configuration
Signed-off-by: Nabarun Pal <pal.nabarun95@gmail.com>
This commit is contained in:
parent
0241da314e
commit
52c582ca77
@ -44,6 +44,7 @@ func addKnownTypes(scheme *runtime.Scheme) error {
|
||||
scheme.AddKnownTypes(SchemeGroupVersion,
|
||||
&AdmissionConfiguration{},
|
||||
&AuthenticationConfiguration{},
|
||||
&AuthorizationConfiguration{},
|
||||
&EgressSelectorConfiguration{},
|
||||
&TracingConfiguration{},
|
||||
)
|
||||
|
@ -198,3 +198,120 @@ type PrefixedClaimOrExpression struct {
|
||||
Claim string
|
||||
Prefix *string
|
||||
}
|
||||
|
||||
// +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"
|
||||
AuthorizationWebhookConnectionInfoTypeKubeConfig 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 AuthorizerType
|
||||
|
||||
// Webhook defines the configuration for a Webhook authorizer
|
||||
// Must be defined when Type=Webhook
|
||||
Webhook *WebhookConfiguration
|
||||
}
|
||||
|
||||
type WebhookConfiguration struct {
|
||||
// Name used to describe the webhook
|
||||
// This is explicitly used in monitoring machinery for metrics
|
||||
// Note: Names must be DNS1123 labels like `mywebhookname` or
|
||||
// subdomains like `webhookname.example.domain`
|
||||
// Required, with no default
|
||||
Name string
|
||||
// The duration to cache 'authorized' responses from the webhook
|
||||
// authorizer.
|
||||
// Same as setting `--authorization-webhook-cache-authorized-ttl` flag
|
||||
// Default: 5m0s
|
||||
AuthorizedTTL metav1.Duration
|
||||
// The duration to cache 'unauthorized' responses from the webhook
|
||||
// authorizer.
|
||||
// Same as setting `--authorization-webhook-cache-unauthorized-ttl` flag
|
||||
// Default: 30s
|
||||
UnauthorizedTTL metav1.Duration
|
||||
// Timeout for the webhook request
|
||||
// Maximum allowed value is 30s.
|
||||
// Required, no default value.
|
||||
Timeout metav1.Duration
|
||||
// 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
|
||||
// MatchConditionSubjectAccessReviewVersion specifies the SubjectAccessReview
|
||||
// version the CEL expressions are evaluated against
|
||||
// Valid values: v1
|
||||
// Required, no default value
|
||||
MatchConditionSubjectAccessReviewVersion string
|
||||
// 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
|
||||
|
||||
// ConnectionInfo defines how we talk to the webhook
|
||||
ConnectionInfo WebhookConnectionInfo
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
type WebhookConnectionInfo struct {
|
||||
// Controls how the webhook should communicate with the server.
|
||||
// Valid values:
|
||||
// - KubeConfig: 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
|
||||
|
||||
// Path to KubeConfigFile for connection info
|
||||
// Required, if connectionInfo.Type is KubeConfig
|
||||
KubeConfigFile *string
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
Copyright 2023 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
func addDefaultingFuncs(scheme *runtime.Scheme) error {
|
||||
return RegisterDefaults(scheme)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
@ -43,7 +43,7 @@ func init() {
|
||||
// We only register manually written functions here. The registration of the
|
||||
// generated functions takes place in the generated files. The separation
|
||||
// makes the code compile even when the generated files are missing.
|
||||
localSchemeBuilder.Register(addKnownTypes)
|
||||
localSchemeBuilder.Register(addKnownTypes, addDefaultingFuncs)
|
||||
}
|
||||
|
||||
// Adds the list of known types to the given scheme.
|
||||
@ -54,6 +54,7 @@ func addKnownTypes(scheme *runtime.Scheme) error {
|
||||
)
|
||||
scheme.AddKnownTypes(ConfigSchemeGroupVersion,
|
||||
&AuthenticationConfiguration{},
|
||||
&AuthorizationConfiguration{},
|
||||
&TracingConfiguration{},
|
||||
)
|
||||
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
|
||||
|
@ -268,3 +268,121 @@ type PrefixedClaimOrExpression struct {
|
||||
// +required
|
||||
Prefix *string `json:"prefix"`
|
||||
}
|
||||
|
||||
// +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"
|
||||
AuthorizationWebhookConnectionInfoTypeKubeConfig 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"`
|
||||
|
||||
// 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 {
|
||||
// Name used to describe the webhook
|
||||
// This is explicitly used in monitoring machinery for metrics
|
||||
// Note: Names must be DNS1123 labels like `mywebhookname` or
|
||||
// subdomains like `webhookname.example.domain`
|
||||
// Required, with no default
|
||||
Name string `json:"name"`
|
||||
// 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:
|
||||
// - KubeConfig: 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"`
|
||||
}
|
||||
|
@ -66,6 +66,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_v1alpha1_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_v1alpha1_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_v1alpha1_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_v1alpha1_AuthorizerConfiguration(a.(*apiserver.AuthorizerConfiguration), b.(*AuthorizerConfiguration), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*ClaimMappings)(nil), (*apiserver.ClaimMappings)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_v1alpha1_ClaimMappings_To_apiserver_ClaimMappings(a.(*ClaimMappings), b.(*apiserver.ClaimMappings), scope)
|
||||
}); err != nil {
|
||||
@ -191,6 +211,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_v1alpha1_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_v1alpha1_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_v1alpha1_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_v1alpha1_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_v1alpha1_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_v1alpha1_WebhookMatchCondition(a.(*apiserver.WebhookMatchCondition), b.(*WebhookMatchCondition), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddConversionFunc((*EgressSelection)(nil), (*apiserver.EgressSelection)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_v1alpha1_EgressSelection_To_apiserver_EgressSelection(a.(*EgressSelection), b.(*apiserver.EgressSelection), scope)
|
||||
}); err != nil {
|
||||
@ -263,6 +313,48 @@ func Convert_apiserver_AuthenticationConfiguration_To_v1alpha1_AuthenticationCon
|
||||
return autoConvert_apiserver_AuthenticationConfiguration_To_v1alpha1_AuthenticationConfiguration(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1alpha1_AuthorizationConfiguration_To_apiserver_AuthorizationConfiguration(in *AuthorizationConfiguration, out *apiserver.AuthorizationConfiguration, s conversion.Scope) error {
|
||||
out.Authorizers = *(*[]apiserver.AuthorizerConfiguration)(unsafe.Pointer(&in.Authorizers))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_v1alpha1_AuthorizationConfiguration_To_apiserver_AuthorizationConfiguration is an autogenerated conversion function.
|
||||
func Convert_v1alpha1_AuthorizationConfiguration_To_apiserver_AuthorizationConfiguration(in *AuthorizationConfiguration, out *apiserver.AuthorizationConfiguration, s conversion.Scope) error {
|
||||
return autoConvert_v1alpha1_AuthorizationConfiguration_To_apiserver_AuthorizationConfiguration(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_apiserver_AuthorizationConfiguration_To_v1alpha1_AuthorizationConfiguration(in *apiserver.AuthorizationConfiguration, out *AuthorizationConfiguration, s conversion.Scope) error {
|
||||
out.Authorizers = *(*[]AuthorizerConfiguration)(unsafe.Pointer(&in.Authorizers))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_apiserver_AuthorizationConfiguration_To_v1alpha1_AuthorizationConfiguration is an autogenerated conversion function.
|
||||
func Convert_apiserver_AuthorizationConfiguration_To_v1alpha1_AuthorizationConfiguration(in *apiserver.AuthorizationConfiguration, out *AuthorizationConfiguration, s conversion.Scope) error {
|
||||
return autoConvert_apiserver_AuthorizationConfiguration_To_v1alpha1_AuthorizationConfiguration(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1alpha1_AuthorizerConfiguration_To_apiserver_AuthorizerConfiguration(in *AuthorizerConfiguration, out *apiserver.AuthorizerConfiguration, s conversion.Scope) error {
|
||||
out.Type = apiserver.AuthorizerType(in.Type)
|
||||
out.Webhook = (*apiserver.WebhookConfiguration)(unsafe.Pointer(in.Webhook))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_v1alpha1_AuthorizerConfiguration_To_apiserver_AuthorizerConfiguration is an autogenerated conversion function.
|
||||
func Convert_v1alpha1_AuthorizerConfiguration_To_apiserver_AuthorizerConfiguration(in *AuthorizerConfiguration, out *apiserver.AuthorizerConfiguration, s conversion.Scope) error {
|
||||
return autoConvert_v1alpha1_AuthorizerConfiguration_To_apiserver_AuthorizerConfiguration(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_apiserver_AuthorizerConfiguration_To_v1alpha1_AuthorizerConfiguration(in *apiserver.AuthorizerConfiguration, out *AuthorizerConfiguration, s conversion.Scope) error {
|
||||
out.Type = string(in.Type)
|
||||
out.Webhook = (*WebhookConfiguration)(unsafe.Pointer(in.Webhook))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_apiserver_AuthorizerConfiguration_To_v1alpha1_AuthorizerConfiguration is an autogenerated conversion function.
|
||||
func Convert_apiserver_AuthorizerConfiguration_To_v1alpha1_AuthorizerConfiguration(in *apiserver.AuthorizerConfiguration, out *AuthorizerConfiguration, s conversion.Scope) error {
|
||||
return autoConvert_apiserver_AuthorizerConfiguration_To_v1alpha1_AuthorizerConfiguration(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1alpha1_ClaimMappings_To_apiserver_ClaimMappings(in *ClaimMappings, out *apiserver.ClaimMappings, s conversion.Scope) error {
|
||||
if err := Convert_v1alpha1_PrefixedClaimOrExpression_To_apiserver_PrefixedClaimOrExpression(&in.Username, &out.Username, s); err != nil {
|
||||
return err
|
||||
@ -583,3 +675,85 @@ func autoConvert_apiserver_UDSTransport_To_v1alpha1_UDSTransport(in *apiserver.U
|
||||
func Convert_apiserver_UDSTransport_To_v1alpha1_UDSTransport(in *apiserver.UDSTransport, out *UDSTransport, s conversion.Scope) error {
|
||||
return autoConvert_apiserver_UDSTransport_To_v1alpha1_UDSTransport(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1alpha1_WebhookConfiguration_To_apiserver_WebhookConfiguration(in *WebhookConfiguration, out *apiserver.WebhookConfiguration, s conversion.Scope) error {
|
||||
out.Name = in.Name
|
||||
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_v1alpha1_WebhookConnectionInfo_To_apiserver_WebhookConnectionInfo(&in.ConnectionInfo, &out.ConnectionInfo, s); err != nil {
|
||||
return err
|
||||
}
|
||||
out.MatchConditions = *(*[]apiserver.WebhookMatchCondition)(unsafe.Pointer(&in.MatchConditions))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_v1alpha1_WebhookConfiguration_To_apiserver_WebhookConfiguration is an autogenerated conversion function.
|
||||
func Convert_v1alpha1_WebhookConfiguration_To_apiserver_WebhookConfiguration(in *WebhookConfiguration, out *apiserver.WebhookConfiguration, s conversion.Scope) error {
|
||||
return autoConvert_v1alpha1_WebhookConfiguration_To_apiserver_WebhookConfiguration(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_apiserver_WebhookConfiguration_To_v1alpha1_WebhookConfiguration(in *apiserver.WebhookConfiguration, out *WebhookConfiguration, s conversion.Scope) error {
|
||||
out.Name = in.Name
|
||||
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_v1alpha1_WebhookConnectionInfo(&in.ConnectionInfo, &out.ConnectionInfo, s); err != nil {
|
||||
return err
|
||||
}
|
||||
out.MatchConditions = *(*[]WebhookMatchCondition)(unsafe.Pointer(&in.MatchConditions))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_apiserver_WebhookConfiguration_To_v1alpha1_WebhookConfiguration is an autogenerated conversion function.
|
||||
func Convert_apiserver_WebhookConfiguration_To_v1alpha1_WebhookConfiguration(in *apiserver.WebhookConfiguration, out *WebhookConfiguration, s conversion.Scope) error {
|
||||
return autoConvert_apiserver_WebhookConfiguration_To_v1alpha1_WebhookConfiguration(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1alpha1_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_v1alpha1_WebhookConnectionInfo_To_apiserver_WebhookConnectionInfo is an autogenerated conversion function.
|
||||
func Convert_v1alpha1_WebhookConnectionInfo_To_apiserver_WebhookConnectionInfo(in *WebhookConnectionInfo, out *apiserver.WebhookConnectionInfo, s conversion.Scope) error {
|
||||
return autoConvert_v1alpha1_WebhookConnectionInfo_To_apiserver_WebhookConnectionInfo(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_apiserver_WebhookConnectionInfo_To_v1alpha1_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_v1alpha1_WebhookConnectionInfo is an autogenerated conversion function.
|
||||
func Convert_apiserver_WebhookConnectionInfo_To_v1alpha1_WebhookConnectionInfo(in *apiserver.WebhookConnectionInfo, out *WebhookConnectionInfo, s conversion.Scope) error {
|
||||
return autoConvert_apiserver_WebhookConnectionInfo_To_v1alpha1_WebhookConnectionInfo(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1alpha1_WebhookMatchCondition_To_apiserver_WebhookMatchCondition(in *WebhookMatchCondition, out *apiserver.WebhookMatchCondition, s conversion.Scope) error {
|
||||
out.Expression = in.Expression
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_v1alpha1_WebhookMatchCondition_To_apiserver_WebhookMatchCondition is an autogenerated conversion function.
|
||||
func Convert_v1alpha1_WebhookMatchCondition_To_apiserver_WebhookMatchCondition(in *WebhookMatchCondition, out *apiserver.WebhookMatchCondition, s conversion.Scope) error {
|
||||
return autoConvert_v1alpha1_WebhookMatchCondition_To_apiserver_WebhookMatchCondition(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_apiserver_WebhookMatchCondition_To_v1alpha1_WebhookMatchCondition(in *apiserver.WebhookMatchCondition, out *WebhookMatchCondition, s conversion.Scope) error {
|
||||
out.Expression = in.Expression
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_apiserver_WebhookMatchCondition_To_v1alpha1_WebhookMatchCondition is an autogenerated conversion function.
|
||||
func Convert_apiserver_WebhookMatchCondition_To_v1alpha1_WebhookMatchCondition(in *apiserver.WebhookMatchCondition, out *WebhookMatchCondition, s conversion.Scope) error {
|
||||
return autoConvert_apiserver_WebhookMatchCondition_To_v1alpha1_WebhookMatchCondition(in, out, s)
|
||||
}
|
||||
|
@ -110,6 +110,59 @@ func (in *AuthenticationConfiguration) DeepCopyObject() runtime.Object {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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 *ClaimMappings) DeepCopyInto(out *ClaimMappings) {
|
||||
*out = *in
|
||||
@ -383,3 +436,65 @@ func (in *UDSTransport) DeepCopy() *UDSTransport {
|
||||
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
|
||||
}
|
||||
|
@ -29,5 +29,15 @@ 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)) })
|
||||
return nil
|
||||
}
|
||||
|
||||
func SetObjectDefaults_AuthorizationConfiguration(in *AuthorizationConfiguration) {
|
||||
for i := range in.Authorizers {
|
||||
a := &in.Authorizers[i]
|
||||
if a.Webhook != nil {
|
||||
SetDefaults_WebhookConfiguration(a.Webhook)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,8 +19,16 @@ package validation
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
v1 "k8s.io/api/authorization/v1"
|
||||
"k8s.io/api/authorization/v1beta1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
utilvalidation "k8s.io/apimachinery/pkg/util/validation"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
api "k8s.io/apiserver/pkg/apis/apiserver"
|
||||
"k8s.io/client-go/util/cert"
|
||||
@ -202,3 +210,147 @@ func validateClaimMappings(m api.ClaimMappings, fldPath *field.Path) field.Error
|
||||
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidateAuthorizationConfiguration validates a given AuthorizationConfiguration.
|
||||
func ValidateAuthorizationConfiguration(fldPath *field.Path, c *api.AuthorizationConfiguration, knownTypes sets.String, repeatableTypes sets.String) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
|
||||
if len(c.Authorizers) == 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("authorizers"), "at least one authorization mode must be defined"))
|
||||
}
|
||||
|
||||
seenAuthorizerTypes := sets.NewString()
|
||||
seenWebhookNames := sets.NewString()
|
||||
for i, a := range c.Authorizers {
|
||||
fldPath := fldPath.Child("authorizers").Index(i)
|
||||
aType := string(a.Type)
|
||||
if aType == "" {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("type"), ""))
|
||||
continue
|
||||
}
|
||||
if !knownTypes.Has(aType) {
|
||||
allErrs = append(allErrs, field.NotSupported(fldPath.Child("type"), aType, knownTypes.List()))
|
||||
continue
|
||||
}
|
||||
if seenAuthorizerTypes.Has(aType) && !repeatableTypes.Has(aType) {
|
||||
allErrs = append(allErrs, field.Duplicate(fldPath.Child("type"), aType))
|
||||
continue
|
||||
}
|
||||
seenAuthorizerTypes.Insert(aType)
|
||||
|
||||
switch a.Type {
|
||||
case api.TypeWebhook:
|
||||
if a.Webhook == nil {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("webhook"), "required when type=Webhook"))
|
||||
continue
|
||||
}
|
||||
allErrs = append(allErrs, ValidateWebhookConfiguration(fldPath, a.Webhook, seenWebhookNames)...)
|
||||
default:
|
||||
if a.Webhook != nil {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("webhook"), "non-null", "may only be specified when type=Webhook"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func ValidateWebhookConfiguration(fldPath *field.Path, c *api.WebhookConfiguration, seenNames sets.String) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
if len(c.Name) == 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("name"), ""))
|
||||
} else if seenNames.Has(c.Name) {
|
||||
allErrs = append(allErrs, field.Duplicate(fldPath.Child("name"), c.Name))
|
||||
} else if errs := utilvalidation.IsDNS1123Subdomain(c.Name); len(errs) != 0 {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), c.Name, fmt.Sprintf("webhook name is invalid: %s", strings.Join(errs, ", "))))
|
||||
}
|
||||
seenNames.Insert(c.Name)
|
||||
|
||||
if c.Timeout.Duration == 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("timeout"), ""))
|
||||
} else if c.Timeout.Duration > 30*time.Second || c.Timeout.Duration < 0 {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("timeout"), c.Timeout.Duration.String(), "must be > 0s and <= 30s"))
|
||||
}
|
||||
|
||||
if c.AuthorizedTTL.Duration == 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("authorizedTTL"), ""))
|
||||
} else if c.AuthorizedTTL.Duration < 0 {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("authorizedTTL"), c.AuthorizedTTL.Duration.String(), "must be > 0s"))
|
||||
}
|
||||
|
||||
if c.UnauthorizedTTL.Duration == 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("unauthorizedTTL"), ""))
|
||||
} else if c.UnauthorizedTTL.Duration < 0 {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("unauthorizedTTL"), c.UnauthorizedTTL.Duration.String(), "must be > 0s"))
|
||||
}
|
||||
|
||||
switch c.SubjectAccessReviewVersion {
|
||||
case "":
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("subjectAccessReviewVersion"), ""))
|
||||
case "v1":
|
||||
_ = &v1.SubjectAccessReview{}
|
||||
case "v1beta1":
|
||||
_ = &v1beta1.SubjectAccessReview{}
|
||||
default:
|
||||
allErrs = append(allErrs, field.NotSupported(fldPath.Child("subjectAccessReviewVersion"), c.SubjectAccessReviewVersion, []string{"v1", "v1beta1"}))
|
||||
}
|
||||
|
||||
switch c.MatchConditionSubjectAccessReviewVersion {
|
||||
case "":
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("matchConditionSubjectAccessReviewVersion"), ""))
|
||||
case "v1":
|
||||
_ = &v1.SubjectAccessReview{}
|
||||
default:
|
||||
allErrs = append(allErrs, field.NotSupported(fldPath.Child("matchConditionSubjectAccessReviewVersion"), c.MatchConditionSubjectAccessReviewVersion, []string{"v1"}))
|
||||
}
|
||||
|
||||
switch c.FailurePolicy {
|
||||
case "":
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("failurePolicy"), ""))
|
||||
case api.FailurePolicyNoOpinion, api.FailurePolicyDeny:
|
||||
default:
|
||||
allErrs = append(allErrs, field.NotSupported(fldPath.Child("failurePolicy"), c.FailurePolicy, []string{"NoOpinion", "Deny"}))
|
||||
}
|
||||
|
||||
switch c.ConnectionInfo.Type {
|
||||
case "":
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("connectionInfo", "type"), ""))
|
||||
case api.AuthorizationWebhookConnectionInfoTypeInCluster:
|
||||
if c.ConnectionInfo.KubeConfigFile != nil {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("connectionInfo", "kubeConfigFile"), *c.ConnectionInfo.KubeConfigFile, "can only be set when type=KubeConfigFile"))
|
||||
}
|
||||
case api.AuthorizationWebhookConnectionInfoTypeKubeConfig:
|
||||
if c.ConnectionInfo.KubeConfigFile == nil || *c.ConnectionInfo.KubeConfigFile == "" {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("connectionInfo", "kubeConfigFile"), ""))
|
||||
} else if !filepath.IsAbs(*c.ConnectionInfo.KubeConfigFile) {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("connectionInfo", "kubeConfigFile"), *c.ConnectionInfo.KubeConfigFile, "must be an absolute path"))
|
||||
} else if info, err := os.Stat(*c.ConnectionInfo.KubeConfigFile); err != nil {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("connectionInfo", "kubeConfigFile"), *c.ConnectionInfo.KubeConfigFile, fmt.Sprintf("error loading file: %v", err)))
|
||||
} else if !info.Mode().IsRegular() {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("connectionInfo", "kubeConfigFile"), *c.ConnectionInfo.KubeConfigFile, "must be a regular file"))
|
||||
}
|
||||
default:
|
||||
allErrs = append(allErrs, field.NotSupported(fldPath.Child("connectionInfo", "type"), c.ConnectionInfo, []string{"InClusterConfig", "KubeConfigFile"}))
|
||||
}
|
||||
|
||||
// TODO: Remove this check and ensure that correct validations below for MatchConditions are added
|
||||
// for i, condition := range c.MatchConditions {
|
||||
// fldPath := fldPath.Child("matchConditions").Index(i).Child("expression")
|
||||
// if len(strings.TrimSpace(condition.Expression)) == 0 {
|
||||
// allErrs = append(allErrs, field.Required(fldPath, ""))
|
||||
// } else {
|
||||
// allErrs = append(allErrs, ValidateWebhookMatchCondition(fldPath, sampleSAR, condition.Expression)...)
|
||||
// }
|
||||
// }
|
||||
if len(c.MatchConditions) != 0 {
|
||||
allErrs = append(allErrs, field.NotSupported(fldPath.Child("matchConditions"), c.MatchConditions, []string{}))
|
||||
}
|
||||
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func ValidateWebhookMatchCondition(fldPath *field.Path, sampleSAR runtime.Object, expression string) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
// TODO: typecheck CEL expression
|
||||
return allErrs
|
||||
}
|
||||
|
@ -21,11 +21,15 @@ import (
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"encoding/pem"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/errors"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
api "k8s.io/apiserver/pkg/apis/apiserver"
|
||||
certutil "k8s.io/client-go/util/cert"
|
||||
@ -412,3 +416,832 @@ func errString(errs errors.Aggregate) string {
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type (
|
||||
test struct {
|
||||
name string
|
||||
configuration api.AuthorizationConfiguration
|
||||
expectedErrList field.ErrorList
|
||||
knownTypes sets.String
|
||||
repeatableTypes sets.String
|
||||
}
|
||||
)
|
||||
|
||||
func TestValidateAuthorizationConfiguration(t *testing.T) {
|
||||
badKubeConfigFile := "../some/relative/path/kubeconfig"
|
||||
|
||||
tempKubeConfigFile, err := os.CreateTemp("/tmp", "kubeconfig")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to set up temp file: %v", err)
|
||||
}
|
||||
tempKubeConfigFilePath := tempKubeConfigFile.Name()
|
||||
defer os.Remove(tempKubeConfigFilePath)
|
||||
|
||||
tests := []test{
|
||||
{
|
||||
name: "atleast one authorizer should be defined",
|
||||
configuration: api.AuthorizationConfiguration{
|
||||
Authorizers: []api.AuthorizerConfiguration{},
|
||||
},
|
||||
expectedErrList: field.ErrorList{field.Required(field.NewPath("authorizers"), "at least one authorization mode must be defined")},
|
||||
knownTypes: sets.NewString(),
|
||||
repeatableTypes: sets.NewString(),
|
||||
},
|
||||
{
|
||||
name: "type is required if an authorizer is defined",
|
||||
configuration: api.AuthorizationConfiguration{
|
||||
Authorizers: []api.AuthorizerConfiguration{
|
||||
{},
|
||||
},
|
||||
},
|
||||
expectedErrList: field.ErrorList{field.Required(field.NewPath("type"), "")},
|
||||
knownTypes: sets.NewString(string("Webhook")),
|
||||
repeatableTypes: sets.NewString(string("Webhook")),
|
||||
},
|
||||
{
|
||||
name: "bare minimum configuration with Webhook",
|
||||
configuration: api.AuthorizationConfiguration{
|
||||
Authorizers: []api.AuthorizerConfiguration{
|
||||
{
|
||||
Type: "Webhook",
|
||||
Webhook: &api.WebhookConfiguration{
|
||||
Name: "default",
|
||||
Timeout: metav1.Duration{Duration: 5 * time.Second},
|
||||
AuthorizedTTL: metav1.Duration{Duration: 5 * time.Minute},
|
||||
UnauthorizedTTL: metav1.Duration{Duration: 30 * time.Second},
|
||||
FailurePolicy: "NoOpinion",
|
||||
SubjectAccessReviewVersion: "v1",
|
||||
MatchConditionSubjectAccessReviewVersion: "v1",
|
||||
ConnectionInfo: api.WebhookConnectionInfo{
|
||||
Type: "InClusterConfig",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErrList: field.ErrorList{},
|
||||
knownTypes: sets.NewString(string("Webhook")),
|
||||
repeatableTypes: sets.NewString(string("Webhook")),
|
||||
},
|
||||
{
|
||||
name: "bare minimum configuration with multiple webhooks",
|
||||
configuration: api.AuthorizationConfiguration{
|
||||
Authorizers: []api.AuthorizerConfiguration{
|
||||
{
|
||||
Type: "Webhook",
|
||||
Webhook: &api.WebhookConfiguration{
|
||||
Name: "default",
|
||||
Timeout: metav1.Duration{Duration: 5 * time.Second},
|
||||
AuthorizedTTL: metav1.Duration{Duration: 5 * time.Minute},
|
||||
UnauthorizedTTL: metav1.Duration{Duration: 30 * time.Second},
|
||||
FailurePolicy: "NoOpinion",
|
||||
SubjectAccessReviewVersion: "v1",
|
||||
MatchConditionSubjectAccessReviewVersion: "v1",
|
||||
ConnectionInfo: api.WebhookConnectionInfo{
|
||||
Type: "InClusterConfig",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: "Webhook",
|
||||
Webhook: &api.WebhookConfiguration{
|
||||
Name: "second-webhook",
|
||||
Timeout: metav1.Duration{Duration: 5 * time.Second},
|
||||
AuthorizedTTL: metav1.Duration{Duration: 5 * time.Minute},
|
||||
UnauthorizedTTL: metav1.Duration{Duration: 30 * time.Second},
|
||||
FailurePolicy: "NoOpinion",
|
||||
SubjectAccessReviewVersion: "v1",
|
||||
MatchConditionSubjectAccessReviewVersion: "v1",
|
||||
ConnectionInfo: api.WebhookConnectionInfo{
|
||||
Type: "InClusterConfig",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErrList: field.ErrorList{},
|
||||
knownTypes: sets.NewString(string("Webhook")),
|
||||
repeatableTypes: sets.NewString(string("Webhook")),
|
||||
},
|
||||
{
|
||||
name: "configuration with unknown types",
|
||||
configuration: api.AuthorizationConfiguration{
|
||||
Authorizers: []api.AuthorizerConfiguration{
|
||||
{
|
||||
Type: "Foo",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErrList: field.ErrorList{field.NotSupported(field.NewPath("type"), "Foo", []string{"..."})},
|
||||
knownTypes: sets.NewString(string("Webhook")),
|
||||
repeatableTypes: sets.NewString(string("Webhook")),
|
||||
},
|
||||
{
|
||||
name: "configuration with not repeatable types",
|
||||
configuration: api.AuthorizationConfiguration{
|
||||
Authorizers: []api.AuthorizerConfiguration{
|
||||
{
|
||||
Type: "Foo",
|
||||
},
|
||||
{
|
||||
Type: "Foo",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErrList: field.ErrorList{field.Duplicate(field.NewPath("type"), "Foo")},
|
||||
knownTypes: sets.NewString([]string{string("Foo"), string("Webhook")}...),
|
||||
repeatableTypes: sets.NewString(string("Webhook")),
|
||||
},
|
||||
{
|
||||
name: "when type=Webhook, webhook needs to be defined",
|
||||
configuration: api.AuthorizationConfiguration{
|
||||
Authorizers: []api.AuthorizerConfiguration{
|
||||
{
|
||||
Type: "Webhook",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErrList: field.ErrorList{field.Required(field.NewPath("webhook"), "required when type=Webhook")},
|
||||
knownTypes: sets.NewString(string("Webhook")),
|
||||
repeatableTypes: sets.NewString(string("Webhook")),
|
||||
},
|
||||
{
|
||||
name: "when type!=Webhook, webhooks needs to be nil",
|
||||
configuration: api.AuthorizationConfiguration{
|
||||
Authorizers: []api.AuthorizerConfiguration{
|
||||
{
|
||||
Type: "Foo",
|
||||
Webhook: &api.WebhookConfiguration{},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErrList: field.ErrorList{field.Invalid(field.NewPath("webhook"), "non-null", "may only be specified when type=Webhook")},
|
||||
knownTypes: sets.NewString(string("Foo")),
|
||||
repeatableTypes: sets.NewString(string("Webhook")),
|
||||
},
|
||||
{
|
||||
name: "webhook name should be of non-zero length",
|
||||
configuration: api.AuthorizationConfiguration{
|
||||
Authorizers: []api.AuthorizerConfiguration{
|
||||
{
|
||||
Type: "Webhook",
|
||||
Webhook: &api.WebhookConfiguration{
|
||||
Name: "",
|
||||
Timeout: metav1.Duration{Duration: 5 * time.Second},
|
||||
AuthorizedTTL: metav1.Duration{Duration: 5 * time.Minute},
|
||||
UnauthorizedTTL: metav1.Duration{Duration: 30 * time.Second},
|
||||
FailurePolicy: "NoOpinion",
|
||||
SubjectAccessReviewVersion: "v1",
|
||||
MatchConditionSubjectAccessReviewVersion: "v1",
|
||||
ConnectionInfo: api.WebhookConnectionInfo{
|
||||
Type: "InClusterConfig",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErrList: field.ErrorList{field.Required(field.NewPath("name"), "")},
|
||||
knownTypes: sets.NewString(string("Webhook")),
|
||||
repeatableTypes: sets.NewString(string("Webhook")),
|
||||
},
|
||||
{
|
||||
name: "webhook names should be unique",
|
||||
configuration: api.AuthorizationConfiguration{
|
||||
Authorizers: []api.AuthorizerConfiguration{
|
||||
{
|
||||
Type: "Webhook",
|
||||
Webhook: &api.WebhookConfiguration{
|
||||
Name: "name-1",
|
||||
Timeout: metav1.Duration{Duration: 5 * time.Second},
|
||||
AuthorizedTTL: metav1.Duration{Duration: 5 * time.Minute},
|
||||
UnauthorizedTTL: metav1.Duration{Duration: 30 * time.Second},
|
||||
FailurePolicy: "NoOpinion",
|
||||
SubjectAccessReviewVersion: "v1",
|
||||
MatchConditionSubjectAccessReviewVersion: "v1",
|
||||
ConnectionInfo: api.WebhookConnectionInfo{
|
||||
Type: "InClusterConfig",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: "Webhook",
|
||||
Webhook: &api.WebhookConfiguration{
|
||||
Name: "name-1",
|
||||
Timeout: metav1.Duration{Duration: 5 * time.Second},
|
||||
AuthorizedTTL: metav1.Duration{Duration: 5 * time.Minute},
|
||||
UnauthorizedTTL: metav1.Duration{Duration: 30 * time.Second},
|
||||
FailurePolicy: "NoOpinion",
|
||||
SubjectAccessReviewVersion: "v1",
|
||||
MatchConditionSubjectAccessReviewVersion: "v1",
|
||||
ConnectionInfo: api.WebhookConnectionInfo{
|
||||
Type: "InClusterConfig",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErrList: field.ErrorList{field.Duplicate(field.NewPath("name"), "name-1")},
|
||||
knownTypes: sets.NewString(string("Webhook")),
|
||||
repeatableTypes: sets.NewString(string("Webhook")),
|
||||
},
|
||||
{
|
||||
name: "webhook names should be DNS1123 labels",
|
||||
configuration: api.AuthorizationConfiguration{
|
||||
Authorizers: []api.AuthorizerConfiguration{
|
||||
{
|
||||
Type: "Webhook",
|
||||
Webhook: &api.WebhookConfiguration{
|
||||
Name: "mywebhookname",
|
||||
Timeout: metav1.Duration{Duration: 5 * time.Second},
|
||||
AuthorizedTTL: metav1.Duration{Duration: 5 * time.Minute},
|
||||
UnauthorizedTTL: metav1.Duration{Duration: 30 * time.Second},
|
||||
FailurePolicy: "NoOpinion",
|
||||
SubjectAccessReviewVersion: "v1",
|
||||
MatchConditionSubjectAccessReviewVersion: "v1",
|
||||
ConnectionInfo: api.WebhookConnectionInfo{
|
||||
Type: "InClusterConfig",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErrList: field.ErrorList{},
|
||||
knownTypes: sets.NewString(string("Webhook")),
|
||||
repeatableTypes: sets.NewString(string("Webhook")),
|
||||
},
|
||||
{
|
||||
name: "webhook names should be DNS1123 subdomains",
|
||||
configuration: api.AuthorizationConfiguration{
|
||||
Authorizers: []api.AuthorizerConfiguration{
|
||||
{
|
||||
Type: "Webhook",
|
||||
Webhook: &api.WebhookConfiguration{
|
||||
Name: "webhookname.example.domain",
|
||||
Timeout: metav1.Duration{Duration: 5 * time.Second},
|
||||
AuthorizedTTL: metav1.Duration{Duration: 5 * time.Minute},
|
||||
UnauthorizedTTL: metav1.Duration{Duration: 30 * time.Second},
|
||||
FailurePolicy: "NoOpinion",
|
||||
SubjectAccessReviewVersion: "v1",
|
||||
MatchConditionSubjectAccessReviewVersion: "v1",
|
||||
ConnectionInfo: api.WebhookConnectionInfo{
|
||||
Type: "InClusterConfig",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErrList: field.ErrorList{},
|
||||
knownTypes: sets.NewString(string("Webhook")),
|
||||
repeatableTypes: sets.NewString(string("Webhook")),
|
||||
},
|
||||
{
|
||||
name: "webhook names should not be invalid DNS1123 labels or subdomains",
|
||||
configuration: api.AuthorizationConfiguration{
|
||||
Authorizers: []api.AuthorizerConfiguration{
|
||||
{
|
||||
Type: "Webhook",
|
||||
Webhook: &api.WebhookConfiguration{
|
||||
Name: "WEBHOOKNAME.example.domain",
|
||||
Timeout: metav1.Duration{Duration: 5 * time.Second},
|
||||
AuthorizedTTL: metav1.Duration{Duration: 5 * time.Minute},
|
||||
UnauthorizedTTL: metav1.Duration{Duration: 30 * time.Second},
|
||||
FailurePolicy: "NoOpinion",
|
||||
SubjectAccessReviewVersion: "v1",
|
||||
MatchConditionSubjectAccessReviewVersion: "v1",
|
||||
ConnectionInfo: api.WebhookConnectionInfo{
|
||||
Type: "InClusterConfig",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErrList: field.ErrorList{field.Invalid(field.NewPath("name"), "WEBHOOKNAME.example.domain", "")},
|
||||
knownTypes: sets.NewString(string("Webhook")),
|
||||
repeatableTypes: sets.NewString(string("Webhook")),
|
||||
},
|
||||
{
|
||||
name: "timeout should be specified",
|
||||
configuration: api.AuthorizationConfiguration{
|
||||
Authorizers: []api.AuthorizerConfiguration{
|
||||
{
|
||||
Type: "Webhook",
|
||||
Webhook: &api.WebhookConfiguration{
|
||||
Name: "default",
|
||||
FailurePolicy: "NoOpinion",
|
||||
AuthorizedTTL: metav1.Duration{Duration: 5 * time.Minute},
|
||||
UnauthorizedTTL: metav1.Duration{Duration: 30 * time.Second},
|
||||
SubjectAccessReviewVersion: "v1",
|
||||
MatchConditionSubjectAccessReviewVersion: "v1",
|
||||
ConnectionInfo: api.WebhookConnectionInfo{
|
||||
Type: "InClusterConfig",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErrList: field.ErrorList{field.Required(field.NewPath("timeout"), "")},
|
||||
knownTypes: sets.NewString(string("Webhook")),
|
||||
repeatableTypes: sets.NewString(string("Webhook")),
|
||||
},
|
||||
//
|
||||
{
|
||||
name: "timeout shouldn't be zero",
|
||||
configuration: api.AuthorizationConfiguration{
|
||||
Authorizers: []api.AuthorizerConfiguration{
|
||||
{
|
||||
Type: "Webhook",
|
||||
Webhook: &api.WebhookConfiguration{
|
||||
Name: "default",
|
||||
FailurePolicy: "NoOpinion",
|
||||
Timeout: metav1.Duration{Duration: 0 * time.Second},
|
||||
AuthorizedTTL: metav1.Duration{Duration: 5 * time.Minute},
|
||||
UnauthorizedTTL: metav1.Duration{Duration: 30 * time.Second},
|
||||
SubjectAccessReviewVersion: "v1",
|
||||
MatchConditionSubjectAccessReviewVersion: "v1",
|
||||
ConnectionInfo: api.WebhookConnectionInfo{
|
||||
Type: "InClusterConfig",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErrList: field.ErrorList{field.Required(field.NewPath("timeout"), "")},
|
||||
knownTypes: sets.NewString(string("Webhook")),
|
||||
repeatableTypes: sets.NewString(string("Webhook")),
|
||||
},
|
||||
{
|
||||
name: "timeout shouldn't be negative",
|
||||
configuration: api.AuthorizationConfiguration{
|
||||
Authorizers: []api.AuthorizerConfiguration{
|
||||
{
|
||||
Type: "Webhook",
|
||||
Webhook: &api.WebhookConfiguration{
|
||||
Name: "default",
|
||||
FailurePolicy: "NoOpinion",
|
||||
Timeout: metav1.Duration{Duration: -30 * time.Second},
|
||||
AuthorizedTTL: metav1.Duration{Duration: 5 * time.Minute},
|
||||
UnauthorizedTTL: metav1.Duration{Duration: 30 * time.Second},
|
||||
SubjectAccessReviewVersion: "v1",
|
||||
MatchConditionSubjectAccessReviewVersion: "v1",
|
||||
ConnectionInfo: api.WebhookConnectionInfo{
|
||||
Type: "InClusterConfig",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErrList: field.ErrorList{field.Invalid(field.NewPath("timeout"), time.Duration(-30*time.Second).String(), "must be > 0s and <= 30s")},
|
||||
knownTypes: sets.NewString(string("Webhook")),
|
||||
repeatableTypes: sets.NewString(string("Webhook")),
|
||||
},
|
||||
{
|
||||
name: "timeout shouldn't be greater than 30seconds",
|
||||
configuration: api.AuthorizationConfiguration{
|
||||
Authorizers: []api.AuthorizerConfiguration{
|
||||
{
|
||||
Type: "Webhook",
|
||||
Webhook: &api.WebhookConfiguration{
|
||||
Name: "default",
|
||||
FailurePolicy: "NoOpinion",
|
||||
Timeout: metav1.Duration{Duration: 60 * time.Second},
|
||||
AuthorizedTTL: metav1.Duration{Duration: 5 * time.Minute},
|
||||
UnauthorizedTTL: metav1.Duration{Duration: 30 * time.Second},
|
||||
SubjectAccessReviewVersion: "v1",
|
||||
MatchConditionSubjectAccessReviewVersion: "v1",
|
||||
ConnectionInfo: api.WebhookConnectionInfo{
|
||||
Type: "InClusterConfig",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErrList: field.ErrorList{field.Invalid(field.NewPath("timeout"), time.Duration(60*time.Second).String(), "must be > 0s and <= 30s")},
|
||||
knownTypes: sets.NewString(string("Webhook")),
|
||||
repeatableTypes: sets.NewString(string("Webhook")),
|
||||
},
|
||||
{
|
||||
name: "authorizedTTL should be defined ",
|
||||
configuration: api.AuthorizationConfiguration{
|
||||
Authorizers: []api.AuthorizerConfiguration{
|
||||
{
|
||||
Type: "Webhook",
|
||||
Webhook: &api.WebhookConfiguration{
|
||||
Name: "default",
|
||||
FailurePolicy: "NoOpinion",
|
||||
Timeout: metav1.Duration{Duration: 5 * time.Second},
|
||||
UnauthorizedTTL: metav1.Duration{Duration: 30 * time.Second},
|
||||
SubjectAccessReviewVersion: "v1",
|
||||
MatchConditionSubjectAccessReviewVersion: "v1",
|
||||
ConnectionInfo: api.WebhookConnectionInfo{
|
||||
Type: "InClusterConfig",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErrList: field.ErrorList{field.Required(field.NewPath("authorizedTTL"), "")},
|
||||
knownTypes: sets.NewString(string("Webhook")),
|
||||
repeatableTypes: sets.NewString(string("Webhook")),
|
||||
},
|
||||
{
|
||||
name: "authorizedTTL shouldn't be negative",
|
||||
configuration: api.AuthorizationConfiguration{
|
||||
Authorizers: []api.AuthorizerConfiguration{
|
||||
{
|
||||
Type: "Webhook",
|
||||
Webhook: &api.WebhookConfiguration{
|
||||
Name: "default",
|
||||
FailurePolicy: "NoOpinion",
|
||||
Timeout: metav1.Duration{Duration: 5 * time.Second},
|
||||
AuthorizedTTL: metav1.Duration{Duration: -30 * time.Second},
|
||||
UnauthorizedTTL: metav1.Duration{Duration: 30 * time.Second},
|
||||
SubjectAccessReviewVersion: "v1",
|
||||
MatchConditionSubjectAccessReviewVersion: "v1",
|
||||
ConnectionInfo: api.WebhookConnectionInfo{
|
||||
Type: "InClusterConfig",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErrList: field.ErrorList{field.Invalid(field.NewPath("authorizedTTL"), time.Duration(-30*time.Second).String(), "must be > 0s")},
|
||||
knownTypes: sets.NewString(string("Webhook")),
|
||||
repeatableTypes: sets.NewString(string("Webhook")),
|
||||
},
|
||||
{
|
||||
name: "unauthorizedTTL should be defined ",
|
||||
configuration: api.AuthorizationConfiguration{
|
||||
Authorizers: []api.AuthorizerConfiguration{
|
||||
{
|
||||
Type: "Webhook",
|
||||
Webhook: &api.WebhookConfiguration{
|
||||
Name: "default",
|
||||
FailurePolicy: "NoOpinion",
|
||||
Timeout: metav1.Duration{Duration: 5 * time.Second},
|
||||
AuthorizedTTL: metav1.Duration{Duration: 5 * time.Minute},
|
||||
SubjectAccessReviewVersion: "v1",
|
||||
MatchConditionSubjectAccessReviewVersion: "v1",
|
||||
ConnectionInfo: api.WebhookConnectionInfo{
|
||||
Type: "InClusterConfig",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErrList: field.ErrorList{field.Required(field.NewPath("unauthorizedTTL"), "")},
|
||||
knownTypes: sets.NewString(string("Webhook")),
|
||||
repeatableTypes: sets.NewString(string("Webhook")),
|
||||
},
|
||||
{
|
||||
name: "unauthorizedTTL shouldn't be negative",
|
||||
configuration: api.AuthorizationConfiguration{
|
||||
Authorizers: []api.AuthorizerConfiguration{
|
||||
{
|
||||
Type: "Webhook",
|
||||
Webhook: &api.WebhookConfiguration{
|
||||
Name: "default",
|
||||
FailurePolicy: "NoOpinion",
|
||||
Timeout: metav1.Duration{Duration: 5 * time.Second},
|
||||
AuthorizedTTL: metav1.Duration{Duration: 5 * time.Minute},
|
||||
UnauthorizedTTL: metav1.Duration{Duration: -30 * time.Second},
|
||||
SubjectAccessReviewVersion: "v1",
|
||||
MatchConditionSubjectAccessReviewVersion: "v1",
|
||||
ConnectionInfo: api.WebhookConnectionInfo{
|
||||
Type: "InClusterConfig",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErrList: field.ErrorList{field.Invalid(field.NewPath("unauthorizedTTL"), time.Duration(-30*time.Second).String(), "must be > 0s")},
|
||||
knownTypes: sets.NewString(string("Webhook")),
|
||||
repeatableTypes: sets.NewString(string("Webhook")),
|
||||
},
|
||||
{
|
||||
name: "SAR should be defined",
|
||||
configuration: api.AuthorizationConfiguration{
|
||||
Authorizers: []api.AuthorizerConfiguration{
|
||||
{
|
||||
Type: "Webhook",
|
||||
Webhook: &api.WebhookConfiguration{
|
||||
Name: "default",
|
||||
Timeout: metav1.Duration{Duration: 5 * time.Second},
|
||||
AuthorizedTTL: metav1.Duration{Duration: 5 * time.Minute},
|
||||
UnauthorizedTTL: metav1.Duration{Duration: 30 * time.Second},
|
||||
MatchConditionSubjectAccessReviewVersion: "v1",
|
||||
FailurePolicy: "NoOpinion",
|
||||
ConnectionInfo: api.WebhookConnectionInfo{
|
||||
Type: "InClusterConfig",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErrList: field.ErrorList{field.Required(field.NewPath("subjectAccessReviewVersion"), "")},
|
||||
knownTypes: sets.NewString(string("Webhook")),
|
||||
repeatableTypes: sets.NewString(string("Webhook")),
|
||||
},
|
||||
{
|
||||
name: "SAR should be one of v1 and v1beta1",
|
||||
configuration: api.AuthorizationConfiguration{
|
||||
Authorizers: []api.AuthorizerConfiguration{
|
||||
{
|
||||
Type: "Webhook",
|
||||
Webhook: &api.WebhookConfiguration{
|
||||
Name: "default",
|
||||
Timeout: metav1.Duration{Duration: 5 * time.Second},
|
||||
AuthorizedTTL: metav1.Duration{Duration: 5 * time.Minute},
|
||||
UnauthorizedTTL: metav1.Duration{Duration: 30 * time.Second},
|
||||
FailurePolicy: "NoOpinion",
|
||||
SubjectAccessReviewVersion: "v2beta1",
|
||||
MatchConditionSubjectAccessReviewVersion: "v1",
|
||||
ConnectionInfo: api.WebhookConnectionInfo{
|
||||
Type: "InClusterConfig",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErrList: field.ErrorList{field.NotSupported(field.NewPath("subjectAccessReviewVersion"), "v2beta1", []string{"v1", "v1beta1"})},
|
||||
knownTypes: sets.NewString(string("Webhook")),
|
||||
repeatableTypes: sets.NewString(string("Webhook")),
|
||||
},
|
||||
{
|
||||
name: "MatchConditionSAR should be defined",
|
||||
configuration: api.AuthorizationConfiguration{
|
||||
Authorizers: []api.AuthorizerConfiguration{
|
||||
{
|
||||
Type: "Webhook",
|
||||
Webhook: &api.WebhookConfiguration{
|
||||
Name: "default",
|
||||
Timeout: metav1.Duration{Duration: 5 * time.Second},
|
||||
AuthorizedTTL: metav1.Duration{Duration: 5 * time.Minute},
|
||||
UnauthorizedTTL: metav1.Duration{Duration: 30 * time.Second},
|
||||
FailurePolicy: "NoOpinion",
|
||||
SubjectAccessReviewVersion: "v1",
|
||||
ConnectionInfo: api.WebhookConnectionInfo{
|
||||
Type: "InClusterConfig",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErrList: field.ErrorList{field.Required(field.NewPath("matchConditionSubjectAccessReviewVersion"), "")},
|
||||
knownTypes: sets.NewString(string("Webhook")),
|
||||
repeatableTypes: sets.NewString(string("Webhook")),
|
||||
},
|
||||
{
|
||||
name: "MatchConditionSAR must not be anything other than v1",
|
||||
configuration: api.AuthorizationConfiguration{
|
||||
Authorizers: []api.AuthorizerConfiguration{
|
||||
{
|
||||
Type: "Webhook",
|
||||
Webhook: &api.WebhookConfiguration{
|
||||
Name: "default",
|
||||
Timeout: metav1.Duration{Duration: 5 * time.Second},
|
||||
AuthorizedTTL: metav1.Duration{Duration: 5 * time.Minute},
|
||||
UnauthorizedTTL: metav1.Duration{Duration: 30 * time.Second},
|
||||
FailurePolicy: "NoOpinion",
|
||||
SubjectAccessReviewVersion: "v1",
|
||||
MatchConditionSubjectAccessReviewVersion: "v1beta1",
|
||||
ConnectionInfo: api.WebhookConnectionInfo{
|
||||
Type: "InClusterConfig",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErrList: field.ErrorList{field.NotSupported(field.NewPath("matchConditionSubjectAccessReviewVersion"), "v1beta1", []string{"v1"})},
|
||||
knownTypes: sets.NewString(string("Webhook")),
|
||||
repeatableTypes: sets.NewString(string("Webhook")),
|
||||
},
|
||||
{
|
||||
name: "failurePolicy should be defined",
|
||||
configuration: api.AuthorizationConfiguration{
|
||||
Authorizers: []api.AuthorizerConfiguration{
|
||||
{
|
||||
Type: "Webhook",
|
||||
Webhook: &api.WebhookConfiguration{
|
||||
Name: "default",
|
||||
Timeout: metav1.Duration{Duration: 5 * time.Second},
|
||||
AuthorizedTTL: metav1.Duration{Duration: 5 * time.Minute},
|
||||
UnauthorizedTTL: metav1.Duration{Duration: 30 * time.Second},
|
||||
SubjectAccessReviewVersion: "v1",
|
||||
MatchConditionSubjectAccessReviewVersion: "v1",
|
||||
ConnectionInfo: api.WebhookConnectionInfo{
|
||||
Type: "InClusterConfig",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErrList: field.ErrorList{field.Required(field.NewPath("failurePolicy"), "")},
|
||||
knownTypes: sets.NewString(string("Webhook")),
|
||||
repeatableTypes: sets.NewString(string("Webhook")),
|
||||
},
|
||||
{
|
||||
name: "failurePolicy should be one of \"NoOpinion\" or \"Deny\"",
|
||||
configuration: api.AuthorizationConfiguration{
|
||||
Authorizers: []api.AuthorizerConfiguration{
|
||||
{
|
||||
Type: "Webhook",
|
||||
Webhook: &api.WebhookConfiguration{
|
||||
Name: "default",
|
||||
Timeout: metav1.Duration{Duration: 5 * time.Second},
|
||||
AuthorizedTTL: metav1.Duration{Duration: 5 * time.Minute},
|
||||
UnauthorizedTTL: metav1.Duration{Duration: 30 * time.Second},
|
||||
FailurePolicy: "AlwaysAllow",
|
||||
SubjectAccessReviewVersion: "v1",
|
||||
MatchConditionSubjectAccessReviewVersion: "v1",
|
||||
ConnectionInfo: api.WebhookConnectionInfo{
|
||||
Type: "InClusterConfig",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErrList: field.ErrorList{field.NotSupported(field.NewPath("failurePolicy"), "AlwaysAllow", []string{"NoOpinion", "Deny"})},
|
||||
knownTypes: sets.NewString(string("Webhook")),
|
||||
repeatableTypes: sets.NewString(string("Webhook")),
|
||||
},
|
||||
{
|
||||
name: "connectionInfo should be defined",
|
||||
configuration: api.AuthorizationConfiguration{
|
||||
Authorizers: []api.AuthorizerConfiguration{
|
||||
{
|
||||
Type: "Webhook",
|
||||
Webhook: &api.WebhookConfiguration{
|
||||
Name: "default",
|
||||
Timeout: metav1.Duration{Duration: 5 * time.Second},
|
||||
AuthorizedTTL: metav1.Duration{Duration: 5 * time.Minute},
|
||||
UnauthorizedTTL: metav1.Duration{Duration: 30 * time.Second},
|
||||
FailurePolicy: "NoOpinion",
|
||||
SubjectAccessReviewVersion: "v1",
|
||||
MatchConditionSubjectAccessReviewVersion: "v1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErrList: field.ErrorList{field.Required(field.NewPath("connectionInfo"), "")},
|
||||
knownTypes: sets.NewString(string("Webhook")),
|
||||
repeatableTypes: sets.NewString(string("Webhook")),
|
||||
},
|
||||
{
|
||||
name: "connectionInfo should be one of InClusterConfig or KubeConfigFile",
|
||||
configuration: api.AuthorizationConfiguration{
|
||||
Authorizers: []api.AuthorizerConfiguration{
|
||||
{
|
||||
Type: "Webhook",
|
||||
Webhook: &api.WebhookConfiguration{
|
||||
Name: "default",
|
||||
Timeout: metav1.Duration{Duration: 5 * time.Second},
|
||||
AuthorizedTTL: metav1.Duration{Duration: 5 * time.Minute},
|
||||
UnauthorizedTTL: metav1.Duration{Duration: 30 * time.Second},
|
||||
FailurePolicy: "NoOpinion",
|
||||
SubjectAccessReviewVersion: "v1",
|
||||
MatchConditionSubjectAccessReviewVersion: "v1",
|
||||
ConnectionInfo: api.WebhookConnectionInfo{
|
||||
Type: "ExternalClusterConfig",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErrList: field.ErrorList{
|
||||
field.NotSupported(field.NewPath("connectionInfo"), api.WebhookConnectionInfo{Type: "ExternalClusterConfig"}, []string{"InClusterConfig", "KubeConfigFile"}),
|
||||
},
|
||||
knownTypes: sets.NewString(string("Webhook")),
|
||||
repeatableTypes: sets.NewString(string("Webhook")),
|
||||
},
|
||||
{
|
||||
name: "if connectionInfo=InClusterConfig, then kubeConfigFile should be nil",
|
||||
configuration: api.AuthorizationConfiguration{
|
||||
Authorizers: []api.AuthorizerConfiguration{
|
||||
{
|
||||
Type: "Webhook",
|
||||
Webhook: &api.WebhookConfiguration{
|
||||
Name: "default",
|
||||
Timeout: metav1.Duration{Duration: 5 * time.Second},
|
||||
AuthorizedTTL: metav1.Duration{Duration: 5 * time.Minute},
|
||||
UnauthorizedTTL: metav1.Duration{Duration: 30 * time.Second},
|
||||
FailurePolicy: "NoOpinion",
|
||||
SubjectAccessReviewVersion: "v1",
|
||||
MatchConditionSubjectAccessReviewVersion: "v1",
|
||||
ConnectionInfo: api.WebhookConnectionInfo{
|
||||
Type: "InClusterConfig",
|
||||
KubeConfigFile: new(string),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErrList: field.ErrorList{
|
||||
field.Invalid(field.NewPath("connectionInfo", "kubeConfigFile"), "", "can only be set when type=KubeConfigFile"),
|
||||
},
|
||||
knownTypes: sets.NewString(string("Webhook")),
|
||||
repeatableTypes: sets.NewString(string("Webhook")),
|
||||
},
|
||||
{
|
||||
name: "if connectionInfo=KubeConfigFile, then KubeConfigFile should be defined",
|
||||
configuration: api.AuthorizationConfiguration{
|
||||
Authorizers: []api.AuthorizerConfiguration{
|
||||
{
|
||||
Type: "Webhook",
|
||||
Webhook: &api.WebhookConfiguration{
|
||||
Name: "default",
|
||||
Timeout: metav1.Duration{Duration: 5 * time.Second},
|
||||
AuthorizedTTL: metav1.Duration{Duration: 5 * time.Minute},
|
||||
UnauthorizedTTL: metav1.Duration{Duration: 30 * time.Second},
|
||||
FailurePolicy: "NoOpinion",
|
||||
SubjectAccessReviewVersion: "v1",
|
||||
MatchConditionSubjectAccessReviewVersion: "v1",
|
||||
ConnectionInfo: api.WebhookConnectionInfo{
|
||||
Type: "KubeConfigFile",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErrList: field.ErrorList{field.Required(field.NewPath("kubeConfigFile"), "")},
|
||||
knownTypes: sets.NewString(string("Webhook")),
|
||||
repeatableTypes: sets.NewString(string("Webhook")),
|
||||
},
|
||||
{
|
||||
name: "if connectionInfo=KubeConfigFile, then KubeConfigFile should be defined, must be an absolute path, should exist, shouldn't be a symlink",
|
||||
configuration: api.AuthorizationConfiguration{
|
||||
Authorizers: []api.AuthorizerConfiguration{
|
||||
{
|
||||
Type: "Webhook",
|
||||
Webhook: &api.WebhookConfiguration{
|
||||
Name: "default",
|
||||
Timeout: metav1.Duration{Duration: 5 * time.Second},
|
||||
AuthorizedTTL: metav1.Duration{Duration: 5 * time.Minute},
|
||||
UnauthorizedTTL: metav1.Duration{Duration: 30 * time.Second},
|
||||
FailurePolicy: "NoOpinion",
|
||||
SubjectAccessReviewVersion: "v1",
|
||||
MatchConditionSubjectAccessReviewVersion: "v1",
|
||||
ConnectionInfo: api.WebhookConnectionInfo{
|
||||
Type: "KubeConfigFile",
|
||||
KubeConfigFile: &badKubeConfigFile,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErrList: field.ErrorList{field.Invalid(field.NewPath("kubeConfigFile"), badKubeConfigFile, "must be an absolute path")},
|
||||
knownTypes: sets.NewString(string("Webhook")),
|
||||
repeatableTypes: sets.NewString(string("Webhook")),
|
||||
},
|
||||
{
|
||||
name: "if connectionInfo=KubeConfigFile, an existent file needs to be passed",
|
||||
configuration: api.AuthorizationConfiguration{
|
||||
Authorizers: []api.AuthorizerConfiguration{
|
||||
{
|
||||
Type: "Webhook",
|
||||
Webhook: &api.WebhookConfiguration{
|
||||
Name: "default",
|
||||
Timeout: metav1.Duration{Duration: 5 * time.Second},
|
||||
AuthorizedTTL: metav1.Duration{Duration: 5 * time.Minute},
|
||||
UnauthorizedTTL: metav1.Duration{Duration: 30 * time.Second},
|
||||
FailurePolicy: "NoOpinion",
|
||||
SubjectAccessReviewVersion: "v1",
|
||||
MatchConditionSubjectAccessReviewVersion: "v1",
|
||||
ConnectionInfo: api.WebhookConnectionInfo{
|
||||
Type: "KubeConfigFile",
|
||||
KubeConfigFile: &tempKubeConfigFilePath,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErrList: field.ErrorList{},
|
||||
knownTypes: sets.NewString(string("Webhook")),
|
||||
repeatableTypes: sets.NewString(string("Webhook")),
|
||||
},
|
||||
|
||||
// TODO: When the CEL expression validator is implemented, add a few test cases to typecheck the expression
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
errList := ValidateAuthorizationConfiguration(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)
|
||||
}
|
||||
|
||||
for i, expected := range test.expectedErrList {
|
||||
if expected.Type.String() != errList[i].Type.String() {
|
||||
t.Errorf("expected err type %s, got %s",
|
||||
expected.Type.String(),
|
||||
errList[i].Type.String())
|
||||
}
|
||||
if expected.BadValue != errList[i].BadValue {
|
||||
t.Errorf("expected bad value '%s', got '%s'",
|
||||
expected.BadValue,
|
||||
errList[i].BadValue)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -110,6 +110,59 @@ func (in *AuthenticationConfiguration) DeepCopyObject() runtime.Object {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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 *ClaimMappings) DeepCopyInto(out *ClaimMappings) {
|
||||
*out = *in
|
||||
@ -383,3 +436,65 @@ func (in *UDSTransport) DeepCopy() *UDSTransport {
|
||||
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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user