1
16
17 package options
18
19 import (
20 "fmt"
21 "strings"
22 "time"
23
24 genericfeatures "k8s.io/apiserver/pkg/features"
25 utilfeature "k8s.io/apiserver/pkg/util/feature"
26
27 "github.com/spf13/pflag"
28
29 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
30 "k8s.io/apimachinery/pkg/util/sets"
31 "k8s.io/apimachinery/pkg/util/wait"
32 authzconfig "k8s.io/apiserver/pkg/apis/apiserver"
33 genericoptions "k8s.io/apiserver/pkg/server/options"
34 versionedinformers "k8s.io/client-go/informers"
35
36 "k8s.io/kubernetes/pkg/kubeapiserver/authorizer"
37 authzmodes "k8s.io/kubernetes/pkg/kubeapiserver/authorizer/modes"
38 )
39
40 const (
41 defaultWebhookName = "default"
42 authorizationModeFlag = "authorization-mode"
43 authorizationWebhookConfigFileFlag = "authorization-webhook-config-file"
44 authorizationWebhookVersionFlag = "authorization-webhook-version"
45 authorizationWebhookAuthorizedTTLFlag = "authorization-webhook-cache-authorized-ttl"
46 authorizationWebhookUnauthorizedTTLFlag = "authorization-webhook-cache-unauthorized-ttl"
47 authorizationPolicyFileFlag = "authorization-policy-file"
48 authorizationConfigFlag = "authorization-config"
49 )
50
51
52 type BuiltInAuthorizationOptions struct {
53 Modes []string
54 PolicyFile string
55 WebhookConfigFile string
56 WebhookVersion string
57 WebhookCacheAuthorizedTTL time.Duration
58 WebhookCacheUnauthorizedTTL time.Duration
59
60
61
62 WebhookRetryBackoff *wait.Backoff
63
64
65
66
67
68
69
70 AuthorizationConfigurationFile string
71
72 AreLegacyFlagsSet func() bool
73 }
74
75
76 func NewBuiltInAuthorizationOptions() *BuiltInAuthorizationOptions {
77 return &BuiltInAuthorizationOptions{
78 Modes: []string{},
79 WebhookVersion: "v1beta1",
80 WebhookCacheAuthorizedTTL: 5 * time.Minute,
81 WebhookCacheUnauthorizedTTL: 30 * time.Second,
82 WebhookRetryBackoff: genericoptions.DefaultAuthWebhookRetryBackoff(),
83 }
84 }
85
86
87 func (o *BuiltInAuthorizationOptions) Complete() []error {
88 if len(o.AuthorizationConfigurationFile) == 0 && len(o.Modes) == 0 {
89 o.Modes = []string{authzmodes.ModeAlwaysAllow}
90 }
91 return nil
92 }
93
94
95 func (o *BuiltInAuthorizationOptions) Validate() []error {
96 if o == nil {
97 return nil
98 }
99 var allErrors []error
100
101
102
103
104
105
106 if o.AuthorizationConfigurationFile != "" {
107 if !utilfeature.DefaultFeatureGate.Enabled(genericfeatures.StructuredAuthorizationConfiguration) {
108 return append(allErrors, fmt.Errorf("--%s cannot be used without enabling StructuredAuthorizationConfiguration feature flag", authorizationConfigFlag))
109 }
110
111
112 if o.AreLegacyFlagsSet != nil && o.AreLegacyFlagsSet() {
113 return append(allErrors, fmt.Errorf("--%s can not be specified when --%s or --authorization-webhook-* flags are defined", authorizationConfigFlag, authorizationModeFlag))
114 }
115
116
117 _, err := authorizer.LoadAndValidateFile(o.AuthorizationConfigurationFile, nil)
118 if err != nil {
119 return append(allErrors, err)
120 }
121
122 return allErrors
123 }
124
125
126 if len(o.Modes) == 0 {
127 allErrors = append(allErrors, fmt.Errorf("at least one authorization-mode must be passed"))
128 }
129
130 modes := sets.NewString(o.Modes...)
131 for _, mode := range o.Modes {
132 if !authzmodes.IsValidAuthorizationMode(mode) {
133 allErrors = append(allErrors, fmt.Errorf("authorization-mode %q is not a valid mode", mode))
134 }
135 if mode == authzmodes.ModeABAC && o.PolicyFile == "" {
136 allErrors = append(allErrors, fmt.Errorf("authorization-mode ABAC's authorization policy file not passed"))
137 }
138 if mode == authzmodes.ModeWebhook && o.WebhookConfigFile == "" {
139 allErrors = append(allErrors, fmt.Errorf("authorization-mode Webhook's authorization config file not passed"))
140 }
141 }
142
143 if o.PolicyFile != "" && !modes.Has(authzmodes.ModeABAC) {
144 allErrors = append(allErrors, fmt.Errorf("cannot specify --authorization-policy-file without mode ABAC"))
145 }
146
147 if o.WebhookConfigFile != "" && !modes.Has(authzmodes.ModeWebhook) {
148 allErrors = append(allErrors, fmt.Errorf("cannot specify --authorization-webhook-config-file without mode Webhook"))
149 }
150
151 if len(o.Modes) != modes.Len() {
152 allErrors = append(allErrors, fmt.Errorf("authorization-mode %q has mode specified more than once", o.Modes))
153 }
154
155 if o.WebhookRetryBackoff != nil && o.WebhookRetryBackoff.Steps <= 0 {
156 allErrors = append(allErrors, fmt.Errorf("number of webhook retry attempts must be greater than 0, but is: %d", o.WebhookRetryBackoff.Steps))
157 }
158
159 return allErrors
160 }
161
162
163 func (o *BuiltInAuthorizationOptions) AddFlags(fs *pflag.FlagSet) {
164 if o == nil {
165 return
166 }
167
168 fs.StringSliceVar(&o.Modes, authorizationModeFlag, o.Modes, ""+
169 "Ordered list of plug-ins to do authorization on secure port. Defaults to AlwaysAllow if --authorization-config is not used. Comma-delimited list of: "+
170 strings.Join(authzmodes.AuthorizationModeChoices, ",")+".")
171
172 fs.StringVar(&o.PolicyFile, authorizationPolicyFileFlag, o.PolicyFile, ""+
173 "File with authorization policy in json line by line format, used with --authorization-mode=ABAC, on the secure port.")
174
175 fs.StringVar(&o.WebhookConfigFile, authorizationWebhookConfigFileFlag, o.WebhookConfigFile, ""+
176 "File with webhook configuration in kubeconfig format, used with --authorization-mode=Webhook. "+
177 "The API server will query the remote service to determine access on the API server's secure port.")
178
179 fs.StringVar(&o.WebhookVersion, authorizationWebhookVersionFlag, o.WebhookVersion, ""+
180 "The API version of the authorization.k8s.io SubjectAccessReview to send to and expect from the webhook.")
181
182 fs.DurationVar(&o.WebhookCacheAuthorizedTTL, authorizationWebhookAuthorizedTTLFlag,
183 o.WebhookCacheAuthorizedTTL,
184 "The duration to cache 'authorized' responses from the webhook authorizer.")
185
186 fs.DurationVar(&o.WebhookCacheUnauthorizedTTL,
187 authorizationWebhookUnauthorizedTTLFlag, o.WebhookCacheUnauthorizedTTL,
188 "The duration to cache 'unauthorized' responses from the webhook authorizer.")
189
190 fs.StringVar(&o.AuthorizationConfigurationFile, authorizationConfigFlag, o.AuthorizationConfigurationFile, ""+
191 "File with Authorization Configuration to configure the authorizer chain."+
192 "Note: This feature is in Alpha since v1.29."+
193 "--feature-gate=StructuredAuthorizationConfiguration=true feature flag needs to be set to true for enabling the functionality."+
194 "This feature is mutually exclusive with the other --authorization-mode and --authorization-webhook-* flags.")
195
196
197 oldAreLegacyFlagsSet := o.AreLegacyFlagsSet
198 o.AreLegacyFlagsSet = func() bool {
199 if oldAreLegacyFlagsSet != nil && oldAreLegacyFlagsSet() {
200 return true
201 }
202
203 return fs.Changed(authorizationModeFlag) ||
204 fs.Changed(authorizationWebhookConfigFileFlag) ||
205 fs.Changed(authorizationWebhookVersionFlag) ||
206 fs.Changed(authorizationWebhookAuthorizedTTLFlag) ||
207 fs.Changed(authorizationWebhookUnauthorizedTTLFlag)
208 }
209 }
210
211
212 func (o *BuiltInAuthorizationOptions) ToAuthorizationConfig(versionedInformerFactory versionedinformers.SharedInformerFactory) (*authorizer.Config, error) {
213 if o == nil {
214 return nil, nil
215 }
216
217 var authorizationConfiguration *authzconfig.AuthorizationConfiguration
218 var err error
219
220
221
222
223
224
225
226
227 if o.AuthorizationConfigurationFile != "" {
228 if !utilfeature.DefaultFeatureGate.Enabled(genericfeatures.StructuredAuthorizationConfiguration) {
229 return nil, fmt.Errorf("--%s cannot be used without enabling StructuredAuthorizationConfiguration feature flag", authorizationConfigFlag)
230 }
231
232 if o.AreLegacyFlagsSet != nil && o.AreLegacyFlagsSet() {
233 return nil, fmt.Errorf("--%s can not be specified when --%s or --authorization-webhook-* flags are defined", authorizationConfigFlag, authorizationModeFlag)
234 }
235
236 authorizationConfiguration, err = authorizer.LoadAndValidateFile(o.AuthorizationConfigurationFile, nil)
237 if err != nil {
238 return nil, err
239 }
240 } else {
241 authorizationConfiguration, err = o.buildAuthorizationConfiguration()
242 if err != nil {
243 return nil, fmt.Errorf("failed to build authorization config: %s", err)
244 }
245 }
246
247 return &authorizer.Config{
248 PolicyFile: o.PolicyFile,
249 VersionedInformerFactory: versionedInformerFactory,
250 WebhookRetryBackoff: o.WebhookRetryBackoff,
251
252 ReloadFile: o.AuthorizationConfigurationFile,
253 AuthorizationConfiguration: authorizationConfiguration,
254 }, nil
255 }
256
257
258 func (o *BuiltInAuthorizationOptions) buildAuthorizationConfiguration() (*authzconfig.AuthorizationConfiguration, error) {
259 var authorizers []authzconfig.AuthorizerConfiguration
260
261 if len(o.Modes) != sets.NewString(o.Modes...).Len() {
262 return nil, fmt.Errorf("modes should not be repeated in --authorization-mode")
263 }
264
265 for _, mode := range o.Modes {
266 switch mode {
267 case authzmodes.ModeWebhook:
268 authorizers = append(authorizers, authzconfig.AuthorizerConfiguration{
269 Type: authzconfig.TypeWebhook,
270 Name: defaultWebhookName,
271 Webhook: &authzconfig.WebhookConfiguration{
272 AuthorizedTTL: metav1.Duration{Duration: o.WebhookCacheAuthorizedTTL},
273 UnauthorizedTTL: metav1.Duration{Duration: o.WebhookCacheUnauthorizedTTL},
274
275
276 Timeout: metav1.Duration{Duration: 30 * time.Second},
277 FailurePolicy: authzconfig.FailurePolicyNoOpinion,
278 SubjectAccessReviewVersion: o.WebhookVersion,
279 ConnectionInfo: authzconfig.WebhookConnectionInfo{
280 Type: authzconfig.AuthorizationWebhookConnectionInfoTypeKubeConfigFile,
281 KubeConfigFile: &o.WebhookConfigFile,
282 },
283 },
284 })
285 default:
286 authorizers = append(authorizers, authzconfig.AuthorizerConfiguration{
287 Type: authzconfig.AuthorizerType(mode),
288 Name: authorizer.GetNameForAuthorizerMode(mode),
289 })
290 }
291 }
292
293 return &authzconfig.AuthorizationConfiguration{Authorizers: authorizers}, nil
294 }
295
View as plain text