Browse code

VKE patch for kubernetes

Change-Id: I27eb6182535524337d6ff6855e334a30d9c84aaa
Reviewed-on: http://photon-jenkins.eng.vmware.com:8082/5382
Tested-by: gerrit-photon <photon-checkins@vmware.com>
Reviewed-by: gerrit-photon <photon-checkins@vmware.com>

Bo Gan authored on 2018/07/21 03:52:17
Showing 4 changed files
... ...
@@ -1,7 +1,7 @@
1
-From 0f361d3f8a3f313f790e0a819aad8006f5393444 Mon Sep 17 00:00:00 2001
1
+From 328d693b17ae4910e8af5ef63a3a7c2414434f7a Mon Sep 17 00:00:00 2001
2 2
 From: Bo Gan <ganb@vmware.com>
3 3
 Date: Sun, 10 Jun 2018 02:16:47 -0700
4
-Subject: [PATCH] Cascade Kubernetes patches for v1.9.6 (3a784cd)
4
+Subject: [PATCH] Cascade Kubernetes patches for v1.9.6 (1f4aedb)
5 5
 
6 6
 ---
7 7
  api/swagger-spec/apps_v1alpha1.json                |  21 +
... ...
@@ -26,14 +26,14 @@ Subject: [PATCH] Cascade Kubernetes patches for v1.9.6 (3a784cd)
26 26
  pkg/cloudprovider/providers/cascade/OWNERS         |   3 +
27 27
  pkg/cloudprovider/providers/cascade/apitypes.go    | 229 ++++++
28 28
  pkg/cloudprovider/providers/cascade/auth.go        | 145 ++++
29
- pkg/cloudprovider/providers/cascade/cascade.go     | 218 ++++++
29
+ pkg/cloudprovider/providers/cascade/cascade.go     | 218 +++++
30 30
  .../providers/cascade/cascade_disks.go             | 225 ++++++
31 31
  .../providers/cascade/cascade_instances.go         |  91 +++
32 32
  .../providers/cascade/cascade_instances_test.go    |  43 +
33 33
  .../providers/cascade/cascade_loadbalancer.go      | 284 +++++++
34
- pkg/cloudprovider/providers/cascade/client.go      | 394 ++++++++++
34
+ pkg/cloudprovider/providers/cascade/client.go      | 399 ++++++++++
35 35
  pkg/cloudprovider/providers/cascade/oidcclient.go  | 297 +++++++
36
- pkg/cloudprovider/providers/cascade/restclient.go  | 262 +++++++
36
+ pkg/cloudprovider/providers/cascade/restclient.go  | 262 ++++++
37 37
  pkg/cloudprovider/providers/cascade/tests_owed     |   5 +
38 38
  pkg/cloudprovider/providers/cascade/utils.go       |  25 +
39 39
  pkg/cloudprovider/providers/providers.go           |   1 +
... ...
@@ -42,15 +42,15 @@ Subject: [PATCH] Cascade Kubernetes patches for v1.9.6 (3a784cd)
42 42
  pkg/volume/cascade_disk/BUILD                      |  43 +
43 43
  pkg/volume/cascade_disk/OWNERS                     |   2 +
44 44
  pkg/volume/cascade_disk/attacher.go                | 265 +++++++
45
- pkg/volume/cascade_disk/cascade_disk.go            | 391 ++++++++++
45
+ pkg/volume/cascade_disk/cascade_disk.go            | 391 +++++++++
46 46
  pkg/volume/cascade_disk/cascade_util.go            | 152 ++++
47 47
  .../admission/persistentvolume/label/admission.go  |  54 ++
48 48
  plugin/pkg/admission/vke/BUILD                     |  60 ++
49
- plugin/pkg/admission/vke/admission.go              | 506 ++++++++++++
50
- plugin/pkg/admission/vke/admission_test.go         | 865 +++++++++++++++++++++
49
+ plugin/pkg/admission/vke/admission.go              | 555 +++++++++++++
50
+ plugin/pkg/admission/vke/admission_test.go         | 882 +++++++++++++++++++++
51 51
  staging/src/k8s.io/api/core/v1/generated.pb.go     | 310 +++++++-
52 52
  staging/src/k8s.io/api/core/v1/types.go            |  26 +-
53
- 46 files changed, 5182 insertions(+), 29 deletions(-)
53
+ 46 files changed, 5253 insertions(+), 29 deletions(-)
54 54
  create mode 100644 pkg/cloudprovider/providers/cascade/BUILD
55 55
  create mode 100644 pkg/cloudprovider/providers/cascade/OWNERS
56 56
  create mode 100644 pkg/cloudprovider/providers/cascade/apitypes.go
... ...
@@ -1716,7 +1716,7 @@ index 0000000..bec5491
1716 1716
 +}
1717 1717
 diff --git a/pkg/cloudprovider/providers/cascade/cascade_loadbalancer.go b/pkg/cloudprovider/providers/cascade/cascade_loadbalancer.go
1718 1718
 new file mode 100644
1719
-index 0000000..e28282f
1719
+index 0000000..fac37e5
1720 1720
 --- /dev/null
1721 1721
 +++ b/pkg/cloudprovider/providers/cascade/cascade_loadbalancer.go
1722 1722
 @@ -0,0 +1,284 @@
... ...
@@ -1880,7 +1880,7 @@ index 0000000..e28282f
1880 1880
 +	loadBalancerName := cloudprovider.GetLoadBalancerName(k8sService)
