Repository: kubernetes/kops (go)

Issue:
kubeAPIServer authorizationMode still requires adding Node,RBAC on k8s 1.19.x
**1. What `kops` version are you running? The command `kops version`, will display
 this information.**
1.19.1
**2. What Kubernetes version are you running? `kubectl version` will print the
 version if a cluster is running or provide the Kubernetes version specified as
 a `kops` flag.**
1.19.8
**3. What cloud provider are you using?**
AWS
**4. What commands did you run?  What is the simplest way to reproduce this issue?**
rolling update from k8s 1.18.13 to 1.19.8
**5. What happened after the commands executed?**
First master node didn't come up
**6. What did you expect to happen?**
First master node comes up
**7. Anything else do we need to know?**
Issue was
```
  kubeAPIServer:
     authorizationMode: RBAC
```
had to be changed to
```
  kubeAPIServer:
     authorizationMode: Node,RBAC
```
The rolling update then worked. This slack thread ( [https://kubernetes.slack.com/archives/C3QUFP0QM/p1614880910266900?thread_ts=1614872952.265000&cid=C3QUFP0QM](url) ) indicates that for k8s >= 1.19 Node should be auto-included per [ https://github.com/kubernetes/kops/blob/master/pkg/model/components/apiserver.go#L89](url) but that's clearly not happening.


Pull request:
Validate that kube-apiserver has the necessary authz modes set
Fixes #11014

Patch:
diff --git a/pkg/apis/kops/validation/validation.go b/pkg/apis/kops/validation/validation.go
index bca466ad6e..fad13d653d 100644
--- a/pkg/apis/kops/validation/validation.go
+++ b/pkg/apis/kops/validation/validation.go
@@ -454,12 +454,33 @@ func validateKubeAPIServer(v *kops.KubeAPIServerConfig, c *kops.Cluster, fldPath
 		}
 	}
 
-	if v.AuthorizationMode != nil && strings.Contains(*v.AuthorizationMode, "Webhook") {
-		if v.AuthorizationWebhookConfigFile == nil {
-			allErrs = append(allErrs, field.Required(fldPath.Child("authorizationWebhookConfigFile"), "Authorization mode Webhook requires authorizationWebhookConfigFile to be specified"))
+	if v.AuthorizationMode != nil {
+		if strings.Contains(*v.AuthorizationMode, "Webhook") {
+			if v.AuthorizationWebhookConfigFile == nil {
+				allErrs = append(allErrs, field.Required(fldPath.Child("authorizationWebhookConfigFile"), "Authorization mode Webhook requires authorizationWebhookConfigFile to be specified"))
+			}
 		}
-	}
 
+		if c.Spec.Authorization != nil && c.Spec.Authorization.RBAC != nil {
+
+			var hasNode, hasRBAC bool
+			for _, mode := range strings.Split(*v.AuthorizationMode, ",") {
+				switch mode {
+				case "Node":
+					hasNode = true
+				case "RBAC":
+					hasRBAC = true
+				default:
+					allErrs = append(allErrs, IsValidValue(fldPath.Child("authorizationMode"), &mode, []string{"ABAC", "Webhook", "Node", "RBAC", "AlwaysAllow", "AlwaysDeny"})...)
+				}
+			}
+			if kops.CloudProviderID(c.Spec.CloudProvider) == kops.CloudProviderAWS && c.IsKubernetesGTE("1.19") {
+				if !hasNode || !hasRBAC {
+					allErrs = append(allErrs, field.Required(fldPath.Child("authorizationMode"), "As of kubernetes 1.19 on AWS, authorizationMode must include RBAC and Node"))
+				}
+			}
+		}
+	}
 	return allErrs
 }
 


Test patch:
diff --git a/pkg/apis/kops/validation/validation_test.go b/pkg/apis/kops/validation/validation_test.go
index 99ea847d0b..9fc1df432a 100644
--- a/pkg/apis/kops/validation/validation_test.go
+++ b/pkg/apis/kops/validation/validation_test.go
@@ -169,6 +169,7 @@ func TestValidateKubeAPIServer(t *testing.T) {
 
 	grid := []struct {
 		Input          kops.KubeAPIServerConfig
+		Cluster        *kops.Cluster
 		ExpectedErrors []string
 		ExpectedDetail string
 	}{
@@ -207,14 +208,64 @@ func TestValidateKubeAPIServer(t *testing.T) {
 			},
 			ExpectedDetail: "Authorization mode Webhook requires authorizationWebhookConfigFile to be specified",
 		},
+		{
+			Input: kops.KubeAPIServerConfig{
+				AuthorizationMode: fi.String("RBAC"),
+			},
+			Cluster: &kops.Cluster{
+				Spec: kops.ClusterSpec{
+					Authorization: &kops.AuthorizationSpec{
+						RBAC: &kops.RBACAuthorizationSpec{},
+					},
+					KubernetesVersion: "1.19.0",
+					CloudProvider:     "aws",
+				},
+			},
+			ExpectedErrors: []string{
+				"Required value::KubeAPIServer.authorizationMode",
+			},
+		},
+		{
+			Input: kops.KubeAPIServerConfig{
+				AuthorizationMode: fi.String("RBAC,Node"),
+			},
+			Cluster: &kops.Cluster{
+				Spec: kops.ClusterSpec{
+					Authorization: &kops.AuthorizationSpec{
+						RBAC: &kops.RBACAuthorizationSpec{},
+					},
+					KubernetesVersion: "1.19.0",
+					CloudProvider:     "aws",
+				},
+			},
+		},
+		{
+			Input: kops.KubeAPIServerConfig{
+				AuthorizationMode: fi.String("RBAC,Node,Bogus"),
+			},
+			Cluster: &kops.Cluster{
+				Spec: kops.ClusterSpec{
+					Authorization: &kops.AuthorizationSpec{
+						RBAC: &kops.RBACAuthorizationSpec{},
+					},
+					KubernetesVersion: "1.19.0",
+					CloudProvider:     "aws",
+				},
+			},
+			ExpectedErrors: []string{
+				"Unsupported value::KubeAPIServer.authorizationMode",
+			},
+		},
 	}
 	for _, g := range grid {
-		cluster := &kops.Cluster{
-			Spec: kops.ClusterSpec{
-				KubernetesVersion: "1.16.0",
-			},
+		if g.Cluster == nil {
+			g.Cluster = &kops.Cluster{
+				Spec: kops.ClusterSpec{
+					KubernetesVersion: "1.16.0",
+				},
+			}
 		}
-		errs := validateKubeAPIServer(&g.Input, cluster, field.NewPath("KubeAPIServer"))
+		errs := validateKubeAPIServer(&g.Input, g.Cluster, field.NewPath("KubeAPIServer"))
 
 		testErrors(t, g.Input, errs, g.ExpectedErrors)