k8s: Validate API server not excluded admission plugins
[integration.git] / test / security / k8s / src / check / validators / master / api_test.go
1 package master_test
2
3 import (
4         . "check/validators/master"
5
6         . "github.com/onsi/ginkgo/extensions/table"
7
8         . "github.com/onsi/ginkgo"
9         . "github.com/onsi/gomega"
10 )
11
12 var _ = Describe("Api", func() {
13         var (
14                 // kubeApiServerCISCompliant uses secure defaults or follows CIS guidelines explicitly.
15                 kubeApiServerCISCompliant = []string{
16                         "--anonymous-auth=false",
17                         "--insecure-port=0",
18                         "--profiling=false",
19                         "--repair-malformed-updates=false",
20                         "--service-account-lookup=true",
21                         "--enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount," +
22                                 "TaintNodesByCondition,Priority,DefaultTolerationSeconds,DefaultStorageClass," +
23                                 "PersistentVolumeClaimResize,MutatingAdmissionWebhook,ValidatingAdmissionWebhook," +
24                                 "ResourceQuota,AlwaysPullImages,DenyEscalatingExec,SecurityContextDeny," +
25                                 "PodSecurityPolicy,NodeRestriction,EventRateLimit",
26                 }
27
28                 // kubeApiServerCasablanca was obtained from virtual environment for testing
29                 // (introduced in Change-Id: I57f9f3caac0e8b391e9ed480f6bebba98e006882).
30                 kubeApiServerCasablanca = []string{
31                         "--storage-backend=etcd2",
32                         "--storage-media-type=application/json",
33                         "--service-cluster-ip-range=10.43.0.0/16",
34                         "--etcd-servers=https://etcd.kubernetes.rancher.internal:2379",
35                         "--insecure-bind-address=0.0.0.0",
36                         "--insecure-port=0",
37                         "--cloud-provider=rancher",
38                         "--allow-privileged=true",
39                         "--admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount," +
40                                 "PersistentVolumeLabel,DefaultStorageClass,DefaultTolerationSeconds,ResourceQuota",
41                         "--client-ca-file=/etc/kubernetes/ssl/ca.pem",
42                         "--tls-cert-file=/etc/kubernetes/ssl/cert.pem",
43                         "--tls-private-key-file=/etc/kubernetes/ssl/key.pem",
44                         "--kubelet-client-certificate=/etc/kubernetes/ssl/cert.pem",
45                         "--kubelet-client-key=/etc/kubernetes/ssl/key.pem",
46                         "--runtime-config=batch/v2alpha1",
47                         "--anonymous-auth=false",
48                         "--authentication-token-webhook-config-file=/etc/kubernetes/authconfig",
49                         "--runtime-config=authentication.k8s.io/v1beta1=true",
50                         "--external-hostname=kubernetes.kubernetes.rancher.internal",
51                         "--etcd-cafile=/etc/kubernetes/etcd/ca.pem",
52                         "--etcd-certfile=/etc/kubernetes/etcd/cert.pem",
53                         "--etcd-keyfile=/etc/kubernetes/etcd/key.pem",
54                         "--tls-cipher-suites=TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256," +
55                                 "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305," +
56                                 "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384," +
57                                 "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305",
58                 }
59
60                 // kubeApiServerCasablanca was obtained from virtual environment for testing
61                 // (introduced in Change-Id: I54ada5fade3b984dedd1715f20579e3ce901faa3).
62                 kubeApiServerDublin = []string{
63                         "--requestheader-group-headers=X-Remote-Group",
64                         "--proxy-client-cert-file=/etc/kubernetes/ssl/kube-apiserver-proxy-client.pem",
65                         "--bind-address=0.0.0.0",
66                         "--tls-cipher-suites=TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256," +
67                                 "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305," +
68                                 "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384," +
69                                 "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305",
70                         "--cloud-provider=",
71                         "--etcd-cafile=/etc/kubernetes/ssl/kube-ca.pem",
72                         "--etcd-servers=https://172.17.0.100:2379",
73                         "--tls-cert-file=/etc/kubernetes/ssl/kube-apiserver.pem",
74                         "--enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount," +
75                                 "DefaultStorageClass,DefaultTolerationSeconds,MutatingAdmissionWebhook," +
76                                 "ValidatingAdmissionWebhook,ResourceQuota,NodeRestriction,PersistentVolumeLabel",
77                         "--insecure-port=0",
78                         "--secure-port=6443",
79                         "--storage-backend=etcd3",
80                         "--kubelet-client-key=/etc/kubernetes/ssl/kube-apiserver-key.pem",
81                         "--requestheader-client-ca-file=/etc/kubernetes/ssl/kube-apiserver-requestheader-ca.pem",
82                         "--service-account-key-file=/etc/kubernetes/ssl/kube-service-account-token-key.pem",
83                         "--service-node-port-range=30000-32767",
84                         "--tls-private-key-file=/etc/kubernetes/ssl/kube-apiserver-key.pem",
85                         "--requestheader-username-headers=X-Remote-User",
86                         "--repair-malformed-updates=false",
87                         "--kubelet-client-certificate=/etc/kubernetes/ssl/kube-apiserver.pem",
88                         "--service-cluster-ip-range=10.43.0.0/16",
89                         "--advertise-address=172.17.0.100",
90                         "--profiling=false",
91                         "--requestheader-extra-headers-prefix=X-Remote-Extra-",
92                         "--etcd-certfile=/etc/kubernetes/ssl/kube-node.pem",
93                         "--anonymous-auth=false",
94                         "--etcd-keyfile=/etc/kubernetes/ssl/kube-node-key.pem",
95                         "--etcd-prefix=/registry",
96                         "--client-ca-file=/etc/kubernetes/ssl/kube-ca.pem",
97                         "--kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname",
98                         "--requestheader-allowed-names=kube-apiserver-proxy-client",
99                         "--service-account-lookup=true",
100                         "--proxy-client-key-file=/etc/kubernetes/ssl/kube-apiserver-proxy-client-key.pem",
101                         "--authorization-mode=Node,RBAC",
102                         "--allow-privileged=true",
103                 }
104         )
105
106         Describe("Boolean flags", func() {
107                 DescribeTable("Basic authentication file",
108                         func(params []string, expected bool) {
109                                 Expect(IsBasicAuthFileAbsent(params)).To(Equal(expected))
110                         },
111                         Entry("Is not absent on insecure cluster", []string{"--basic-auth-file=/path/to/file"}, false),
112                         Entry("Should be absent on CIS-compliant cluster", kubeApiServerCISCompliant, true),
113                         Entry("Should be absent on Casablanca cluster", kubeApiServerCasablanca, true),
114                         Entry("Should be absent on Dublin cluster", kubeApiServerDublin, true),
115                 )
116
117                 DescribeTable("Token authentication file",
118                         func(params []string, expected bool) {
119                                 Expect(IsTokenAuthFileAbsent(params)).To(Equal(expected))
120                         },
121                         Entry("Is not absent on insecure cluster", []string{"--token-auth-file=/path/to/file"}, false),
122                         Entry("Should be absent on CIS-compliant cluster", kubeApiServerCISCompliant, true),
123                         Entry("Should be absent on Casablanca cluster", kubeApiServerCasablanca, true),
124                         Entry("Should be absent on Dublin cluster", kubeApiServerDublin, true),
125                 )
126
127                 DescribeTable("Accepting any token",
128                         func(params []string, expected bool) {
129                                 Expect(IsInsecureAllowAnyTokenAbsent(params)).To(Equal(expected))
130                         },
131                         Entry("Is not absent on insecure cluster", []string{"--insecure-allow-any-token"}, false),
132                         Entry("Should be absent on CIS-compliant cluster", kubeApiServerCISCompliant, true),
133                         Entry("Should be absent on Casablanca cluster", kubeApiServerCasablanca, true),
134                         Entry("Should be absent on Dublin cluster", kubeApiServerDublin, true),
135                 )
136
137                 DescribeTable("Anonymous requests",
138                         func(params []string, expected bool) {
139                                 Expect(IsAnonymousAuthDisabled(params)).To(Equal(expected))
140                         },
141                         Entry("Is not set on insecure cluster", []string{}, false),
142                         Entry("Should be set to false on CIS-compliant cluster", kubeApiServerCISCompliant, true),
143                         Entry("Should be set to false on Casablanca cluster", kubeApiServerCasablanca, true),
144                         Entry("Should be set to false on Dublin cluster", kubeApiServerDublin, true),
145                 )
146
147                 DescribeTable("HTTPS for kubelet",
148                         func(params []string, expected bool) {
149                                 Expect(IsKubeletHTTPSAbsentOrEnabled(params)).To(Equal(expected))
150                         },
151                         Entry("Is explicitly disabled on insecure cluster", []string{"--kubelet-https=false"}, false),
152                         Entry("Should be absent or set to true on CIS-compliant cluster", kubeApiServerCISCompliant, true),
153                         Entry("Should be absent or set to true on Casablanca cluster", kubeApiServerCasablanca, true),
154                         Entry("Should be absent or set to true on Dublin cluster", kubeApiServerDublin, true),
155                 )
156
157                 DescribeTable("Bind address",
158                         func(params []string, expected bool) {
159                                 Expect(IsInsecureBindAddressAbsentOrLoopback(params)).To(Equal(expected))
160                         },
161                         Entry("Is not absent on insecure cluster", []string{"--insecure-bind-address=1.2.3.4"}, false),
162                         Entry("Is not absent nor set to loopback on Casablanca cluster", kubeApiServerCasablanca, false),
163                         Entry("Should be absent or set to loopback on CIS-compliant cluster", kubeApiServerCISCompliant, true),
164                         Entry("Should be absent or set to loopback on Dublin cluster", kubeApiServerDublin, true),
165                 )
166
167                 DescribeTable("Bind port",
168                         func(params []string, expected bool) {
169                                 Expect(IsInsecurePortUnbound(params)).To(Equal(expected))
170                         },
171                         Entry("Is not set on insecure cluster", []string{}, false),
172                         Entry("Is explicitly enabled on insecure cluster", []string{"--insecure-port=1234"}, false),
173                         Entry("Should be set to 0 on CIS-compliant cluster", kubeApiServerCISCompliant, true),
174                         Entry("Should be set to 0 on Casablanca cluster", kubeApiServerCasablanca, true),
175                         Entry("Should be set to 0 on Dublin cluster", kubeApiServerDublin, true),
176                 )
177
178                 DescribeTable("Secure bind port",
179                         func(params []string, expected bool) {
180                                 Expect(IsSecurePortAbsentOrValid(params)).To(Equal(expected))
181                         },
182                         Entry("Is explicitly disabled on insecure cluster", []string{"--secure-port=0"}, false),
183                         Entry("Should be absent or set to valid port on CIS-compliant cluster", kubeApiServerCISCompliant, true),
184                         Entry("Should be absent or set to valid port on Casablanca cluster", kubeApiServerCasablanca, true),
185                         Entry("Should be absent or set to valid port on Dublin cluster", kubeApiServerDublin, true),
186                 )
187
188                 DescribeTable("Profiling",
189                         func(params []string, expected bool) {
190                                 Expect(IsProfilingDisabled(params)).To(Equal(expected))
191                         },
192                         Entry("Is not set on insecure cluster", []string{}, false),
193                         Entry("Is explicitly enabled on insecure cluster", []string{"--profiling=true"}, false),
194                         Entry("Is not set on Casablanca cluster", kubeApiServerCasablanca, false),
195                         Entry("Should be set to false on CIS-compliant cluster", kubeApiServerCISCompliant, true),
196                         Entry("Should be set to false on Dublin cluster", kubeApiServerDublin, true),
197                 )
198
199                 DescribeTable("Repairing malformed updates",
200                         func(params []string, expected bool) {
201                                 Expect(IsRepairMalformedUpdatesDisabled(params)).To(Equal(expected))
202                         },
203                         Entry("Is not set on insecure cluster", []string{}, false),
204                         Entry("Is explicitly enabled on insecure cluster", []string{"--repair-malformed-updates=true"}, false),
205                         Entry("Is not set on Casablanca cluster", kubeApiServerCasablanca, false),
206                         Entry("Should be set to false on CIS-compliant cluster", kubeApiServerCISCompliant, true),
207                         Entry("Should be set to false on Dublin cluster", kubeApiServerDublin, true),
208                 )
209
210                 DescribeTable("Service account lookup",
211                         func(params []string, expected bool) {
212                                 Expect(IsServiceAccountLookupEnabled(params)).To(Equal(expected))
213                         },
214                         Entry("Is not set on insecure cluster", []string{}, false),
215                         Entry("Is explicitly disabled on insecure cluster", []string{"--service-account-lookup=false"}, false),
216                         Entry("Is not set on Casablanca cluster", kubeApiServerCasablanca, false),
217                         Entry("Should be set to true on CIS-compliant cluster", kubeApiServerCISCompliant, true),
218                         Entry("Should be set to true on Dublin cluster", kubeApiServerDublin, true),
219                 )
220
221                 DescribeTable("AlwaysAdmit admission control plugin",
222                         func(params []string, expected bool) {
223                                 Expect(IsAlwaysAdmitAdmissionControlPluginExcluded(params)).To(Equal(expected))
224                         },
225                         Entry("Is not absent on insecure cluster", []string{"--enable-admission-plugins=Foo,Bar,AlwaysAdmit,Baz,Quuz"}, false),
226                         Entry("Is not absent on insecure deprecated cluster", []string{"--admission-control=Foo,Bar,AlwaysAdmit,Baz,Quuz"}, false),
227                         Entry("Should be absent on CIS-compliant cluster", kubeApiServerCISCompliant, true),
228                         Entry("Should be absent on Casablanca cluster", kubeApiServerCasablanca, true),
229                         Entry("Should be absent on Dublin cluster", kubeApiServerDublin, true),
230                 )
231
232                 DescribeTable("AlwaysPullImages admission control plugin",
233                         func(params []string, expected bool) {
234                                 Expect(IsAlwaysPullImagesAdmissionControlPluginIncluded(params)).To(Equal(expected))
235                         },
236                         Entry("Is not present on insecure cluster", []string{"--enable-admission-plugins=Foo,Bar"}, false),
237                         Entry("Is not present on insecure deprecated cluster", []string{"--admission-control=Foo,Bar"}, false),
238                         Entry("Is not present on Casablanca cluster", kubeApiServerCasablanca, false),
239                         Entry("Is not present on Dublin cluster", kubeApiServerDublin, false),
240                         Entry("Should be present on CIS-compliant cluster", kubeApiServerCISCompliant, true),
241                 )
242
243                 DescribeTable("DenyEscalatingExec admission control plugin",
244                         func(params []string, expected bool) {
245                                 Expect(IsDenyEscalatingExecAdmissionControlPluginIncluded(params)).To(Equal(expected))
246                         },
247                         Entry("Is not present on insecure cluster", []string{"--enable-admission-plugins=Foo,Bar"}, false),
248                         Entry("Is not present on insecure deprecated cluster", []string{"--admission-control=Foo,Bar"}, false),
249                         Entry("Is not present on Casablanca cluster", kubeApiServerCasablanca, false),
250                         Entry("Is not present on Dublin cluster", kubeApiServerDublin, false),
251                         Entry("Should be present on CIS-compliant cluster", kubeApiServerCISCompliant, true),
252                 )
253
254                 DescribeTable("SecurityContextDeny admission control plugin",
255                         func(params []string, expected bool) {
256                                 Expect(IsSecurityContextDenyAdmissionControlPluginIncluded(params)).To(Equal(expected))
257                         },
258                         Entry("Is not present on insecure cluster", []string{"--enable-admission-plugins=Foo,Bar"}, false),
259                         Entry("Is not present on insecure deprecated cluster", []string{"--admission-control=Foo,Bar"}, false),
260                         Entry("Is not present on Casablanca cluster", kubeApiServerCasablanca, false),
261                         Entry("Is not present on Dublin cluster", kubeApiServerDublin, false),
262                         Entry("Should be present on CIS-compliant cluster", kubeApiServerCISCompliant, true),
263                 )
264
265                 DescribeTable("PodSecurityPolicy admission control plugin",
266                         func(params []string, expected bool) {
267                                 Expect(IsPodSecurityPolicyAdmissionControlPluginIncluded(params)).To(Equal(expected))
268                         },
269                         Entry("Is not present on insecure cluster", []string{"--enable-admission-plugins=Foo,Bar"}, false),
270                         Entry("Is not present on insecure deprecated cluster", []string{"--admission-control=Foo,Bar"}, false),
271                         Entry("Is not present on Casablanca cluster", kubeApiServerCasablanca, false),
272                         Entry("Is not present on Dublin cluster", kubeApiServerDublin, false),
273                         Entry("Should be present on CIS-compliant cluster", kubeApiServerCISCompliant, true),
274                 )
275
276                 DescribeTable("ServiceAccount admission control plugin",
277                         func(params []string, expected bool) {
278                                 Expect(IsServiceAccountAdmissionControlPluginIncluded(params)).To(Equal(expected))
279                         },
280                         Entry("Is not present on insecure cluster", []string{"--enable-admission-plugins=Foo,Bar"}, false),
281                         Entry("Is not present on insecure deprecated cluster", []string{"--admission-control=Foo,Bar"}, false),
282                         Entry("Should be present on CIS-compliant cluster", kubeApiServerCISCompliant, true),
283                         Entry("Should be present on Casablanca cluster", kubeApiServerCasablanca, true),
284                         Entry("Should be present on Dublin cluster", kubeApiServerDublin, true),
285                 )
286
287                 DescribeTable("NodeRestriction admission control plugin",
288                         func(params []string, expected bool) {
289                                 Expect(IsNodeRestrictionAdmissionControlPluginIncluded(params)).To(Equal(expected))
290                         },
291                         Entry("Is not present on insecure cluster", []string{"--enable-admission-plugins=Foo,Bar"}, false),
292                         Entry("Is not present on insecure deprecated cluster", []string{"--admission-control=Foo,Bar"}, false),
293                         Entry("Is not present on Casablanca cluster", kubeApiServerCasablanca, false),
294                         Entry("Should be present on CIS-compliant cluster", kubeApiServerCISCompliant, true),
295                         Entry("Should be present on Dublin cluster", kubeApiServerDublin, true),
296                 )
297
298                 DescribeTable("EventRateLimit admission control plugin",
299                         func(params []string, expected bool) {
300                                 Expect(IsEventRateLimitAdmissionControlPluginIncluded(params)).To(Equal(expected))
301                         },
302                         Entry("Is not present on insecure cluster", []string{"--enable-admission-plugins=Foo,Bar"}, false),
303                         Entry("Is not present on insecure deprecated cluster", []string{"--admission-control=Foo,Bar"}, false),
304                         Entry("Is not present on Casablanca cluster", kubeApiServerCasablanca, false),
305                         Entry("Is not present on Dublin cluster", kubeApiServerDublin, false),
306                         Entry("Should be present on CIS-compliant cluster", kubeApiServerCISCompliant, true),
307                 )
308
309                 DescribeTable("NamespaceLifecycle admission control plugin",
310                         func(params []string, expected bool) {
311                                 Expect(IsNamespaceLifecycleAdmissionControlPluginNotExcluded(params)).To(Equal(expected))
312                         },
313                         Entry("Is explicitly disabled on insecure cluster", []string{"--disable-admission-plugins=Foo,Bar,NamespaceLifecycle,Baz,Quuz"}, false),
314                         Entry("Should not be disabled on CIS-compliant cluster", kubeApiServerCISCompliant, true),
315                         Entry("Should not be disabled on Casablanca cluster", kubeApiServerCasablanca, true),
316                         Entry("Should not be disabled on Dublin cluster", kubeApiServerDublin, true),
317                 )
318         })
319 })