1881 1881
 +	logger.Infof("Load balancer name: %s", loadBalancerName)
1882 1882
 +
1883
-+	task, err := cc.apiClient.DeleteLoadBalancer(StringPtr(loadBalancerName))
1883
++	task, err := cc.apiClient.DeleteLoadBalancer(StringPtr(loadBalancerName), k8sService.Name)
1884 1884
 +	if err != nil {
1885 1885
 +		logger.Errorf("Failed to delete load balancer. Error: [%v]", err)
1886 1886
 +		// If we get a NotFound error, we assume that the load balancer is already deleted. So we don't return an error
... ...
@@ -2007,10 +2007,10 @@ index 0000000..e28282f
2007 2007
 \ No newline at end of file
2008 2008
 diff --git a/pkg/cloudprovider/providers/cascade/client.go b/pkg/cloudprovider/providers/cascade/client.go
2009 2009
 new file mode 100644
2010
-index 0000000..72049e0
2010
+index 0000000..e4494e4
2011 2011
 --- /dev/null
2012 2012
 +++ b/pkg/cloudprovider/providers/cascade/client.go
2013
-@@ -0,0 +1,394 @@
2013
+@@ -0,0 +1,399 @@
2014 2014
 +package cascade
2015 2015
 +
2016 2016
 +import (
... ...
@@ -2329,9 +2329,14 @@ index 0000000..72049e0
2329 2329
 +}
2330 2330
 +
2331 2331
 +// DeleteLoadBalancer deletes a load balancer by name
2332
-+func (api *Client) DeleteLoadBalancer(loadBalancerName *string) (*Task, error) {
2332
++func (api *Client) DeleteLoadBalancer(loadBalancerName *string, subDomain string) (*Task, error) {
2333 2333
 +	uri := fmt.Sprintf("%s/v1/tenants/%s/clusters/%s/loadbalancers/%s", api.cfg.endpoint, api.cfg.tenantName,
2334 2334
 +		api.cfg.clusterID, StringVal(loadBalancerName))
2335
++
2336
++	if len(subDomain) > 0 {
2337
++		uri = fmt.Sprintf(uri + "?sub-domain=%s", subDomain)
2338
++	}
2339
++
2335 2340
 +	res, err := api.restClient.Delete(uri, api.options.TokenOptions)
2336 2341
 +	if err != nil {
2337 2342
 +		return nil, err
... ...
@@ -4124,10 +4129,10 @@ index 0000000..d0bb7c7
4124 4124
 \ No newline at end of file
4125 4125
 diff --git a/plugin/pkg/admission/vke/admission.go b/plugin/pkg/admission/vke/admission.go
4126 4126
 new file mode 100644
4127
-index 0000000..6bd7592
4127
+index 0000000..c2714cb
4128 4128
 --- /dev/null
4129 4129
 +++ b/plugin/pkg/admission/vke/admission.go
4130
-@@ -0,0 +1,506 @@
4130
+@@ -0,0 +1,555 @@
4131 4131
 +package vke
4132 4132
 +
4133 4133
 +import (
... ...
@@ -4156,11 +4161,13 @@ index 0000000..6bd7592
4156 4156
 +	PluginName = "VMwareAdmissionController"
4157 4157
 +
4158 4158
 +	systemUnsecuredUser      = "system:unsecured"
4159
++	systemNodesGroup         = "system:nodes"
4160
++	systemMasterGroup        = "system:master"
4161
++	systemNodePrefix         = "system:node:"
4162
++	systemClusterPrefix      = "system:clusterID:"
4159 4163
 +	privilegedNamespace      = "vke-system"
4160 4164
 +	privilegedServiceAccount = "system:serviceaccount:" + privilegedNamespace + ":"
4161 4165
 +	reservedPrefix           = "vke"
4162
-+	kubeletGroup             = "system:nodes"
4163
-+	kubeProxyGroup           = "vke:kube-proxies"
4164 4166
 +	reservedTolerationKey    = "Dedicated"
4165 4167
 +	reservedTolerationValue  = "Master"
4166 4168
 +	masterNodePrefix         = "master"
... ...
@@ -4180,10 +4187,12 @@ index 0000000..6bd7592
4180 4180
 +	psp             *extensions.PodSecurityPolicy
4181 4181
 +	strategyFactory podsecuritypolicy.StrategyFactory
4182 4182
 +	privilegedGroup string
4183
++	clusterID       string
4183 4184
 +}
4184 4185
 +
4185 4186
 +// vmwareAdmissionControllerConfig holds config data for VMwareAdmissionController.
4186 4187
 +type vmwareAdmissionControllerConfig struct {
4188
++	ClusterID             string `yaml:"clusterID"`
4187 4189
 +	PrivilegedGroup       string `yaml:"privilegedGroup"`
4188 4190
 +	PodSecurityPolicyFile string `yaml:"podSecurityPolicyFile"`
4189 4191
 +}
... ...
@@ -4205,6 +4214,10 @@ index 0000000..6bd7592
4205 4205
 +		return validateSystemUnsecuredUser(vac, a)
4206 4206
 +	}
4207 4207
 +
4208
++	if isCertificateFromNode(a) {
4209
++		return validateNodeCertificate(vac, a)
4210
++	}
4211
++
4208 4212
 +	if isPrivilegedNamespace(a) {
4209 4213
 +		return admission.NewForbidden(a,
4210 4214
 +			fmt.Errorf("%s validation failed: cannot modify resources in namespace %s", PluginName, a.GetNamespace()))
... ...
@@ -4255,6 +4268,7 @@ index 0000000..6bd7592
4255 4255
 +		psp:             psp,
4256 4256
 +		strategyFactory: podsecuritypolicy.NewSimpleStrategyFactory(),
4257 4257
 +		privilegedGroup: config.VMwareAdmissionController.PrivilegedGroup,
4258
++		clusterID:       config.VMwareAdmissionController.ClusterID,
4258 4259
 +	}, nil
4259 4260
 +}
4260 4261
 +
... ...
@@ -4348,12 +4362,9 @@ index 0000000..6bd7592
4348 4348
 +
4349 4349
 +	// If the request comes from a user belonging to a privileged group, then we allow it. Only calls from Cascade
4350 4350
 +	// controller will belong to this privileged group.
4351
-+	// If the request comes from kubelet or kube-proxy, we allow those too. We don't want to prevent kubelet from being
4352
-+	// able to create a privileged pod or update nodes. The same way we don't want to prevent kube-proxy from creating
4353
-+	// events.
4354 4351
 +	groups := a.GetUserInfo().GetGroups()
4355 4352
 +	for _, group := range groups {
4356
-+		if group == vac.privilegedGroup || group == kubeletGroup || group == kubeProxyGroup {
4353
++		if group == vac.privilegedGroup {
4357 4354
 +			return true
4358 4355
 +		}
4359 4356
 +	}
... ...
@@ -4385,6 +4396,49 @@ index 0000000..6bd7592
4385 4385
 +	return nil
4386 4386
 +}
4387 4387
 +
4388
++func isCertificateFromNode(a admission.Attributes) bool {
4389
++	// If the request came from a user with group = systemNodesGroup, then we assume that the request comes from a node
4390
++	// which uses a certificate for authentication.
4391
++	groups := a.GetUserInfo().GetGroups()
4392
++	for _, group := range groups {
4393
++		if group == systemNodesGroup {
4394
++			return true
4395
++		}
4396
++	}
4397
++	return false
4398
++}
4399
++
4400
++func validateNodeCertificate(vac *vmwareAdmissionController, a admission.Attributes) (err error) {
4401
++	// If the groups have system:cluster:cluster-id as one of the groups, then validate that the cluster ID belongs to
4402
++	// this cluster. If not fail. This prevents cross cluster access using certificates.
4403
++	invalidClusterID := false
4404
++	masterNode := false
4405
++	groups := a.GetUserInfo().GetGroups()
4406
++	for _, group := range groups {
4407
++		if strings.HasPrefix(group, systemClusterPrefix) {
4408
++			groupParts := strings.Split(group, ":")
4409
++			if vac.clusterID != "" && groupParts[len(groupParts)-1] != vac.clusterID {
4410
++				invalidClusterID = true
4411
++			}
4412
++		}
4413
++		if group == systemMasterGroup {
4414
++			masterNode = true
4415
++		}
4416
++	}
4417
++	if invalidClusterID {
4418
++		return admission.NewForbidden(a, fmt.Errorf("%s validation failed: request is not from this cluster", PluginName))
4419
++	}
4420
++
4421
++	// If a worker node name does not have the node prefix, then fail. This is needed for the request to go through node
4422
++	// restriction admission controller. If it is not set, then a user can bypass node restriction admission controller
4423
++	// and modify the master node.
4424
++	name := a.GetUserInfo().GetName()
4425
++	if !strings.HasPrefix(name, systemNodePrefix) && !masterNode {
4426
++		return admission.NewForbidden(a, fmt.Errorf("%s validation failed: username is invalid", PluginName))
4427
++	}
4428
++	return nil
4429
++}
4430
++
4388 4431
 +func isPrivilegedNamespace(a admission.Attributes) bool {
4389 4432
 +	// If the namespace mentioned in the resource is privileged, return true. We will hit this for calls made to all
4390 4433
 +	// resources in this namespace and during delete and update operation on the namespace itself.
... ...
@@ -4636,10 +4690,10 @@ index 0000000..6bd7592
4636 4636
 +}
4637 4637
 diff --git a/plugin/pkg/admission/vke/admission_test.go b/plugin/pkg/admission/vke/admission_test.go
4638 4638
 new file mode 100644
4639
-index 0000000..7f8ec60
4639
+index 0000000..4ce4a1f
4640 4640
 --- /dev/null
4641 4641
 +++ b/plugin/pkg/admission/vke/admission_test.go
4642
-@@ -0,0 +1,865 @@
4642
+@@ -0,0 +1,882 @@
4643 4643
 +package vke
4644 4644
 +
4645 4645
 +import (
... ...
@@ -4657,10 +4711,12 @@ index 0000000..7f8ec60
4657 4657
 +
4658 4658
 +const (
4659 4659
 +	testServiceAccountsGroup = "system.test\\cascade-controller-service-accounts"
4660
++	clusterID                = "cluster-id"
4660 4661
 +	defaultConfigFileFormat  = `
4661 4662
 +vmwareAdmissionController:
4662 4663
 +  privilegedGroup: %s
4663 4664
 +  podSecurityPolicyFile: %s
4665
++  clusterID: %s
4664 4666
 +`
4665 4667
 +	pspFileName   = "/tmp/psp.yaml"
4666 4668
 +	pspConfigFile = `
... ...
@@ -4988,13 +5044,7 @@ index 0000000..7f8ec60
4988 4988
 +		"allowed: kubelet group creates pod in vke-system namespace": {
4989 4989
 +			operation:          kadmission.Create,
4990 4990
 +			pod:                newTestPodBuilder().withNamespace(privilegedNamespace).build(),
4991
-+			userInfo:           newTestUserBuilder().withGroup(kubeletGroup).build(),
4992
-+			shouldPassValidate: true,
4993
-+		},
4994
-+		"allowed: kubeProxy group creates pod in vke-system namespace": {
4995
-+			operation:          kadmission.Create,
4996
-+			pod:                newTestPodBuilder().withNamespace(privilegedNamespace).build(),
4997
-+			userInfo:           newTestUserBuilder().withGroup(kubeProxyGroup).build(),
4991
++			userInfo:           newTestUserBuilder().withGroup(systemNodesGroup).withName("system:node:worker").build(),
4998 4992
 +			shouldPassValidate: true,
4999 4993
 +		},
5000 4994
 +		"denied: regular lightwave group does not grant privileged access": {
... ...
@@ -5009,6 +5059,27 @@ index 0000000..7f8ec60
5009 5009
 +			userInfo:           newTestUserBuilder().withGroup("test1\\group1").withGroup(testServiceAccountsGroup).build(),
5010 5010
 +			shouldPassValidate: true,
5011 5011
 +		},
5012
++		"denied: cross cluster acccess": {
5013
++			operation: kadmission.Create,
5014
++			pod:       newTestPodBuilder().build(),
5015
++			userInfo: newTestUserBuilder().withName("system:node:worker").withGroup("system:clusterID:some-cluster-id").
5016
++				withGroup("system:nodes").withGroup("system:worker").build(),
5017
++			shouldPassValidate: false,
5018
++		},
5019
++		"denied: node restriction admission controller bypass": {
5020
++			operation: kadmission.Create,
5021
++			pod:       newTestPodBuilder().build(),
5022
++			userInfo: newTestUserBuilder().withGroup("system:clusterID:cluster-id").withGroup("system:nodes").
5023
++				withGroup("system:worker").build(),
5024
++			shouldPassValidate: false,
5025
++		},
5026
++		"allowed: master node to bypass node restriction admission controller": {
5027
++			operation: kadmission.Create,
5028
++			pod:       newTestPodBuilder().build(),
5029
++			userInfo: newTestUserBuilder().withName("kubernetes-master").withGroup("system:clusterID:cluster-id").
5030
++				withGroup("system:nodes").withGroup("system:master").build(),
5031
++			shouldPassValidate: true,
5032
++		},
5012 5033
 +	}
5013 5034
 +	for k, v := range tests {
5014 5035
 +		testPodValidation(k, v.operation, v.pod, v.name, v.userInfo, v.shouldPassValidate, t)
... ...
@@ -5284,7 +5355,7 @@ index 0000000..7f8ec60
5284 5284
 +func testPodValidation(testCaseName string, op kadmission.Operation, pod *kapi.Pod, name string, userInfo user.Info,
5285 5285
 +	shouldPassValidate bool, t *testing.T) {
5286 5286
 +
5287
-+	defaultConfigFile := fmt.Sprintf(defaultConfigFileFormat, testServiceAccountsGroup, pspFileName)
5287
++	defaultConfigFile := fmt.Sprintf(defaultConfigFileFormat, testServiceAccountsGroup, pspFileName, clusterID)
5288 5288
 +	configFile := strings.NewReader(defaultConfigFile)
5289 5289
 +	plugin, err := NewVMwareAdmissionController(configFile)
5290 5290
 +	if err != nil {
... ...
@@ -5310,7 +5381,7 @@ index 0000000..7f8ec60
5310 5310
 +func testResourceValidation(testCaseName string, op kadmission.Operation, resource, subresource, name, namespace string,
5311 5311
 +	userInfo user.Info, shouldPassValidate bool, t *testing.T) {
5312 5312
 +
5313
-+	defaultConfigFile := fmt.Sprintf(defaultConfigFileFormat, testServiceAccountsGroup, pspFileName)
5313
++	defaultConfigFile := fmt.Sprintf(defaultConfigFileFormat, testServiceAccountsGroup, pspFileName, clusterID)
5314 5314
 +	configFile := strings.NewReader(defaultConfigFile)
5315 5315
 +	plugin, err := NewVMwareAdmissionController(configFile)
5316 5316
 +	if err != nil {
... ...
@@ -1,7 +1,7 @@
1
-From 0dc83e12d98eb7aaa682bf6c251c89ff842e2275 Mon Sep 17 00:00:00 2001
1
+From a9402d16a92e04e25ede3816872855e9a89fb812 Mon Sep 17 00:00:00 2001
2 2
 From: Bo Gan <ganb@vmware.com>
3 3
 Date: Sun, 10 Jun 2018 02:13:51 -0700
4
-Subject: [PATCH] Cascade Kubernetes patches for v1.10.2 (3a784cd)
4
+Subject: [PATCH] Cascade Kubernetes patches for v1.10.2 (1f4aedb)
5 5
 
6 6
 ---
7 7
  api/swagger-spec/apps_v1alpha1.json                |  21 +
... ...
@@ -28,11 +28,11 @@ Subject: [PATCH] Cascade Kubernetes patches for v1.10.2 (3a784cd)
28 28
  pkg/cloudprovider/providers/cascade/cascade.go     | 214 +++++
29 29
  .../providers/cascade/cascade_disks.go             | 227 ++++++
30 30
  .../providers/cascade/cascade_instances.go         |  92 +++
31
- .../providers/cascade/cascade_instances_test.go    |  44 ++
31
+ .../providers/cascade/cascade_instances_test.go    |  44 +
32 32
  .../providers/cascade/cascade_loadbalancer.go      | 285 +++++++
33
- pkg/cloudprovider/providers/cascade/client.go      | 394 ++++++++++
33
+ pkg/cloudprovider/providers/cascade/client.go      | 399 ++++++++++
34 34
  pkg/cloudprovider/providers/cascade/oidcclient.go  | 297 +++++++
35
- pkg/cloudprovider/providers/cascade/restclient.go  | 262 +++++++
35
+ pkg/cloudprovider/providers/cascade/restclient.go  | 262 ++++++
36 36
  pkg/cloudprovider/providers/cascade/tests_owed     |   5 +
37 37
  pkg/cloudprovider/providers/cascade/utils.go       |  25 +
38 38
  pkg/cloudprovider/providers/providers.go           |   1 +
... ...
@@ -41,16 +41,16 @@ Subject: [PATCH] Cascade Kubernetes patches for v1.10.2 (3a784cd)
41 41
  pkg/security/podsecuritypolicy/util/util.go        |   3 +
42 42
  pkg/volume/cascade_disk/BUILD                      |  43 +
43 43
  pkg/volume/cascade_disk/OWNERS                     |   2 +
44
- pkg/volume/cascade_disk/attacher.go                | 264 +++++++
45
- pkg/volume/cascade_disk/cascade_disk.go            | 390 ++++++++++
44
+ pkg/volume/cascade_disk/attacher.go                | 264 ++++++
45
+ pkg/volume/cascade_disk/cascade_disk.go            | 390 +++++++++
46 46
  pkg/volume/cascade_disk/cascade_util.go            | 152 ++++
47 47
  .../admission/persistentvolume/label/admission.go  |  54 ++
48 48
  plugin/pkg/admission/vke/BUILD                     |  60 ++
49
- plugin/pkg/admission/vke/admission.go              | 505 ++++++++++++
50
- plugin/pkg/admission/vke/admission_test.go         | 865 +++++++++++++++++++++
49
+ plugin/pkg/admission/vke/admission.go              | 554 +++++++++++++
50
+ plugin/pkg/admission/vke/admission_test.go         | 882 +++++++++++++++++++++
51 51
  staging/src/k8s.io/api/core/v1/generated.pb.go     | 310 +++++++-
52 52
  staging/src/k8s.io/api/core/v1/types.go            |  24 +-
53
- 46 files changed, 5183 insertions(+), 29 deletions(-)
53
+ 46 files changed, 5254 insertions(+), 29 deletions(-)
54 54
  create mode 100644 pkg/cloudprovider/providers/cascade/BUILD
55 55
  create mode 100644 pkg/cloudprovider/providers/cascade/OWNERS
56 56
  create mode 100644 pkg/cloudprovider/providers/cascade/apitypes.go
... ...
@@ -1716,7 +1716,7 @@ index 0000000..8fb314d
1716 1716
 +}
1717 1717
 diff --git a/pkg/cloudprovider/providers/cascade/cascade_loadbalancer.go b/pkg/cloudprovider/providers/cascade/cascade_loadbalancer.go
1718 1718
 new file mode 100644
1719
-index 0000000..1038639
1719
+index 0000000..6338072
1720 1720
 --- /dev/null
1721 1721
 +++ b/pkg/cloudprovider/providers/cascade/cascade_loadbalancer.go
1722 1722
 @@ -0,0 +1,285 @@
... ...
@@ -1881,7 +1881,7 @@ index 0000000..1038639
1881 1881
 +	loadBalancerName := cloudprovider.GetLoadBalancerName(k8sService)
1882 1882
 +	logger.Infof("Load balancer name: %s", loadBalancerName)
1883 1883
 +
1884
-+	task, err := cc.apiClient.DeleteLoadBalancer(StringPtr(loadBalancerName))
1884
++	task, err := cc.apiClient.DeleteLoadBalancer(StringPtr(loadBalancerName), k8sService.Name)
1885 1885
 +	if err != nil {
1886 1886
 +		logger.Errorf("Failed to delete load balancer. Error: [%v]", err)
1887 1887
 +		// If we get a NotFound error, we assume that the load balancer is already deleted. So we don't return an error
... ...
@@ -2007,10 +2007,10 @@ index 0000000..1038639
2007 2007
 +}
2008 2008
 diff --git a/pkg/cloudprovider/providers/cascade/client.go b/pkg/cloudprovider/providers/cascade/client.go
2009 2009
 new file mode 100644
2010
-index 0000000..72049e0
2010
+index 0000000..e4494e4
2011 2011
 --- /dev/null
2012 2012
 +++ b/pkg/cloudprovider/providers/cascade/client.go
2013
-@@ -0,0 +1,394 @@
2013
+@@ -0,0 +1,399 @@
2014 2014
 +package cascade
2015 2015
 +
2016 2016
 +import (
... ...
@@ -2329,9 +2329,14 @@ index 0000000..72049e0
2329 2329
 +}
2330 2330
 +
2331 2331
 +// DeleteLoadBalancer deletes a load balancer by name
2332
-+func (api *Client) DeleteLoadBalancer(loadBalancerName *string) (*Task, error) {
2332
++func (api *Client) DeleteLoadBalancer(loadBalancerName *string, subDomain string) (*Task, error) {
2333 2333
 +	uri := fmt.Sprintf("%s/v1/tenants/%s/clusters/%s/loadbalancers/%s", api.cfg.endpoint, api.cfg.tenantName,
2334 2334
 +		api.cfg.clusterID, StringVal(loadBalancerName))
2335
++
2336
++	if len(subDomain) > 0 {
2337
++		uri = fmt.Sprintf(uri + "?sub-domain=%s", subDomain)
2338
++	}
2339
++
2335 2340
 +	res, err := api.restClient.Delete(uri, api.options.TokenOptions)
2336 2341
 +	if err != nil {
2337 2342
 +		return nil, err
... ...
@@ -4149,10 +4154,10 @@ index 0000000..2fb36c7
4149 4149
 \ No newline at end of file
4150 4150
 diff --git a/plugin/pkg/admission/vke/admission.go b/plugin/pkg/admission/vke/admission.go
4151 4151
 new file mode 100644
4152
-index 0000000..e05e7be
4152
+index 0000000..64c2d16
4153 4153
 --- /dev/null
4154 4154
 +++ b/plugin/pkg/admission/vke/admission.go
4155
-@@ -0,0 +1,505 @@
4155
+@@ -0,0 +1,554 @@
4156 4156
 +package vke
4157 4157
 +
4158 4158
 +import (
... ...
@@ -4181,11 +4186,13 @@ index 0000000..e05e7be
4181 4181
 +	PluginName = "VMwareAdmissionController"
4182 4182
 +
4183 4183
 +	systemUnsecuredUser      = "system:unsecured"
4184
++	systemNodesGroup         = "system:nodes"
4185
++	systemMasterGroup        = "system:master"
4186
++	systemNodePrefix         = "system:node:"
4187
++	systemClusterPrefix      = "system:clusterID:"
4184 4188
 +	privilegedNamespace      = "vke-system"
4185 4189
 +	privilegedServiceAccount = "system:serviceaccount:" + privilegedNamespace + ":"
4186 4190
 +	reservedPrefix           = "vke"
4187
-+	kubeletGroup             = "system:nodes"
4188
-+	kubeProxyGroup           = "vke:kube-proxies"
4189 4191
 +	reservedTolerationKey    = "Dedicated"
4190 4192
 +	reservedTolerationValue  = "Master"
4191 4193
 +	masterNodePrefix         = "master"
... ...
@@ -4205,10 +4212,12 @@ index 0000000..e05e7be
4205 4205
 +	psp             *extensions.PodSecurityPolicy
4206 4206
 +	strategyFactory podsecuritypolicy.StrategyFactory
4207 4207
 +	privilegedGroup string
4208
++	clusterID       string
4208 4209
 +}
4209 4210
 +
4210 4211
 +// vmwareAdmissionControllerConfig holds config data for VMwareAdmissionController.
4211 4212
 +type vmwareAdmissionControllerConfig struct {
4213
++	ClusterID             string `yaml:"clusterID"`
4212 4214
 +	PrivilegedGroup       string `yaml:"privilegedGroup"`
4213 4215
 +	PodSecurityPolicyFile string `yaml:"podSecurityPolicyFile"`
4214 4216
 +}
... ...
@@ -4230,6 +4239,10 @@ index 0000000..e05e7be
4230 4230
 +		return validateSystemUnsecuredUser(vac, a)
4231 4231
 +	}
4232 4232
 +
4233
++	if isCertificateFromNode(a) {
4234
++		return validateNodeCertificate(vac, a)
4235
++	}
4236
++
4233 4237
 +	if isPrivilegedNamespace(a) {
4234 4238
 +		return admission.NewForbidden(a,
4235 4239
 +			fmt.Errorf("%s validation failed: cannot modify resources in namespace %s", PluginName, a.GetNamespace()))
... ...
@@ -4280,6 +4293,7 @@ index 0000000..e05e7be
4280 4280
 +		psp:             psp,
4281 4281
 +		strategyFactory: podsecuritypolicy.NewSimpleStrategyFactory(),
4282 4282
 +		privilegedGroup: config.VMwareAdmissionController.PrivilegedGroup,
4283
++		clusterID:       config.VMwareAdmissionController.ClusterID,
4283 4284
 +	}, nil
4284 4285
 +}
4285 4286
 +
... ...
@@ -4373,12 +4387,9 @@ index 0000000..e05e7be
4373 4373
 +
4374 4374
 +	// If the request comes from a user belonging to a privileged group, then we allow it. Only calls from Cascade
4375 4375
 +	// controller will belong to this privileged group.
4376
-+	// If the request comes from kubelet or kube-proxy, we allow those too. We don't want to prevent kubelet from being
4377
-+	// able to create a privileged pod or update nodes. The same way we don't want to prevent kube-proxy from creating
4378
-+	// events.
4379 4376
 +	groups := a.GetUserInfo().GetGroups()
4380 4377
 +	for _, group := range groups {
4381
-+		if group == vac.privilegedGroup || group == kubeletGroup || group == kubeProxyGroup {
4378
++		if group == vac.privilegedGroup {
4382 4379
 +			return true
4383 4380
 +		}
4384 4381
 +	}
... ...
@@ -4410,6 +4421,49 @@ index 0000000..e05e7be
4410 4410
 +	return nil
4411 4411
 +}
4412 4412
 +
4413
++func isCertificateFromNode(a admission.Attributes) bool {
4414
++	// If the request came from a user with group = systemNodesGroup, then we assume that the request comes from a node
4415
++	// which uses a certificate for authentication.
4416
++	groups := a.GetUserInfo().GetGroups()
4417
++	for _, group := range groups {
4418
++		if group == systemNodesGroup {
4419
++			return true
4420
++		}
4421
++	}
4422
++	return false
4423
++}
4424
++
4425
++func validateNodeCertificate(vac *vmwareAdmissionController, a admission.Attributes) (err error) {
4426
++	// If the groups have system:cluster:cluster-id as one of the groups, then validate that the cluster ID belongs to
4427
++	// this cluster. If not fail. This prevents cross cluster access using certificates.
4428
++	invalidClusterID := false
4429
++	masterNode := false
4430
++	groups := a.GetUserInfo().GetGroups()
4431
++	for _, group := range groups {
4432
++		if strings.HasPrefix(group, systemClusterPrefix) {
4433
++			groupParts := strings.Split(group, ":")
4434
++			if vac.clusterID != "" && groupParts[len(groupParts)-1] != vac.clusterID {
4435
++				invalidClusterID = true
4436
++			}
4437
++		}
4438
++		if group == systemMasterGroup {
4439
++			masterNode = true
4440
++		}
4441
++	}
4442
++	if invalidClusterID {
4443
++		return admission.NewForbidden(a, fmt.Errorf("%s validation failed: request is not from this cluster", PluginName))
4444
++	}
4445
++
4446
++	// If a worker node name does not have the node prefix, then fail. This is needed for the request to go through node
4447
++	// restriction admission controller. If it is not set, then a user can bypass node restriction admission controller
4448
++	// and modify the master node.
4449
++	name := a.GetUserInfo().GetName()
4450
++	if !strings.HasPrefix(name, systemNodePrefix) && !masterNode {
4451
++		return admission.NewForbidden(a, fmt.Errorf("%s validation failed: username is invalid", PluginName))
4452
++	}
4453
++	return nil
4454
++}
4455
++
4413 4456
 +func isPrivilegedNamespace(a admission.Attributes) bool {
4414 4457
 +	// If the namespace mentioned in the resource is privileged, return true. We will hit this for calls made to all
4415 4458
 +	// resources in this namespace and during delete and update operation on the namespace itself.
... ...
@@ -4660,10 +4714,10 @@ index 0000000..e05e7be
4660 4660
 +}
4661 4661
 diff --git a/plugin/pkg/admission/vke/admission_test.go b/plugin/pkg/admission/vke/admission_test.go
4662 4662
 new file mode 100644
4663
-index 0000000..7f8ec60
4663
+index 0000000..4ce4a1f
4664 4664
 --- /dev/null
4665 4665
 +++ b/plugin/pkg/admission/vke/admission_test.go
4666
-@@ -0,0 +1,865 @@
4666
+@@ -0,0 +1,882 @@
4667 4667
 +package vke
4668 4668
 +
4669 4669
 +import (
... ...
@@ -4681,10 +4735,12 @@ index 0000000..7f8ec60
4681 4681
 +
4682 4682
 +const (
4683 4683
 +	testServiceAccountsGroup = "system.test\\cascade-controller-service-accounts"
4684
++	clusterID                = "cluster-id"
4684 4685
 +	defaultConfigFileFormat  = `
4685 4686
 +vmwareAdmissionController:
4686 4687
 +  privilegedGroup: %s
4687 4688
 +  podSecurityPolicyFile: %s
4689
++  clusterID: %s
4688 4690
 +`
4689 4691
 +	pspFileName   = "/tmp/psp.yaml"
4690 4692
 +	pspConfigFile = `
... ...
@@ -5012,13 +5068,7 @@ index 0000000..7f8ec60
5012 5012
 +		"allowed: kubelet group creates pod in vke-system namespace": {
5013 5013
 +			operation:          kadmission.Create,
5014 5014
 +			pod:                newTestPodBuilder().withNamespace(privilegedNamespace).build(),
5015
-+			userInfo:           newTestUserBuilder().withGroup(kubeletGroup).build(),
5016
-+			shouldPassValidate: true,
5017
-+		},
5018
-+		"allowed: kubeProxy group creates pod in vke-system namespace": {
5019
-+			operation:          kadmission.Create,
5020
-+			pod:                newTestPodBuilder().withNamespace(privilegedNamespace).build(),
5021
-+			userInfo:           newTestUserBuilder().withGroup(kubeProxyGroup).build(),
5015
++			userInfo:           newTestUserBuilder().withGroup(systemNodesGroup).withName("system:node:worker").build(),
5022 5016
 +			shouldPassValidate: true,
5023 5017
 +		},
5024 5018
 +		"denied: regular lightwave group does not grant privileged access": {
... ...
@@ -5033,6 +5083,27 @@ index 0000000..7f8ec60
5033 5033
 +			userInfo:           newTestUserBuilder().withGroup("test1\\group1").withGroup(testServiceAccountsGroup).build(),
5034 5034
 +			shouldPassValidate: true,
5035 5035
 +		},
5036
++		"denied: cross cluster acccess": {
5037
++			operation: kadmission.Create,
5038
++			pod:       newTestPodBuilder().build(),
5039
++			userInfo: newTestUserBuilder().withName("system:node:worker").withGroup("system:clusterID:some-cluster-id").
5040
++				withGroup("system:nodes").withGroup("system:worker").build(),
5041
++			shouldPassValidate: false,
5042
++		},
5043
++		"denied: node restriction admission controller bypass": {
5044
++			operation: kadmission.Create,
5045
++			pod:       newTestPodBuilder().build(),
5046
++			userInfo: newTestUserBuilder().withGroup("system:clusterID:cluster-id").withGroup("system:nodes").
5047
++				withGroup("system:worker").build(),
5048
++			shouldPassValidate: false,
5049
++		},
5050
++		"allowed: master node to bypass node restriction admission controller": {
5051
++			operation: kadmission.Create,
5052
++			pod:       newTestPodBuilder().build(),
5053
++			userInfo: newTestUserBuilder().withName("kubernetes-master").withGroup("system:clusterID:cluster-id").
5054
++				withGroup("system:nodes").withGroup("system:master").build(),
5055
++			shouldPassValidate: true,
5056
++		},
5036 5057
 +	}
5037 5058
 +	for k, v := range tests {
5038 5059
 +		testPodValidation(k, v.operation, v.pod, v.name, v.userInfo, v.shouldPassValidate, t)
... ...
@@ -5308,7 +5379,7 @@ index 0000000..7f8ec60
5308 5308
 +func testPodValidation(testCaseName string, op kadmission.Operation, pod *kapi.Pod, name string, userInfo user.Info,
5309 5309
 +	shouldPassValidate bool, t *testing.T) {
5310 5310
 +
5311
-+	defaultConfigFile := fmt.Sprintf(defaultConfigFileFormat, testServiceAccountsGroup, pspFileName)
5311
++	defaultConfigFile := fmt.Sprintf(defaultConfigFileFormat, testServiceAccountsGroup, pspFileName, clusterID)
5312 5312
 +	configFile := strings.NewReader(defaultConfigFile)
5313 5313
 +	plugin, err := NewVMwareAdmissionController(configFile)
5314 5314
 +	if err != nil {
... ...
@@ -5334,7 +5405,7 @@ index 0000000..7f8ec60
5334 5334
 +func testResourceValidation(testCaseName string, op kadmission.Operation, resource, subresource, name, namespace string,
5335 5335
 +	userInfo user.Info, shouldPassValidate bool, t *testing.T) {
5336 5336
 +
5337
-+	defaultConfigFile := fmt.Sprintf(defaultConfigFileFormat, testServiceAccountsGroup, pspFileName)
5337
++	defaultConfigFile := fmt.Sprintf(defaultConfigFileFormat, testServiceAccountsGroup, pspFileName, clusterID)
5338 5338
 +	configFile := strings.NewReader(defaultConfigFile)
5339 5339
 +	plugin, err := NewVMwareAdmissionController(configFile)
5340 5340
 +	if err != nil {
... ...
@@ -1,7 +1,7 @@
1 1
 Summary:        Kubernetes cluster management
2 2
 Name:           kubernetes
3 3
 Version:        1.10.2
4
-Release:        8%{?dist}
4
+Release:        9%{?dist}
5 5
 License:        ASL 2.0
6 6
 URL:            https://github.com/kubernetes/kubernetes/archive/v%{version}.tar.gz
7 7
 Source0:        kubernetes-%{version}.tar.gz
... ...
@@ -207,6 +207,8 @@ fi
207 207
 /opt/vmware/kubernetes/windows/amd64/kubectl.exe
208 208
 
209 209
 %changelog
210
+*   Fri Jul 20 2018 Bo Gan <ganb@vmware.com> 1.10.2-9
211
+-   Update vke patch (1f4aedb)
210 212
 *   Tue Jul 03 2018 Bo Gan <ganb@vmware.com> 1.10.2-8
211 213
 -   Update vke patch (3a784cd)
212 214
 *   Tue Jun 19 2018 Bo Gan <ganb@vmware.com> 1.10.2-7
... ...
@@ -1,7 +1,7 @@
1 1
 Summary:        Kubernetes cluster management
2 2
 Name:           kubernetes
3 3
 Version:        1.9.6
4
-Release:        7%{?dist}
4
+Release:        8%{?dist}
5 5
 License:        ASL 2.0
6 6
 URL:            https://github.com/kubernetes/kubernetes/archive/v%{version}.tar.gz
7 7
 Source0:        kubernetes-v%{version}.tar.gz
... ...
@@ -185,6 +185,8 @@ fi
185 185
 %{_bindir}/pause-amd64
186 186
 
187 187
 %changelog
188
+*   Fri Jul 20 2018 Bo Gan <ganb@vmware.com> 1.9.6-8
189
+-   Update vke patch (1f4aedb)
188 190
 *   Tue Jul 03 2018 Bo Gan <ganb@vmware.com> 1.9.6-7
189 191
 -   Update vke patch (3a784cd)
190 192
 *   Tue Jun 19 2018 Bo Gan <ganb@vmware.com> 1.9.6-6