diff options
-rw-r--r-- | test/security/k8s/src/check/check.go | 45 | ||||
-rw-r--r-- | test/security/k8s/src/check/cmd/check/check.go | 14 | ||||
-rw-r--r-- | test/security/k8s/src/check/rancher/rancher.go | 36 | ||||
-rw-r--r-- | test/security/k8s/src/check/raw/raw.go | 31 | ||||
-rw-r--r-- | test/security/k8s/src/check/validators/master/master.go | 97 |
5 files changed, 142 insertions, 81 deletions
diff --git a/test/security/k8s/src/check/check.go b/test/security/k8s/src/check/check.go new file mode 100644 index 000000000..c185887d7 --- /dev/null +++ b/test/security/k8s/src/check/check.go @@ -0,0 +1,45 @@ +package check + +// Informer collects and returns information on cluster. +type Informer interface { + // GetAPIParams returns API server parameters. + GetAPIParams() ([]string, error) +} + +// Command represents commands run on cluster. +type Command int + +const ( + // APIProcess represents API server command ("kube-apiserver"). + APIProcess Command = iota +) + +func (c Command) String() string { + names := [...]string{ + "kube-apiserver", + } + + if c < APIProcess || c > APIProcess { + return "exit" + } + return names[c] +} + +// Service represents services run on Rancher-based cluster. +type Service int + +const ( + // APIService represents API server service ("kubernetes/kubernetes"). + APIService Service = iota +) + +func (s Service) String() string { + names := [...]string{ + "kubernetes/kubernetes", + } + + if s < APIService || s > APIService { + return "" + } + return names[s] +} diff --git a/test/security/k8s/src/check/cmd/check/check.go b/test/security/k8s/src/check/cmd/check/check.go index 80a17f0c5..40e3a092c 100644 --- a/test/security/k8s/src/check/cmd/check/check.go +++ b/test/security/k8s/src/check/cmd/check/check.go @@ -4,6 +4,7 @@ import ( "flag" "log" + "check" "check/rancher" "check/raw" "check/validators/master" @@ -25,23 +26,20 @@ func main() { *rke = true } - var ( - k8sParams []string - err error - ) + var info check.Informer switch { case *ranchercli: - k8sParams, err = rancher.GetK8sParams() + info = &rancher.Rancher{} case *rke: - k8sParams, err = raw.GetK8sParams() + info = &raw.Raw{} default: log.Fatal("Missing cluster access method.") } + apiParams, err := info.GetAPIParams() if err != nil { log.Fatal(err) } - - master.Check(k8sParams) + master.CheckAPI(apiParams) } diff --git a/test/security/k8s/src/check/rancher/rancher.go b/test/security/k8s/src/check/rancher/rancher.go index d60b73b65..d77f15445 100644 --- a/test/security/k8s/src/check/rancher/rancher.go +++ b/test/security/k8s/src/check/rancher/rancher.go @@ -3,8 +3,10 @@ package rancher import ( "bytes" - "errors" + "fmt" "os/exec" + + "check" ) const ( @@ -16,32 +18,40 @@ const ( cmdDockerCmdPs = "ps" cmdDockerCmdPsParams = "--no-trunc" cmdDockerCmdPsFilter = "--filter" - cmdDockerCmdPsFilterArgs = "label=io.rancher.stack_service.name=kubernetes/kubernetes" + cmdDockerCmdPsFilterArgs = "label=io.rancher.stack_service.name=" cmdDockerCmdPsFormat = "--format" cmdDockerCmdPsFormatArgs = "{{.Command}}" - k8sProcess = "kube-apiserver" ) -// GetK8sParams returns parameters of running Kubernetes API server. +// Rancher implements Informer interface. +type Rancher struct { + check.Informer +} + +// GetAPIParams returns parameters of running Kubernetes API server. // It queries default environment set in configuration file. -func GetK8sParams() ([]string, error) { +func (r *Rancher) GetAPIParams() ([]string, error) { + return getProcessParams(check.APIProcess, check.APIService) +} + +func getProcessParams(process check.Command, service check.Service) ([]string, error) { hosts, err := listHosts() if err != nil { return []string{}, err } for _, host := range hosts { - cmd, err := getK8sCmd(host) + cmd, err := getPsCmdOutput(host, service) if err != nil { return []string{}, err } if len(cmd) > 0 { - i := bytes.Index(cmd, []byte(k8sProcess)) + i := bytes.Index(cmd, []byte(process.String())) if i == -1 { - return []string{}, errors.New("missing " + k8sProcess + " command") + return []string{}, fmt.Errorf("missing %s command", process) } - return btos(cmd[i+len(k8sProcess):]), nil + return btos(cmd[i+len(process.String()):]), nil } } return []string{}, nil @@ -58,17 +68,17 @@ func listHosts() ([]string, error) { return btos(out), nil } -// getK8sCmd returns running Kubernetes API server command with its parameters. +// getPsCmdOutput returns running Kubernetes service command with its parameters. // It queries default environment set in configuration file. -func getK8sCmd(host string) ([]byte, error) { +func getPsCmdOutput(host string, service check.Service) ([]byte, error) { // Following is equivalent to: // $ rancher --host $HOST \ // docker ps --no-trunc \ - // --filter "label=io.rancher.stack_service.name=kubernetes/kubernetes" \ + // --filter "label=io.rancher.stack_service.name=$SERVICE" \ // --format "{{.Command}}" cmd := exec.Command(bin, paramHost, host, cmdDocker, cmdDockerCmdPs, cmdDockerCmdPsParams, - cmdDockerCmdPsFilter, cmdDockerCmdPsFilterArgs, + cmdDockerCmdPsFilter, cmdDockerCmdPsFilterArgs+service.String(), cmdDockerCmdPsFormat, cmdDockerCmdPsFormatArgs) out, err := cmd.Output() if err != nil { diff --git a/test/security/k8s/src/check/raw/raw.go b/test/security/k8s/src/check/raw/raw.go index 4efa1d4f8..2a9f0a17f 100644 --- a/test/security/k8s/src/check/raw/raw.go +++ b/test/security/k8s/src/check/raw/raw.go @@ -3,7 +3,7 @@ package raw import ( "bytes" - "errors" + "fmt" "io/ioutil" "os/user" "path/filepath" @@ -11,6 +11,7 @@ import ( "golang.org/x/crypto/ssh" kh "golang.org/x/crypto/ssh/knownhosts" + "check" "check/config" ) @@ -19,15 +20,21 @@ const ( etcd = "etcd" worker = "worker" - k8sProcess = "kube-apiserver" - dockerInspectCmd = "docker inspect " + k8sProcess + " --format {{.Args}}" - knownHostsFile = "~/.ssh/known_hosts" ) -// GetK8sParams returns parameters of running Kubernetes API servers. +// Raw implements Informer interface. +type Raw struct { + check.Informer +} + +// GetAPIParams returns parameters of running Kubernetes API servers. // It queries only cluster nodes with "controlplane" role. -func GetK8sParams() ([]string, error) { +func (r *Raw) GetAPIParams() ([]string, error) { + return getProcessParams(check.APIProcess) +} + +func getProcessParams(process check.Command) ([]string, error) { nodes, err := config.GetNodesInfo() if err != nil { return []string{}, err @@ -35,17 +42,17 @@ func GetK8sParams() ([]string, error) { for _, node := range nodes { if isControlplaneNode(node.Role) { - cmd, err := getK8sCmd(node) + cmd, err := getInspectCmdOutput(node, process) if err != nil { return []string{}, err } if len(cmd) > 0 { - i := bytes.Index(cmd, []byte(k8sProcess)) + i := bytes.Index(cmd, []byte(process.String())) if i == -1 { - return []string{}, errors.New("missing " + k8sProcess + " command") + return []string{}, fmt.Errorf("missing %s command", process) } - return btos(cmd[i+len(k8sProcess):]), nil + return btos(cmd[i+len(process.String()):]), nil } } } @@ -62,7 +69,7 @@ func isControlplaneNode(roles []string) bool { return false } -func getK8sCmd(node config.NodeInfo) ([]byte, error) { +func getInspectCmdOutput(node config.NodeInfo, cmd check.Command) ([]byte, error) { path, err := expandPath(node.SSHKeyPath) if err != nil { return nil, err @@ -95,7 +102,7 @@ func getK8sCmd(node config.NodeInfo) ([]byte, error) { } defer conn.Close() - out, err := runCommand(dockerInspectCmd, conn) + out, err := runCommand(fmt.Sprintf("docker inspect %s --format {{.Args}}", cmd), conn) if err != nil { return nil, err } diff --git a/test/security/k8s/src/check/validators/master/master.go b/test/security/k8s/src/check/validators/master/master.go index e9dc66cc6..ff3b79648 100644 --- a/test/security/k8s/src/check/validators/master/master.go +++ b/test/security/k8s/src/check/validators/master/master.go @@ -6,52 +6,53 @@ import ( "check/validators/master/api" ) -// Check validates master node complies with CIS guideliness. -func Check(k8sParams []string) { - log.Printf("IsBasicAuthFileAbsent: %t\n", api.IsBasicAuthFileAbsent(k8sParams)) - log.Printf("IsTokenAuthFileAbsent: %t\n", api.IsTokenAuthFileAbsent(k8sParams)) - log.Printf("IsInsecureAllowAnyTokenAbsent: %t\n", api.IsInsecureAllowAnyTokenAbsent(k8sParams)) - - log.Printf("IsAnonymousAuthDisabled: %t\n", api.IsAnonymousAuthDisabled(k8sParams)) - log.Printf("IsInsecurePortUnbound: %t\n", api.IsInsecurePortUnbound(k8sParams)) - log.Printf("IsProfilingDisabled: %t\n", api.IsProfilingDisabled(k8sParams)) - log.Printf("IsRepairMalformedUpdatesDisabled: %t\n", api.IsRepairMalformedUpdatesDisabled(k8sParams)) - log.Printf("IsServiceAccountLookupEnabled: %t\n", api.IsServiceAccountLookupEnabled(k8sParams)) - - log.Printf("IsKubeletHTTPSAbsentOrEnabled: %t\n", api.IsKubeletHTTPSAbsentOrEnabled(k8sParams)) - log.Printf("IsInsecureBindAddressAbsentOrLoopback: %t\n", api.IsInsecureBindAddressAbsentOrLoopback(k8sParams)) - log.Printf("IsSecurePortAbsentOrValid: %t\n", api.IsSecurePortAbsentOrValid(k8sParams)) - - log.Printf("IsAlwaysAdmitAdmissionControlPluginExcluded: %t\n", api.IsAlwaysAdmitAdmissionControlPluginExcluded(k8sParams)) - - log.Printf("IsAlwaysPullImagesAdmissionControlPluginIncluded: %t\n", api.IsAlwaysPullImagesAdmissionControlPluginIncluded(k8sParams)) - log.Printf("IsDenyEscalatingExecAdmissionControlPluginIncluded: %t\n", api.IsDenyEscalatingExecAdmissionControlPluginIncluded(k8sParams)) - log.Printf("IsSecurityContextDenyAdmissionControlPluginIncluded: %t\n", api.IsSecurityContextDenyAdmissionControlPluginIncluded(k8sParams)) - log.Printf("IsPodSecurityPolicyAdmissionControlPluginIncluded: %t\n", api.IsPodSecurityPolicyAdmissionControlPluginIncluded(k8sParams)) - log.Printf("IsServiceAccountAdmissionControlPluginIncluded: %t\n", api.IsServiceAccountAdmissionControlPluginIncluded(k8sParams)) - log.Printf("IsNodeRestrictionAdmissionControlPluginIncluded: %t\n", api.IsNodeRestrictionAdmissionControlPluginIncluded(k8sParams)) - log.Printf("IsEventRateLimitAdmissionControlPluginIncluded: %t\n", api.IsEventRateLimitAdmissionControlPluginIncluded(k8sParams)) - - log.Printf("IsNamespaceLifecycleAdmissionControlPluginNotExcluded: %t\n", api.IsNamespaceLifecycleAdmissionControlPluginNotExcluded(k8sParams)) - - log.Printf("IsAlwaysAllowAuthorizationModeExcluded: %t\n", api.IsAlwaysAllowAuthorizationModeExcluded(k8sParams)) - log.Printf("IsNodeAuthorizationModeIncluded: %t\n", api.IsNodeAuthorizationModeIncluded(k8sParams)) - - log.Printf("IsAuditLogPathSet: %t\n", api.IsAuditLogPathSet(k8sParams)) - log.Printf("IsAuditLogMaxAgeValid: %t\n", api.IsAuditLogMaxAgeValid(k8sParams)) - log.Printf("IsAuditLogMaxBackupValid: %t\n", api.IsAuditLogMaxBackupValid(k8sParams)) - log.Printf("IsAuditLogMaxSizeValid: %t\n", api.IsAuditLogMaxSizeValid(k8sParams)) - - log.Printf("IsRequestTimeoutValid: %t\n", api.IsRequestTimeoutValid(k8sParams)) - - log.Printf("IsKubeletCertificateAuthoritySet: %t\n", api.IsKubeletCertificateAuthoritySet(k8sParams)) - log.Printf("IsClientCertificateAuthoritySet: %t\n", api.IsClientCertificateAuthoritySet(k8sParams)) - log.Printf("IsEtcdCertificateAuthoritySet: %t\n", api.IsEtcdCertificateAuthoritySet(k8sParams)) - - log.Printf("IsServiceAccountKeySet: %t\n", api.IsServiceAccountKeySet(k8sParams)) - log.Printf("IsKubeletClientCertificateAndKeySet: %t\n", api.IsKubeletClientCertificateAndKeySet(k8sParams)) - log.Printf("IsEtcdCertificateAndKeySet: %t\n", api.IsEtcdCertificateAndKeySet(k8sParams)) - log.Printf("IsTLSCertificateAndKeySet: %t\n", api.IsTLSCertificateAndKeySet(k8sParams)) - - log.Printf("IsStrongCryptoCipherInUse: %t\n", api.IsStrongCryptoCipherInUse(k8sParams)) +// CheckAPI validates API server complies with CIS guideliness. +func CheckAPI(params []string) { + log.Println("==> API:") + log.Printf("IsBasicAuthFileAbsent: %t\n", api.IsBasicAuthFileAbsent(params)) + log.Printf("IsTokenAuthFileAbsent: %t\n", api.IsTokenAuthFileAbsent(params)) + log.Printf("IsInsecureAllowAnyTokenAbsent: %t\n", api.IsInsecureAllowAnyTokenAbsent(params)) + + log.Printf("IsAnonymousAuthDisabled: %t\n", api.IsAnonymousAuthDisabled(params)) + log.Printf("IsInsecurePortUnbound: %t\n", api.IsInsecurePortUnbound(params)) + log.Printf("IsProfilingDisabled: %t\n", api.IsProfilingDisabled(params)) + log.Printf("IsRepairMalformedUpdatesDisabled: %t\n", api.IsRepairMalformedUpdatesDisabled(params)) + log.Printf("IsServiceAccountLookupEnabled: %t\n", api.IsServiceAccountLookupEnabled(params)) + + log.Printf("IsKubeletHTTPSAbsentOrEnabled: %t\n", api.IsKubeletHTTPSAbsentOrEnabled(params)) + log.Printf("IsInsecureBindAddressAbsentOrLoopback: %t\n", api.IsInsecureBindAddressAbsentOrLoopback(params)) + log.Printf("IsSecurePortAbsentOrValid: %t\n", api.IsSecurePortAbsentOrValid(params)) + + log.Printf("IsAlwaysAdmitAdmissionControlPluginExcluded: %t\n", api.IsAlwaysAdmitAdmissionControlPluginExcluded(params)) + + log.Printf("IsAlwaysPullImagesAdmissionControlPluginIncluded: %t\n", api.IsAlwaysPullImagesAdmissionControlPluginIncluded(params)) + log.Printf("IsDenyEscalatingExecAdmissionControlPluginIncluded: %t\n", api.IsDenyEscalatingExecAdmissionControlPluginIncluded(params)) + log.Printf("IsSecurityContextDenyAdmissionControlPluginIncluded: %t\n", api.IsSecurityContextDenyAdmissionControlPluginIncluded(params)) + log.Printf("IsPodSecurityPolicyAdmissionControlPluginIncluded: %t\n", api.IsPodSecurityPolicyAdmissionControlPluginIncluded(params)) + log.Printf("IsServiceAccountAdmissionControlPluginIncluded: %t\n", api.IsServiceAccountAdmissionControlPluginIncluded(params)) + log.Printf("IsNodeRestrictionAdmissionControlPluginIncluded: %t\n", api.IsNodeRestrictionAdmissionControlPluginIncluded(params)) + log.Printf("IsEventRateLimitAdmissionControlPluginIncluded: %t\n", api.IsEventRateLimitAdmissionControlPluginIncluded(params)) + + log.Printf("IsNamespaceLifecycleAdmissionControlPluginNotExcluded: %t\n", api.IsNamespaceLifecycleAdmissionControlPluginNotExcluded(params)) + + log.Printf("IsAlwaysAllowAuthorizationModeExcluded: %t\n", api.IsAlwaysAllowAuthorizationModeExcluded(params)) + log.Printf("IsNodeAuthorizationModeIncluded: %t\n", api.IsNodeAuthorizationModeIncluded(params)) + + log.Printf("IsAuditLogPathSet: %t\n", api.IsAuditLogPathSet(params)) + log.Printf("IsAuditLogMaxAgeValid: %t\n", api.IsAuditLogMaxAgeValid(params)) + log.Printf("IsAuditLogMaxBackupValid: %t\n", api.IsAuditLogMaxBackupValid(params)) + log.Printf("IsAuditLogMaxSizeValid: %t\n", api.IsAuditLogMaxSizeValid(params)) + + log.Printf("IsRequestTimeoutValid: %t\n", api.IsRequestTimeoutValid(params)) + + log.Printf("IsKubeletCertificateAuthoritySet: %t\n", api.IsKubeletCertificateAuthoritySet(params)) + log.Printf("IsClientCertificateAuthoritySet: %t\n", api.IsClientCertificateAuthoritySet(params)) + log.Printf("IsEtcdCertificateAuthoritySet: %t\n", api.IsEtcdCertificateAuthoritySet(params)) + + log.Printf("IsServiceAccountKeySet: %t\n", api.IsServiceAccountKeySet(params)) + log.Printf("IsKubeletClientCertificateAndKeySet: %t\n", api.IsKubeletClientCertificateAndKeySet(params)) + log.Printf("IsEtcdCertificateAndKeySet: %t\n", api.IsEtcdCertificateAndKeySet(params)) + log.Printf("IsTLSCertificateAndKeySet: %t\n", api.IsTLSCertificateAndKeySet(params)) + + log.Printf("IsStrongCryptoCipherInUse: %t\n", api.IsStrongCryptoCipherInUse(params)) } |