aboutsummaryrefslogtreecommitdiffstats
path: root/test/security/k8s/src/check/validators/master/api/api.go
blob: 1ca920e1a22df425dee767b093dd8a81d4cf2e5a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
package api

import (
	"strconv"
	"strings"
)

const (
	portDisabled = 0
	portLowest   = 1
	portHighest  = 65536

	auditLogAge     = 30
	auditLogBackups = 10
	auditLogSize    = 100

	strongCryptoCiphers = "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM" +
		"_SHA256,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_AES_256_GCM" +
		"_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_AES_256_GCM" +
		"_SHA384,TLS_RSA_WITH_AES_256_GCM_SHA384,TLS_RSA_WITH_AES_128_GCM_SHA256"

	requestTimeout = 60
)

// IsBasicAuthFileAbsent validates there is no basic authentication file specified.
func IsBasicAuthFileAbsent(params []string) bool {
	return isFlagAbsent("--basic-auth-file=", params)
}

// IsTokenAuthFileAbsent validates there is no token based authentication file specified.
func IsTokenAuthFileAbsent(params []string) bool {
	return isFlagAbsent("--token-auth-file=", params)
}

// IsInsecureAllowAnyTokenAbsent validates insecure tokens are not accepted.
func IsInsecureAllowAnyTokenAbsent(params []string) bool {
	return isFlagAbsent("--insecure-allow-any-token", params)
}

// isFlagAbsent checks absence of selected flag in parameters.
func isFlagAbsent(flag string, params []string) bool {
	found := filterFlags(params, flag)
	if len(found) != 0 {
		return false
	}
	return true
}

// IsAnonymousAuthDisabled validates there is single "--anonymous-auth" flag and it is set to "false".
func IsAnonymousAuthDisabled(params []string) bool {
	return hasSingleFlagArgument("--anonymous-auth=", "false", params)
}

// IsInsecurePortUnbound validates there is single "--insecure-port" flag and it is set to "0" (disabled).
func IsInsecurePortUnbound(params []string) bool {
	return hasSingleFlagArgument("--insecure-port=", strconv.Itoa(portDisabled), params)
}

// IsProfilingDisabled validates there is single "--profiling" flag and it is set to "false".
func IsProfilingDisabled(params []string) bool {
	return hasSingleFlagArgument("--profiling=", "false", params)
}

// IsRepairMalformedUpdatesDisabled validates there is single "--repair-malformed-updates" flag and it is set to "false".
func IsRepairMalformedUpdatesDisabled(params []string) bool {
	return hasSingleFlagArgument("--repair-malformed-updates=", "false", params)
}

// IsServiceAccountLookupEnabled validates there is single "--service-account-lookup" flag and it is set to "true".
func IsServiceAccountLookupEnabled(params []string) bool {
	return hasSingleFlagArgument("--service-account-lookup=", "true", params)
}

// IsStrongCryptoCipherInUse validates there is single "--tls-cipher-suites=" flag and it is set to strong crypto ciphers.
func IsStrongCryptoCipherInUse(params []string) bool {
	return hasSingleFlagArgument("--tls-cipher-suites=", strongCryptoCiphers, params)
}

// hasSingleFlagArgument checks whether selected flag was used once and has requested argument.
func hasSingleFlagArgument(flag string, argument string, params []string) bool {
	found := filterFlags(params, flag)
	if len(found) != 1 {
		return false
	}

	_, value := splitKV(found[0], "=")
	if value != argument {
		return false
	}
	return true
}

// filterFlags returns all occurrences of selected flag.
func filterFlags(strs []string, flag string) []string {
	var filtered []string
	for _, str := range strs {
		if strings.HasPrefix(str, flag) {
			filtered = append(filtered, str)
		}
	}
	return filtered
}

// splitKV splits key and value (after first occurrence of separator).
func splitKV(s, sep string) (string, string) {
	ret := strings.SplitN(s, sep, 2)
	return ret[0], ret[1]
}

