diff --git a/cfg/aks-1.7/policies.yaml b/cfg/aks-1.7/policies.yaml index 043c3ce..1c0def5 100644 --- a/cfg/aks-1.7/policies.yaml +++ b/cfg/aks-1.7/policies.yaml @@ -10,7 +10,16 @@ groups: checks: - id: 4.1.1 text: "Ensure that the cluster-admin role is only used where required (Automated)" - type: "manual" + audit: "kubectl get clusterrolebindings -o=custom-columns=NAME:.metadata.name,ROLE:.roleRef.name,SUBJECT:.subjects[*].name" + audit_config: "kubectl get clusterrolebindings" + tests: + test_items: + - flag: cluster-admin + path: '{.roleRef.name}' + set: true + compare: + op: eq + value: "cluster-admin" remediation: | Identify all clusterrolebindings to the cluster-admin role. Check if they are used and if they need this role or if they could use a role with fewer privileges. @@ -21,7 +30,34 @@ groups: - id: 4.1.2 text: "Minimize access to secrets (Automated)" - type: "manual" + audit: "kubectl get roles,rolebindings --all-namespaces -o=custom-columns=NAME:.metadata.name,ROLE:.rules[*].resources,SUBJECT:.subjects[*].name" + audit_config: "kubectl get roles --all-namespaces" + tests: + test_items: + - flag: secrets + path: '{.rules[*].resources}' + set: true + compare: + op: eq + value: "secrets" + - flag: get + path: '{.rules[*].verbs}' + set: true + compare: + op: contains + value: "get" + - flag: list + path: '{.rules[*].verbs}' + set: true + compare: + op: contains + value: "list" + - flag: watch + path: '{.rules[*].verbs}' + set: true + compare: + op: contains + value: "watch" remediation: | Where possible, remove get, list and watch access to secret objects in the cluster. scored: false @@ -45,14 +81,44 @@ groups: - id: 4.1.4 text: "Minimize access to create pods (Automated)" - type: "manual" + audit: "kubectl get roles,rolebindings --all-namespaces -o=custom-columns=NAME:.metadata.name,ROLE:.rules[*].resources,SUBJECT:.subjects[*].name" + audit_config: "kubectl get roles --all-namespaces" + tests: + test_items: + - flag: pods + path: '{.rules[*].resources}' + set: true + compare: + op: eq + value: "pods" + - flag: create + path: '{.rules[*].verbs}' + set: true + compare: + op: contains + value: "create" remediation: | Where possible, remove create access to pod objects in the cluster. scored: false - id: 4.1.5 text: "Ensure that default service accounts are not actively used (Automated)" - type: "manual" + audit: "kubectl get serviceaccounts --all-namespaces -o custom-columns=NAME:.metadata.name,NAMESPACE:.metadata.namespace,TOKEN:.automountServiceAccountToken" + audit_config: "kubectl get serviceaccounts --all-namespaces" + tests: + test_items: + - flag: default + path: '{.metadata.name}' + set: true + compare: + op: eq + value: "default" + - flag: automountServiceAccountToken + path: '{.automountServiceAccountToken}' + set: true + compare: + op: eq + value: "false" remediation: | Create explicit service accounts wherever a Kubernetes workload requires specific access to the Kubernetes API server. @@ -62,7 +128,16 @@ groups: - id: 4.1.6 text: "Ensure that Service Account Tokens are only mounted where necessary (Automated)" - type: "manual" + audit: "kubectl get pods --all-namespaces -o custom-columns=NAME:.metadata.name,NAMESPACE:.metadata.namespace,SERVICE_ACCOUNT:.spec.serviceAccountName,MOUNT_TOKEN:.spec.automountServiceAccountToken" + audit_config: "kubectl get pods --all-namespaces" + tests: + test_items: + - flag: automountServiceAccountToken + path: '{.spec.automountServiceAccountToken}' + set: true + compare: + op: eq + value: "false" remediation: | Modify the definition of pods and service accounts which do not need to mount service account tokens to disable it. @@ -258,7 +333,16 @@ groups: - id: 4.6.3 text: "The default namespace should not be used (Automated)" - type: "manual" + audit: "kubectl get all -n default" + audit_config: "kubectl get all -n default" + tests: + test_items: + - flag: "namespace" + path: "{.metadata.namespace}" + set: true + compare: + op: eq + value: "default" remediation: | Ensure that namespaces are created to allow for appropriate segregation of Kubernetes resources and that all new resources are created in a specific namespace. diff --git a/cmd/util.go b/cmd/util.go index 8f290f5..417bc22 100644 --- a/cmd/util.go +++ b/cmd/util.go @@ -300,6 +300,7 @@ func getKubeVersion() (*KubeVersion, error) { glog.V(3).Infof("Error fetching cluster config: %s", err) } isRKE := false + isAKS := false // Variable to track AKS detection if err == nil && kubeConfig != nil { k8sClient, err := kubernetes.NewForConfig(kubeConfig) if err != nil { @@ -311,7 +312,12 @@ func getKubeVersion() (*KubeVersion, error) { if err != nil { glog.V(3).Infof("Error detecting RKE cluster: %s", err) } + isAKS, err = IsAKS(context.Background(), k8sClient) + if err != nil { + glog.V(3).Infof("Error detecting AKS cluster: %s", err) + } } + } if k8sVer, err := getKubeVersionFromRESTAPI(); err == nil { @@ -319,6 +325,9 @@ func getKubeVersion() (*KubeVersion, error) { if isRKE { k8sVer.GitVersion = k8sVer.GitVersion + "-rancher1" } + if isAKS { + k8sVer.GitVersion = k8sVer.GitVersion + "-aks1" // Mark it as AKS in the version + } return k8sVer, nil } @@ -485,6 +494,26 @@ func getPlatformInfoFromVersion(s string) Platform { } } +func IsAKS(ctx context.Context, k8sClient kubernetes.Interface) (bool, error) { + // Query the nodes for any annotations that indicate AKS (Azure Kubernetes Service) + nodes, err := k8sClient.CoreV1().Nodes().List(ctx, metav1.ListOptions{Limit: 1}) + if err != nil { + return false, err + } + + // If the cluster contains nodes with specific AKS annotations, it’s likely AKS + if len(nodes.Items) == 0 { + return false, nil + } + + annotations := nodes.Items[0].Annotations + if _, exists := annotations["azure-identity-binding"]; exists { // "azure-identity-binding" is one possible AKS-specific annotation + return true, nil + } + + return false, nil +} + func getPlatformBenchmarkVersion(platform Platform) string { glog.V(3).Infof("getPlatformBenchmarkVersion platform: %s", platform) switch platform.Name { diff --git a/cmd/util_test.go b/cmd/util_test.go index aea9d64..72aed0c 100644 --- a/cmd/util_test.go +++ b/cmd/util_test.go @@ -627,6 +627,11 @@ func Test_getPlatformNameFromKubectlOutput(t *testing.T) { args: args{s: "v1.27.6+rke2r1"}, want: Platform{Name: "rke2r", Version: "1.27"}, }, + { + name: "aks", + args: args{s: "v1.27.6+aks1"}, + want: Platform{Name: "aks", Version: "1.27"}, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -729,6 +734,13 @@ func Test_getPlatformBenchmarkVersion(t *testing.T) { }, want: "rke2-cis-1.7", }, + { + name: "aks", + args: args{ + platform: Platform{Name: "aks", Version: "1.27"}, + }, + want: "aks-1.7", + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) {