k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/pkg/kubeapiserver/authorizer/config.go (about) 1 /* 2 Copyright 2016 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package authorizer 18 19 import ( 20 "context" 21 "fmt" 22 "os" 23 "strings" 24 "time" 25 26 utilerrors "k8s.io/apimachinery/pkg/util/errors" 27 utilnet "k8s.io/apimachinery/pkg/util/net" 28 "k8s.io/apimachinery/pkg/util/sets" 29 "k8s.io/apimachinery/pkg/util/wait" 30 authzconfig "k8s.io/apiserver/pkg/apis/apiserver" 31 "k8s.io/apiserver/pkg/apis/apiserver/load" 32 "k8s.io/apiserver/pkg/apis/apiserver/validation" 33 "k8s.io/apiserver/pkg/authorization/authorizer" 34 utilfeature "k8s.io/apiserver/pkg/util/feature" 35 versionedinformers "k8s.io/client-go/informers" 36 resourcev1alpha2informers "k8s.io/client-go/informers/resource/v1alpha2" 37 "k8s.io/kubernetes/pkg/auth/authorizer/abac" 38 "k8s.io/kubernetes/pkg/auth/nodeidentifier" 39 "k8s.io/kubernetes/pkg/features" 40 "k8s.io/kubernetes/pkg/kubeapiserver/authorizer/modes" 41 "k8s.io/kubernetes/plugin/pkg/auth/authorizer/node" 42 "k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac" 43 "k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/bootstrappolicy" 44 ) 45 46 // Config contains the data on how to authorize a request to the Kube API Server 47 type Config struct { 48 // Options for ModeABAC 49 50 // Path to an ABAC policy file. 51 PolicyFile string 52 53 // Options for ModeWebhook 54 55 // WebhookRetryBackoff specifies the backoff parameters for the authorization webhook retry logic. 56 // This allows us to configure the sleep time at each iteration and the maximum number of retries allowed 57 // before we fail the webhook call in order to limit the fan out that ensues when the system is degraded. 58 WebhookRetryBackoff *wait.Backoff 59 60 VersionedInformerFactory versionedinformers.SharedInformerFactory 61 62 // Optional field, custom dial function used to connect to webhook 63 CustomDial utilnet.DialFunc 64 65 // ReloadFile holds the filename to reload authorization configuration from 66 ReloadFile string 67 // AuthorizationConfiguration stores the configuration for the Authorizer chain 68 // It will deprecate most of the above flags when GA 69 AuthorizationConfiguration *authzconfig.AuthorizationConfiguration 70 } 71 72 // New returns the right sort of union of multiple authorizer.Authorizer objects 73 // based on the authorizationMode or an error. 74 // stopCh is used to shut down config reload goroutines when the server is shutting down. 75 func (config Config) New(ctx context.Context, serverID string) (authorizer.Authorizer, authorizer.RuleResolver, error) { 76 if len(config.AuthorizationConfiguration.Authorizers) == 0 { 77 return nil, nil, fmt.Errorf("at least one authorization mode must be passed") 78 } 79 80 r := &reloadableAuthorizerResolver{ 81 initialConfig: config, 82 apiServerID: serverID, 83 lastLoadedConfig: config.AuthorizationConfiguration, 84 reloadInterval: time.Minute, 85 } 86 87 seenTypes := sets.New[authzconfig.AuthorizerType]() 88 89 // Build and store authorizers which will persist across reloads 90 for _, configuredAuthorizer := range config.AuthorizationConfiguration.Authorizers { 91 seenTypes.Insert(configuredAuthorizer.Type) 92 93 // Keep cases in sync with constant list in k8s.io/kubernetes/pkg/kubeapiserver/authorizer/modes/modes.go. 94 switch configuredAuthorizer.Type { 95 case authzconfig.AuthorizerType(modes.ModeNode): 96 var slices resourcev1alpha2informers.ResourceSliceInformer 97 if utilfeature.DefaultFeatureGate.Enabled(features.DynamicResourceAllocation) { 98 slices = config.VersionedInformerFactory.Resource().V1alpha2().ResourceSlices() 99 } 100 node.RegisterMetrics() 101 graph := node.NewGraph() 102 node.AddGraphEventHandlers( 103 graph, 104 config.VersionedInformerFactory.Core().V1().Nodes(), 105 config.VersionedInformerFactory.Core().V1().Pods(), 106 config.VersionedInformerFactory.Core().V1().PersistentVolumes(), 107 config.VersionedInformerFactory.Storage().V1().VolumeAttachments(), 108 slices, // Nil check in AddGraphEventHandlers can be removed when always creating this. 109 ) 110 r.nodeAuthorizer = node.NewAuthorizer(graph, nodeidentifier.NewDefaultNodeIdentifier(), bootstrappolicy.NodeRules()) 111 112 case authzconfig.AuthorizerType(modes.ModeABAC): 113 var err error 114 r.abacAuthorizer, err = abac.NewFromFile(config.PolicyFile) 115 if err != nil { 116 return nil, nil, err 117 } 118 case authzconfig.AuthorizerType(modes.ModeRBAC): 119 r.rbacAuthorizer = rbac.New( 120 &rbac.RoleGetter{Lister: config.VersionedInformerFactory.Rbac().V1().Roles().Lister()}, 121 &rbac.RoleBindingLister{Lister: config.VersionedInformerFactory.Rbac().V1().RoleBindings().Lister()}, 122 &rbac.ClusterRoleGetter{Lister: config.VersionedInformerFactory.Rbac().V1().ClusterRoles().Lister()}, 123 &rbac.ClusterRoleBindingLister{Lister: config.VersionedInformerFactory.Rbac().V1().ClusterRoleBindings().Lister()}, 124 ) 125 } 126 } 127 128 // Require all non-webhook authorizer types to remain specified in the file on reload 129 seenTypes.Delete(authzconfig.TypeWebhook) 130 r.requireNonWebhookTypes = seenTypes 131 132 // Construct the authorizers / ruleResolvers for the given configuration 133 authorizer, ruleResolver, err := r.newForConfig(r.initialConfig.AuthorizationConfiguration) 134 if err != nil { 135 return nil, nil, err 136 } 137 138 r.current.Store(&authorizerResolver{ 139 authorizer: authorizer, 140 ruleResolver: ruleResolver, 141 }) 142 143 if r.initialConfig.ReloadFile != "" { 144 go r.runReload(ctx) 145 } 146 147 return r, r, nil 148 } 149 150 // RepeatableAuthorizerTypes is the list of Authorizer that can be repeated in the Authorization Config 151 var repeatableAuthorizerTypes = []string{modes.ModeWebhook} 152 153 // GetNameForAuthorizerMode returns the name to be set for the mode in AuthorizationConfiguration 154 // For now, lower cases the mode name 155 func GetNameForAuthorizerMode(mode string) string { 156 return strings.ToLower(mode) 157 } 158 159 func LoadAndValidateFile(configFile string, requireNonWebhookTypes sets.Set[authzconfig.AuthorizerType]) (*authzconfig.AuthorizationConfiguration, error) { 160 data, err := os.ReadFile(configFile) 161 if err != nil { 162 return nil, err 163 } 164 return LoadAndValidateData(data, requireNonWebhookTypes) 165 } 166 167 func LoadAndValidateData(data []byte, requireNonWebhookTypes sets.Set[authzconfig.AuthorizerType]) (*authzconfig.AuthorizationConfiguration, error) { 168 // load the file and check for errors 169 authorizationConfiguration, err := load.LoadFromData(data) 170 if err != nil { 171 return nil, fmt.Errorf("failed to load AuthorizationConfiguration from file: %w", err) 172 } 173 174 // validate the file and return any error 175 if errors := validation.ValidateAuthorizationConfiguration(nil, authorizationConfiguration, 176 sets.NewString(modes.AuthorizationModeChoices...), 177 sets.NewString(repeatableAuthorizerTypes...), 178 ); len(errors) != 0 { 179 return nil, fmt.Errorf(errors.ToAggregate().Error()) 180 } 181 182 // test to check if the authorizer names passed conform to the authorizers for type!=Webhook 183 // this test is only for kube-apiserver and hence checked here 184 // it preserves compatibility with o.buildAuthorizationConfiguration 185 var allErrors []error 186 seenModes := sets.New[authzconfig.AuthorizerType]() 187 for _, authorizer := range authorizationConfiguration.Authorizers { 188 if string(authorizer.Type) == modes.ModeWebhook { 189 continue 190 } 191 seenModes.Insert(authorizer.Type) 192 193 expectedName := GetNameForAuthorizerMode(string(authorizer.Type)) 194 if expectedName != authorizer.Name { 195 allErrors = append(allErrors, fmt.Errorf("expected name %s for authorizer %s instead of %s", expectedName, authorizer.Type, authorizer.Name)) 196 } 197 198 } 199 200 if missingTypes := requireNonWebhookTypes.Difference(seenModes); missingTypes.Len() > 0 { 201 allErrors = append(allErrors, fmt.Errorf("missing required types: %v", sets.List(missingTypes))) 202 } 203 204 if len(allErrors) > 0 { 205 return nil, utilerrors.NewAggregate(allErrors) 206 } 207 208 return authorizationConfiguration, nil 209 }