// IsKubeletHTTPSAbsentOrEnabled validates there is single "--kubelet-https" flag and it is set to "true".
func IsKubeletHTTPSAbsentOrEnabled(params []string) bool {
	return isFlagAbsent("--kubelet-https=", params) ||
		hasSingleFlagArgument("--kubelet-https=", "true", params)
}

// IsInsecureBindAddressAbsentOrLoopback validates there is no insecure bind address or it is loopback address.
func IsInsecureBindAddressAbsentOrLoopback(params []string) bool {
	return isFlagAbsent("--insecure-bind-address=", params) ||
		hasSingleFlagArgument("--insecure-bind-address=", "127.0.0.1", params)
}

// IsSecurePortAbsentOrValid validates there is no secure port set explicitly or it has legal value.
func IsSecurePortAbsentOrValid(params []string) bool {
	return isFlagAbsent("--secure-port=", params) ||
		hasFlagValidPort("--secure-port=", params)
}

// hasFlagValidPort checks whether selected flag has valid port as an argument in given command.
func hasFlagValidPort(flag string, params []string) bool {
	found := filterFlags(params, flag)
	if len(found) != 1 {
		return false
	}

	_, value := splitKV(found[0], "=")
	port, err := strconv.Atoi(value) // what about empty parameter?
	if err != nil {
		return false
	}
	if port < portLowest || port > portHighest {
		return false
	}
	return true
}

// IsAlwaysAdmitAdmissionControlPluginExcluded validates AlwaysAdmit is excluded from admission control plugins.
func IsAlwaysAdmitAdmissionControlPluginExcluded(params []string) bool {
	if isSingleFlagPresent("--enable-admission-plugins=", params) {
		return !hasFlagArgumentIncluded("--enable-admission-plugins=", "AlwaysAdmit", params)
	}
	if isSingleFlagPresent("--admission-control=", params) {
		return !hasFlagArgumentIncluded("--admission-control=", "AlwaysAdmit", params)
	}
	return false
}

// IsAlwaysPullImagesAdmissionControlPluginIncluded validates AlwaysPullImages is included in admission control plugins.
func IsAlwaysPullImagesAdmissionControlPluginIncluded(params []string) bool {
	if isSingleFlagPresent("--enable-admission-plugins=", params) {
		return hasFlagArgumentIncluded("--enable-admission-plugins=", "AlwaysPullImages", params)
	}
	if isSingleFlagPresent("--admission-control=", params) {
		return hasFlagArgumentIncluded("--admission-control=", "AlwaysPullImages", params)
	}
	return false
}

// IsDenyEscalatingExecAdmissionControlPluginIncluded validates DenyEscalatingExec is included in admission control plugins.
func IsDenyEscalatingExecAdmissionControlPluginIncluded(params []string) bool {
	if isSingleFlagPresent("--enable-admission-plugins=", params) {
		return hasFlagArgumentIncluded("--enable-admission-plugins=", "DenyEscalatingExec", params)
	}
	if isSingleFlagPresent("--admission-control=", params) {
		return hasFlagArgumentIncluded("--admission-control=", "DenyEscalatingExec", params)
	}
	return false
}

// IsSecurityContextDenyAdmissionControlPluginIncluded validates SecurityContextDeny is included in admission control plugins.
func IsSecurityContextDenyAdmissionControlPluginIncluded(params []string) bool {
	if isSingleFlagPresent("--enable-admission-plugins=", params) {
		return hasFlagArgumentIncluded("--enable-admission-plugins=", "SecurityContextDeny", params)
	}
	if isSingleFlagPresent("--admission-control=", params) {
		return hasFlagArgumentIncluded("--admission-control=", "SecurityContextDeny", params)
	}
	return false
}

// IsPodSecurityPolicyAdmissionControlPluginIncluded validates PodSecurityPolicy is included in admission control plugins.
func IsPodSecurityPolicyAdmissionControlPluginIncluded(params []string) bool {
	if isSingleFlagPresent("--enable-admission-plugins=", params) {
		return hasFlagArgumentIncluded("--enable-admission-plugins=", "PodSecurityPolicy", params)
	}
	if isSingleFlagPresent("--admission-control=", params) {
		return hasFlagArgumentIncluded("--admission-control=", "PodSecurityPolicy", params)
	}
	return false
}

