1
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
47 type Config struct {
48
49
50
51 PolicyFile string
52
53
54
55
56
57
58 WebhookRetryBackoff *wait.Backoff
59
60 VersionedInformerFactory versionedinformers.SharedInformerFactory
61
62
63 CustomDial utilnet.DialFunc
64
65
66 ReloadFile string
67
68
69 AuthorizationConfiguration *authzconfig.AuthorizationConfiguration
70 }
71
72
73
74
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
90 for _, configuredAuthorizer := range config.AuthorizationConfiguration.Authorizers {
91 seenTypes.Insert(configuredAuthorizer.Type)
92
93
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,
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
129 seenTypes.Delete(authzconfig.TypeWebhook)
130 r.requireNonWebhookTypes = seenTypes
131
132
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
151 var repeatableAuthorizerTypes = []string{modes.ModeWebhook}
152
153
154
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
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
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
183
184
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 }
210
View as plain text