bc25d99223d0c9c356de3a210ac43d5cdc9eb37a
[integration.git] / test / security / k8s / src / check / validators / master / api.go
1 package master
2
3 import (
4         "strconv"
5         "strings"
6 )
7
8 const (
9         portDisabled = 0
10         portLowest   = 1
11         portHighest  = 65536
12
13         auditLogAge     = 30
14         auditLogBackups = 10
15         auditLogSize    = 100
16
17         strongCryptoCiphers = "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM" +
18                 "_SHA256,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_AES_256_GCM" +
19                 "_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_AES_256_GCM" +
20                 "_SHA384,TLS_RSA_WITH_AES_256_GCM_SHA384,TLS_RSA_WITH_AES_128_GCM_SHA256"
21 )
22
23 // IsBasicAuthFileAbsent validates there is no basic authentication file specified.
24 func IsBasicAuthFileAbsent(params []string) bool {
25         return isFlagAbsent("--basic-auth-file=", params)
26 }
27
28 // IsTokenAuthFileAbsent validates there is no token based authentication file specified.
29 func IsTokenAuthFileAbsent(params []string) bool {
30         return isFlagAbsent("--token-auth-file=", params)
31 }
32
33 // IsInsecureAllowAnyTokenAbsent validates insecure tokens are not accepted.
34 func IsInsecureAllowAnyTokenAbsent(params []string) bool {
35         return isFlagAbsent("--insecure-allow-any-token", params)
36 }
37
38 // isFlagAbsent checks absence of selected flag in parameters.
39 func isFlagAbsent(flag string, params []string) bool {
40         found := filterFlags(params, flag)
41         if len(found) != 0 {
42                 return false
43         }
44         return true
45 }
46
47 // IsAnonymousAuthDisabled validates there is single "--anonymous-auth" flag and it is set to "false".
48 func IsAnonymousAuthDisabled(params []string) bool {
49         return hasSingleFlagArgument("--anonymous-auth=", "false", params)
50 }
51
52 // IsInsecurePortUnbound validates there is single "--insecure-port" flag and it is set to "0" (disabled).
53 func IsInsecurePortUnbound(params []string) bool {
54         return hasSingleFlagArgument("--insecure-port=", strconv.Itoa(portDisabled), params)
55 }
56
57 // IsProfilingDisabled validates there is single "--profiling" flag and it is set to "false".
58 func IsProfilingDisabled(params []string) bool {
59         return hasSingleFlagArgument("--profiling=", "false", params)
60 }
61
62 // IsRepairMalformedUpdatesDisabled validates there is single "--repair-malformed-updates" flag and it is set to "false".
63 func IsRepairMalformedUpdatesDisabled(params []string) bool {
64         return hasSingleFlagArgument("--repair-malformed-updates=", "false", params)
65 }
66
67 // IsServiceAccountLookupEnabled validates there is single "--service-account-lookup" flag and it is set to "true".
68 func IsServiceAccountLookupEnabled(params []string) bool {
69         return hasSingleFlagArgument("--service-account-lookup=", "true", params)
70 }
71
72 // IsStrongCryptoCipherInUse validates there is single "--tls-cipher-suites=" flag and it is set to strong crypto ciphers.
73 func IsStrongCryptoCipherInUse(params []string) bool {
74         return hasSingleFlagArgument("--tls-cipher-suites=", strongCryptoCiphers, params)
75 }
76
77 // hasSingleFlagArgument checks whether selected flag was used once and has requested argument.
78 func hasSingleFlagArgument(flag string, argument string, params []string) bool {
79         found := filterFlags(params, flag)
80         if len(found) != 1 {
81                 return false
82         }
83
84         _, value := splitKV(found[0], "=")
85         if value != argument {
86                 return false
87         }
88         return true
89 }
90
91 // filterFlags returns all occurrences of selected flag.
92 func filterFlags(strs []string, flag string) []string {
93         var filtered []string
94         for _, str := range strs {
95                 if strings.HasPrefix(str, flag) {
96                         filtered = append(filtered, str)
97                 }
98         }
99         return filtered
100 }
101
102 // splitKV splits key and value (after first occurrence of separator).
103 func splitKV(s, sep string) (string, string) {
104         ret := strings.SplitN(s, sep, 2)
105         return ret[0], ret[1]
106 }
107
108 // IsKubeletHTTPSAbsentOrEnabled validates there is single "--kubelet-https" flag and it is set to "true".
109 func IsKubeletHTTPSAbsentOrEnabled(params []string) bool {
110         return isFlagAbsent("--kubelet-https=", params) ||
111                 hasSingleFlagArgument("--kubelet-https=", "true", params)
112 }
113
114 // IsInsecureBindAddressAbsentOrLoopback validates there is no insecure bind address or it is loopback address.
115 func IsInsecureBindAddressAbsentOrLoopback(params []string) bool {
116         return isFlagAbsent("--insecure-bind-address=", params) ||
117                 hasSingleFlagArgument("--insecure-bind-address=", "127.0.0.1", params)
118 }
119
120 // IsSecurePortAbsentOrValid validates there is no secure port set explicitly or it has legal value.
121 func IsSecurePortAbsentOrValid(params []string) bool {
122         return isFlagAbsent("--secure-port=", params) ||
123                 hasFlagValidPort("--secure-port=", params)
124 }
125
126 // hasFlagValidPort checks whether selected flag has valid port as an argument in given command.
127 func hasFlagValidPort(flag string, params []string) bool {
128         found := filterFlags(params, flag)
129         if len(found) != 1 {
130                 return false
131         }
132
133         _, value := splitKV(found[0], "=")
134         port, err := strconv.Atoi(value) // what about empty parameter?
135         if err != nil {
136                 return false
137         }
138         if port < portLowest || port > portHighest {
139                 return false
140         }
141         return true
142 }
143
144 // IsAlwaysAdmitAdmissionControlPluginExcluded validates AlwaysAdmit is excluded from admission control plugins.
145 func IsAlwaysAdmitAdmissionControlPluginExcluded(params []string) bool {
146         if isSingleFlagPresent("--enable-admission-plugins=", params) {
147                 return !hasFlagArgumentIncluded("--enable-admission-plugins=", "AlwaysAdmit", params)
148         }
149         if isSingleFlagPresent("--admission-control=", params) {
150                 return !hasFlagArgumentIncluded("--admission-control=", "AlwaysAdmit", params)
151         }
152         return false
153 }
154
155 // IsAlwaysPullImagesAdmissionControlPluginIncluded validates AlwaysPullImages is included in admission control plugins.
156 func IsAlwaysPullImagesAdmissionControlPluginIncluded(params []string) bool {
157         if isSingleFlagPresent("--enable-admission-plugins=", params) {
158                 return hasFlagArgumentIncluded("--enable-admission-plugins=", "AlwaysPullImages", params)
159         }
160         if isSingleFlagPresent("--admission-control=", params) {
161                 return hasFlagArgumentIncluded("--admission-control=", "AlwaysPullImages", params)
162         }
163         return false
164 }
165
166 // IsDenyEscalatingExecAdmissionControlPluginIncluded validates DenyEscalatingExec is included in admission control plugins.
167 func IsDenyEscalatingExecAdmissionControlPluginIncluded(params []string) bool {
168         if isSingleFlagPresent("--enable-admission-plugins=", params) {
169                 return hasFlagArgumentIncluded("--enable-admission-plugins=", "DenyEscalatingExec", params)
170         }
171         if isSingleFlagPresent("--admission-control=", params) {
172                 return hasFlagArgumentIncluded("--admission-control=", "DenyEscalatingExec", params)
173         }
174         return false
175 }
176
177 // IsSecurityContextDenyAdmissionControlPluginIncluded validates SecurityContextDeny is included in admission control plugins.
178 func IsSecurityContextDenyAdmissionControlPluginIncluded(params []string) bool {
179         if isSingleFlagPresent("--enable-admission-plugins=", params) {
180                 return hasFlagArgumentIncluded("--enable-admission-plugins=", "SecurityContextDeny", params)
181         }
182         if isSingleFlagPresent("--admission-control=", params) {
183                 return hasFlagArgumentIncluded("--admission-control=", "SecurityContextDeny", params)
184         }
185         return false
186 }
187
188 // IsPodSecurityPolicyAdmissionControlPluginIncluded validates PodSecurityPolicy is included in admission control plugins.
189 func IsPodSecurityPolicyAdmissionControlPluginIncluded(params []string) bool {
190         if isSingleFlagPresent("--enable-admission-plugins=", params) {
191                 return hasFlagArgumentIncluded("--enable-admission-plugins=", "PodSecurityPolicy", params)
192         }
193         if isSingleFlagPresent("--admission-control=", params) {
194                 return hasFlagArgumentIncluded("--admission-control=", "PodSecurityPolicy", params)
195         }
196         return false
197 }
198
199 // IsServiceAccountAdmissionControlPluginIncluded validates ServiceAccount is included in admission control plugins.
200 func IsServiceAccountAdmissionControlPluginIncluded(params []string) bool {
201         if isSingleFlagPresent("--enable-admission-plugins=", params) {
202                 return hasFlagArgumentIncluded("--enable-admission-plugins=", "ServiceAccount", params)
203         }
204         if isSingleFlagPresent("--admission-control=", params) {
205                 return hasFlagArgumentIncluded("--admission-control=", "ServiceAccount", params)
206         }
207         return false
208 }
209
210 // IsNodeRestrictionAdmissionControlPluginIncluded validates NodeRestriction is included in admission control plugins.
211 func IsNodeRestrictionAdmissionControlPluginIncluded(params []string) bool {
212         if isSingleFlagPresent("--enable-admission-plugins=", params) {
213                 return hasFlagArgumentIncluded("--enable-admission-plugins=", "NodeRestriction", params)
214         }
215         if isSingleFlagPresent("--admission-control=", params) {
216                 return hasFlagArgumentIncluded("--admission-control=", "NodeRestriction", params)
217         }
218         return false
219 }
220
221 // IsEventRateLimitAdmissionControlPluginIncluded validates EventRateLimit is included in admission control plugins.
222 func IsEventRateLimitAdmissionControlPluginIncluded(params []string) bool {
223         if isSingleFlagPresent("--enable-admission-plugins=", params) {
224                 return hasFlagArgumentIncluded("--enable-admission-plugins=", "EventRateLimit", params)
225         }
226         if isSingleFlagPresent("--admission-control=", params) {
227                 return hasFlagArgumentIncluded("--admission-control=", "EventRateLimit", params)
228         }
229         return false
230 }
231
232 // IsNamespaceLifecycleAdmissionControlPluginNotExcluded validates NamespaceLifecycle is excluded from admission control plugins.
233 func IsNamespaceLifecycleAdmissionControlPluginNotExcluded(params []string) bool {
234         if isSingleFlagPresent("--disable-admission-plugins=", params) {
235                 return !hasFlagArgumentIncluded("--disable-admission-plugins=", "NamespaceLifecycle", params)
236         }
237         return true
238 }
239
240 // isSingleFlagPresent checks presence of selected flag and whether it was used once.
241 func isSingleFlagPresent(flag string, params []string) bool {
242         found := filterFlags(params, flag)
243         if len(found) != 1 {
244                 return false
245         }
246         return true
247 }
248
249 // hasFlagArgumentIncluded checks whether selected flag includes requested argument.
250 func hasFlagArgumentIncluded(flag string, argument string, params []string) bool {
251         found := filterFlags(params, flag)
252         if len(found) != 1 {
253                 return false
254         }
255
256         _, values := splitKV(found[0], "=")
257         for _, v := range strings.Split(values, ",") {
258                 if v == argument {
259                         return true
260                 }
261         }
262         return false
263 }
264
265 // IsAlwaysAllowAuthorizationModeExcluded validates AlwaysAllow is excluded from authorization modes.
266 func IsAlwaysAllowAuthorizationModeExcluded(params []string) bool {
267         return isSingleFlagPresent("--authorization-mode=", params) &&
268                 !hasFlagArgumentIncluded("--authorization-mode=", "AlwaysAllow", params)
269 }
270
271 // IsNodeAuthorizationModeIncluded validates Node is included in authorization modes.
272 func IsNodeAuthorizationModeIncluded(params []string) bool {
273         return hasFlagArgumentIncluded("--authorization-mode=", "Node", params)
274 }
275
276 // IsAuditLogPathSet validates there is single "--audit-log-path" flag and has non-empty argument.
277 func IsAuditLogPathSet(params []string) bool {
278         return hasSingleFlagNonemptyArgument("--audit-log-path=", params)
279 }
280
281 // IsKubeletCertificateAuthoritySet validates there is single "--kubelet-certificate-authority" flag and has non-empty argument.
282 func IsKubeletCertificateAuthoritySet(params []string) bool {
283         return hasSingleFlagNonemptyArgument("--kubelet-certificate-authority", params)
284 }
285
286 // IsClientCertificateAuthoritySet validates there is single "--client-ca-file" flag and has non-empty argument.
287 func IsClientCertificateAuthoritySet(params []string) bool {
288         return hasSingleFlagNonemptyArgument("--client-ca-file", params)
289 }
290
291 // IsEtcdCertificateAuthoritySet validates there is single "--etcd-cafile" flag and has non-empty argument.
292 func IsEtcdCertificateAuthoritySet(params []string) bool {
293         return hasSingleFlagNonemptyArgument("--etcd-cafile", params)
294 }
295
296 // IsServiceAccountKeySet validates there is single "--service-account-key-file" flag and has non-empty argument.
297 func IsServiceAccountKeySet(params []string) bool {
298         return hasSingleFlagNonemptyArgument("--service-account-key-file", params)
299 }
300
301 // IsKubeletClientCertificateAndKeySet validates there are single "--kubelet-client-certificate" and "--kubelet-client-key" flags and have non-empty arguments.
302 func IsKubeletClientCertificateAndKeySet(params []string) bool {
303         return hasSingleFlagNonemptyArgument("--kubelet-client-certificate", params) &&
304                 hasSingleFlagNonemptyArgument("--kubelet-client-key", params)
305 }
306
307 // IsEtcdCertificateAndKeySet validates there are single "--etcd-certfile" and "--etcd-keyfile" flags and have non-empty arguments.
308 func IsEtcdCertificateAndKeySet(params []string) bool {
309         return hasSingleFlagNonemptyArgument("--etcd-certfile", params) &&
310                 hasSingleFlagNonemptyArgument("--etcd-keyfile", params)
311 }
312
313 // IsTLSCertificateAndKeySet validates there are single "--tls-cert-file" and "--tls-private-key-file" flags and have non-empty arguments.
314 func IsTLSCertificateAndKeySet(params []string) bool {
315         return hasSingleFlagNonemptyArgument("--tls-cert-file", params) &&
316                 hasSingleFlagNonemptyArgument("--tls-private-key-file", params)
317 }
318
319 // hasSingleFlagNonemptyArgument checks whether selected flag was used once and has non-empty argument.
320 func hasSingleFlagNonemptyArgument(flag string, params []string) bool {
321         found := filterFlags(params, flag)
322         if len(found) != 1 {
323                 return false
324         }
325
326         _, value := splitKV(found[0], "=")
327         if value == "" {
328                 return false
329         }
330         return true
331 }
332
333 // IsAuditLogMaxAgeValid validates audit log age is set and it has recommended value.
334 func IsAuditLogMaxAgeValid(params []string) bool {
335         return hasSingleFlagRecommendedNumericArgument("--audit-log-maxage", auditLogAge, params)
336 }
337
338 // IsAuditLogMaxBackupValid validates audit log age is set and it has recommended value.
339 func IsAuditLogMaxBackupValid(params []string) bool {
340         return hasSingleFlagRecommendedNumericArgument("--audit-log-maxbackup", auditLogBackups, params)
341 }
342
343 // IsAuditLogMaxSizeValid validates audit log age is set and it has recommended value.
344 func IsAuditLogMaxSizeValid(params []string) bool {
345         return hasSingleFlagRecommendedNumericArgument("--audit-log-maxsize", auditLogSize, params)
346 }
347
348 // hasSingleFlagRecommendedNumericArgument checks whether selected flag was used once and has
349 // an argument that is greater or equal than the recommended value for given command.
350 func hasSingleFlagRecommendedNumericArgument(flag string, recommendation int, params []string) bool {
351         found := filterFlags(params, flag)
352         if len(found) != 1 {
353                 return false
354         }
355
356         _, value := splitKV(found[0], "=")
357         arg, err := strconv.Atoi(value) // what about empty parameter?
358         if err != nil {
359                 return false
360         }
361         if arg < recommendation {
362                 return false
363         }
364         return true
365 }