// IsServiceAccountAdmissionControlPluginIncluded validates ServiceAccount is included in admission control plugins.
func IsServiceAccountAdmissionControlPluginIncluded(params []string) bool {
	if isSingleFlagPresent("--enable-admission-plugins=", params) {
		return hasFlagArgumentIncluded("--enable-admission-plugins=", "ServiceAccount", params)
	}
	if isSingleFlagPresent("--admission-control=", params) {
		return hasFlagArgumentIncluded("--admission-control=", "ServiceAccount", params)
	}
	return false
}

// IsNodeRestrictionAdmissionControlPluginIncluded validates NodeRestriction is included in admission control plugins.
func IsNodeRestrictionAdmissionControlPluginIncluded(params []string) bool {
	if isSingleFlagPresent("--enable-admission-plugins=", params) {
		return hasFlagArgumentIncluded("--enable-admission-plugins=", "NodeRestriction", params)
	}
	if isSingleFlagPresent("--admission-control=", params) {
		return hasFlagArgumentIncluded("--admission-control=", "NodeRestriction", params)
	}
	return false
}

// IsEventRateLimitAdmissionControlPluginIncluded validates EventRateLimit is included in admission control plugins.
func IsEventRateLimitAdmissionControlPluginIncluded(params []string) bool {
	if isSingleFlagPresent("--enable-admission-plugins=", params) {
		return hasFlagArgumentIncluded("--enable-admission-plugins=", "EventRateLimit", params)
	}
	if isSingleFlagPresent("--admission-control=", params) {
		return hasFlagArgumentIncluded("--admission-control=", "EventRateLimit", params)
	}
	return false
}

// IsNamespaceLifecycleAdmissionControlPluginNotExcluded validates NamespaceLifecycle is excluded from admission control plugins.
func IsNamespaceLifecycleAdmissionControlPluginNotExcluded(params []string) bool {
	if isSingleFlagPresent("--disable-admission-plugins=", params) {
		return !hasFlagArgumentIncluded("--disable-admission-plugins=", "NamespaceLifecycle", params)
	}
	return true
}

// isSingleFlagPresent checks presence of selected flag and whether it was used once.
func isSingleFlagPresent(flag string, params []string) bool {
	found := filterFlags(params, flag)
	if len(found) != 1 {
		return false
	}
	return true
}

// hasFlagArgumentIncluded checks whether selected flag includes requested argument.
func hasFlagArgumentIncluded(flag string, argument string, params []string) bool {
	found := filterFlags(params, flag)
	if len(found) != 1 {
		return false
	}

	_, values := splitKV(found[0], "=")
	for _, v := range strings.Split(values, ",") {
		if v == argument {
			return true
		}
	}
	return false
}

// IsAlwaysAllowAuthorizationModeExcluded validates AlwaysAllow is excluded from authorization modes.
func IsAlwaysAllowAuthorizationModeExcluded(params []string) bool {
	return isSingleFlagPresent("--authorization-mode=", params) &&
		!hasFlagArgumentIncluded("--authorization-mode=", "AlwaysAllow", params)
}

// IsNodeAuthorizationModeIncluded validates Node is included in authorization modes.
func IsNodeAuthorizationModeIncluded(params []string) bool {
	return hasFlagArgumentIncluded("--authorization-mode=", "Node", params)
}

// IsAuditLogPathSet validates there is single "--audit-log-path" flag and has non-empty argument.
func IsAuditLogPathSet(params []string) bool {
	return hasSingleFlagNonemptyArgument("--audit-log-path=", params)
}

// IsKubeletCertificateAuthoritySet validates there is single "--kubelet-certificate-authority" flag and has non-empty argument.
func IsKubeletCertificateAuthoritySet(params []string) bool {
	return hasSingleFlagNonemptyArgument("--kubelet-certificate-authority", params)
}

// IsClientCertificateAuthoritySet validates there is single "--client-ca-file" flag and has non-empty argument.
func IsClientCertificateAuthoritySet(params []string) bool {
	return hasSingleFlagNonemptyArgument("--client-ca-file", params)
}

