1
16
17 package authorizer
18
19 import (
20 "bytes"
21 "context"
22 "errors"
23 "fmt"
24 "os"
25 "reflect"
26 "sync"
27 "sync/atomic"
28 "time"
29
30 "k8s.io/apimachinery/pkg/util/sets"
31 authzconfig "k8s.io/apiserver/pkg/apis/apiserver"
32 "k8s.io/apiserver/pkg/authentication/user"
33 "k8s.io/apiserver/pkg/authorization/authorizer"
34 "k8s.io/apiserver/pkg/authorization/authorizerfactory"
35 "k8s.io/apiserver/pkg/authorization/cel"
36 authorizationmetrics "k8s.io/apiserver/pkg/authorization/metrics"
37 "k8s.io/apiserver/pkg/authorization/union"
38 "k8s.io/apiserver/pkg/server/options/authorizationconfig/metrics"
39 webhookutil "k8s.io/apiserver/pkg/util/webhook"
40 "k8s.io/apiserver/plugin/pkg/authorizer/webhook"
41 webhookmetrics "k8s.io/apiserver/plugin/pkg/authorizer/webhook/metrics"
42 "k8s.io/klog/v2"
43 "k8s.io/kubernetes/pkg/auth/authorizer/abac"
44 "k8s.io/kubernetes/pkg/kubeapiserver/authorizer/modes"
45 "k8s.io/kubernetes/pkg/util/filesystem"
46 "k8s.io/kubernetes/plugin/pkg/auth/authorizer/node"
47 "k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac"
48 )
49
50 type reloadableAuthorizerResolver struct {
51
52
53
54 initialConfig Config
55
56 apiServerID string
57
58 reloadInterval time.Duration
59 requireNonWebhookTypes sets.Set[authzconfig.AuthorizerType]
60
61 nodeAuthorizer *node.NodeAuthorizer
62 rbacAuthorizer *rbac.RBACAuthorizer
63 abacAuthorizer abac.PolicyList
64
65 lastLoadedLock sync.Mutex
66 lastLoadedConfig *authzconfig.AuthorizationConfiguration
67 lastReadData []byte
68
69 current atomic.Pointer[authorizerResolver]
70 }
71
72 type authorizerResolver struct {
73 authorizer authorizer.Authorizer
74 ruleResolver authorizer.RuleResolver
75 }
76
77 func (r *reloadableAuthorizerResolver) Authorize(ctx context.Context, a authorizer.Attributes) (authorized authorizer.Decision, reason string, err error) {
78 return r.current.Load().authorizer.Authorize(ctx, a)
79 }
80
81 func (r *reloadableAuthorizerResolver) RulesFor(user user.Info, namespace string) ([]authorizer.ResourceRuleInfo, []authorizer.NonResourceRuleInfo, bool, error) {
82 return r.current.Load().ruleResolver.RulesFor(user, namespace)
83 }
84
85
86 func (r *reloadableAuthorizerResolver) newForConfig(authzConfig *authzconfig.AuthorizationConfiguration) (authorizer.Authorizer, authorizer.RuleResolver, error) {
87 if len(authzConfig.Authorizers) == 0 {
88 return nil, nil, fmt.Errorf("at least one authorization mode must be passed")
89 }
90
91 var (
92 authorizers []authorizer.Authorizer
93 ruleResolvers []authorizer.RuleResolver
94 )
95
96
97 superuserAuthorizer := authorizerfactory.NewPrivilegedGroups(user.SystemPrivilegedGroup)
98 authorizers = append(authorizers, superuserAuthorizer)
99
100 for _, configuredAuthorizer := range authzConfig.Authorizers {
101
102 switch configuredAuthorizer.Type {
103 case authzconfig.AuthorizerType(modes.ModeNode):
104 if r.nodeAuthorizer == nil {
105 return nil, nil, fmt.Errorf("authorizer type Node is not allowed if it was not enabled at initial server startup")
106 }
107 authorizers = append(authorizers, authorizationmetrics.InstrumentedAuthorizer(string(configuredAuthorizer.Type), configuredAuthorizer.Name, r.nodeAuthorizer))
108 ruleResolvers = append(ruleResolvers, r.nodeAuthorizer)
109 case authzconfig.AuthorizerType(modes.ModeAlwaysAllow):
110 alwaysAllowAuthorizer := authorizerfactory.NewAlwaysAllowAuthorizer()
111 authorizers = append(authorizers, authorizationmetrics.InstrumentedAuthorizer(string(configuredAuthorizer.Type), configuredAuthorizer.Name, alwaysAllowAuthorizer))
112 ruleResolvers = append(ruleResolvers, alwaysAllowAuthorizer)
113 case authzconfig.AuthorizerType(modes.ModeAlwaysDeny):
114 alwaysDenyAuthorizer := authorizerfactory.NewAlwaysDenyAuthorizer()
115 authorizers = append(authorizers, authorizationmetrics.InstrumentedAuthorizer(string(configuredAuthorizer.Type), configuredAuthorizer.Name, alwaysDenyAuthorizer))
116 ruleResolvers = append(ruleResolvers, alwaysDenyAuthorizer)
117 case authzconfig.AuthorizerType(modes.ModeABAC):
118 if r.abacAuthorizer == nil {
119 return nil, nil, fmt.Errorf("authorizer type ABAC is not allowed if it was not enabled at initial server startup")
120 }
121 authorizers = append(authorizers, authorizationmetrics.InstrumentedAuthorizer(string(configuredAuthorizer.Type), configuredAuthorizer.Name, r.abacAuthorizer))
122 ruleResolvers = append(ruleResolvers, r.abacAuthorizer)
123 case authzconfig.AuthorizerType(modes.ModeWebhook):
124 if r.initialConfig.WebhookRetryBackoff == nil {
125 return nil, nil, errors.New("retry backoff parameters for authorization webhook has not been specified")
126 }
127 clientConfig, err := webhookutil.LoadKubeconfig(*configuredAuthorizer.Webhook.ConnectionInfo.KubeConfigFile, r.initialConfig.CustomDial)
128 if err != nil {
129 return nil, nil, err
130 }
131 var decisionOnError authorizer.Decision
132 switch configuredAuthorizer.Webhook.FailurePolicy {
133 case authzconfig.FailurePolicyNoOpinion:
134 decisionOnError = authorizer.DecisionNoOpinion
135 case authzconfig.FailurePolicyDeny:
136 decisionOnError = authorizer.DecisionDeny
137 default:
138 return nil, nil, fmt.Errorf("unknown failurePolicy %q", configuredAuthorizer.Webhook.FailurePolicy)
139 }
140 webhookAuthorizer, err := webhook.New(clientConfig,
141 configuredAuthorizer.Webhook.SubjectAccessReviewVersion,
142 configuredAuthorizer.Webhook.AuthorizedTTL.Duration,
143 configuredAuthorizer.Webhook.UnauthorizedTTL.Duration,
144 *r.initialConfig.WebhookRetryBackoff,
145 decisionOnError,
146 configuredAuthorizer.Webhook.MatchConditions,
147 configuredAuthorizer.Name,
148 kubeapiserverWebhookMetrics{WebhookMetrics: webhookmetrics.NewWebhookMetrics(), MatcherMetrics: cel.NewMatcherMetrics()},
149 )
150 if err != nil {
151 return nil, nil, err
152 }
153 authorizers = append(authorizers, authorizationmetrics.InstrumentedAuthorizer(string(configuredAuthorizer.Type), configuredAuthorizer.Name, webhookAuthorizer))
154 ruleResolvers = append(ruleResolvers, webhookAuthorizer)
155 case authzconfig.AuthorizerType(modes.ModeRBAC):
156 if r.rbacAuthorizer == nil {
157 return nil, nil, fmt.Errorf("authorizer type RBAC is not allowed if it was not enabled at initial server startup")
158 }
159 authorizers = append(authorizers, authorizationmetrics.InstrumentedAuthorizer(string(configuredAuthorizer.Type), configuredAuthorizer.Name, r.rbacAuthorizer))
160 ruleResolvers = append(ruleResolvers, r.rbacAuthorizer)
161 default:
162 return nil, nil, fmt.Errorf("unknown authorization mode %s specified", configuredAuthorizer.Type)
163 }
164 }
165
166 return union.New(authorizers...), union.NewRuleResolvers(ruleResolvers...), nil
167 }
168
169 type kubeapiserverWebhookMetrics struct {
170
171 webhookmetrics.NoopRequestMetrics
172
173 webhookmetrics.WebhookMetrics
174
175 cel.MatcherMetrics
176 }
177
178
179
180 func (r *reloadableAuthorizerResolver) runReload(ctx context.Context) {
181 metrics.RegisterMetrics()
182 metrics.RecordAuthorizationConfigAutomaticReloadSuccess(r.apiServerID)
183
184 filesystem.WatchUntil(
185 ctx,
186 r.reloadInterval,
187 r.initialConfig.ReloadFile,
188 func() {
189 r.checkFile(ctx)
190 },
191 func(err error) {
192 klog.ErrorS(err, "watching authorization config file")
193 },
194 )
195 }
196
197 func (r *reloadableAuthorizerResolver) checkFile(ctx context.Context) {
198 r.lastLoadedLock.Lock()
199 defer r.lastLoadedLock.Unlock()
200
201 data, err := os.ReadFile(r.initialConfig.ReloadFile)
202 if err != nil {
203 klog.ErrorS(err, "reloading authorization config")
204 metrics.RecordAuthorizationConfigAutomaticReloadFailure(r.apiServerID)
205 return
206 }
207 if bytes.Equal(data, r.lastReadData) {
208
209 return
210 }
211 klog.InfoS("found new authorization config data")
212 r.lastReadData = data
213
214 config, err := LoadAndValidateData(data, r.requireNonWebhookTypes)
215 if err != nil {
216 klog.ErrorS(err, "reloading authorization config")
217 metrics.RecordAuthorizationConfigAutomaticReloadFailure(r.apiServerID)
218 return
219 }
220 if reflect.DeepEqual(config, r.lastLoadedConfig) {
221
222 return
223 }
224 klog.InfoS("found new authorization config")
225 r.lastLoadedConfig = config
226
227 authorizer, ruleResolver, err := r.newForConfig(config)
228 if err != nil {
229 klog.ErrorS(err, "reloading authorization config")
230 metrics.RecordAuthorizationConfigAutomaticReloadFailure(r.apiServerID)
231 return
232 }
233 klog.InfoS("constructed new authorizer")
234
235 r.current.Store(&authorizerResolver{
236 authorizer: authorizer,
237 ruleResolver: ruleResolver,
238 })
239 klog.InfoS("reloaded authz config")
240 metrics.RecordAuthorizationConfigAutomaticReloadSuccess(r.apiServerID)
241 }
242
View as plain text