// IsEtcdCertificateAuthoritySet validates there is single "--etcd-cafile" flag and has non-empty argument.
func IsEtcdCertificateAuthoritySet(params []string) bool {
	return hasSingleFlagNonemptyArgument("--etcd-cafile", params)
}

// IsServiceAccountKeySet validates there is single "--service-account-key-file" flag and has non-empty argument.
func IsServiceAccountKeySet(params []string) bool {
	return hasSingleFlagNonemptyArgument("--service-account-key-file", params)
}

// IsKubeletClientCertificateAndKeySet validates there are single "--kubelet-client-certificate" and "--kubelet-client-key" flags and have non-empty arguments.
func IsKubeletClientCertificateAndKeySet(params []string) bool {
	return hasSingleFlagNonemptyArgument("--kubelet-client-certificate", params) &&
		hasSingleFlagNonemptyArgument("--kubelet-client-key", params)
}

// IsEtcdCertificateAndKeySet validates there are single "--etcd-certfile" and "--etcd-keyfile" flags and have non-empty arguments.
func IsEtcdCertificateAndKeySet(params []string) bool {
	return hasSingleFlagNonemptyArgument("--etcd-certfile", params) &&
		hasSingleFlagNonemptyArgument("--etcd-keyfile", params)
}

// IsTLSCertificateAndKeySet validates there are single "--tls-cert-file" and "--tls-private-key-file" flags and have non-empty arguments.
func IsTLSCertificateAndKeySet(params []string) bool {
	return hasSingleFlagNonemptyArgument("--tls-cert-file", params) &&
		hasSingleFlagNonemptyArgument("--tls-private-key-file", params)
}

// hasSingleFlagNonemptyArgument checks whether selected flag was used once and has non-empty argument.
func hasSingleFlagNonemptyArgument(flag string, params []string) bool {
	found := filterFlags(params, flag)
	if len(found) != 1 {
		return false
	}

	_, value := splitKV(found[0], "=")
	if value == "" {
		return false
	}
	return true
}

// IsAuditLogMaxAgeValid validates audit log age is set and it has recommended value.
func IsAuditLogMaxAgeValid(params []string) bool {
	return hasSingleFlagRecommendedNumericArgument("--audit-log-maxage", auditLogAge, params)
}

// IsAuditLogMaxBackupValid validates audit log age is set and it has recommended value.
func IsAuditLogMaxBackupValid(params []string) bool {
	return hasSingleFlagRecommendedNumericArgument("--audit-log-maxbackup", auditLogBackups, params)
}

// IsAuditLogMaxSizeValid validates audit log age is set and it has recommended value.
func IsAuditLogMaxSizeValid(params []string) bool {
	return hasSingleFlagRecommendedNumericArgument("--audit-log-maxsize", auditLogSize, params)
}

// hasSingleFlagRecommendedNumericArgument checks whether selected flag was used once and has
// an argument that is greater or equal than the recommended value for given command.
func hasSingleFlagRecommendedNumericArgument(flag string, recommendation int, params []string) bool {
	found := filterFlags(params, flag)
	if len(found) != 1 {
		return false
	}

	_, value := splitKV(found[0], "=")
	arg, err := strconv.Atoi(value) // what about empty parameter?
	if err != nil {
		return false
	}
	if arg < recommendation {
		return false
	}
	return true
}

// IsRequestTimeoutValid validates request timeout is set and it has recommended value.
func IsRequestTimeoutValid(params []string) bool {
	return isFlagAbsent("--request-timeout", params) ||
		hasSingleFlagValidTimeout("--request-timeout", requestTimeout, 2*requestTimeout, params)
}

// hasSingleFlagValidTimeout checks whether selected flag has valid timeout as an argument in given command.
func hasSingleFlagValidTimeout(flag string, min int, max int, params []string) bool {
	found := filterFlags(params, flag)
	if len(found) != 1 {
		return false
	}

	_, value := splitKV(found[0], "=")
	timeout, err := strconv.Atoi(value) // what about empty parameter?
	if err != nil {
		return false
	}
	if timeout < min || timeout > max {
		return false
	}
	return true
}