mirror of
https://github.com/aquasecurity/kube-bench.git
synced 2025-07-03 05:12:43 +00:00
Support auto-detect platform when running on EKS or GKE (#683)
* Support auto-detect platform when running on EKS or GKE * Change to get platform name from `kubectl version` * fix regexp and add test * Update Server Version match for EKS * try to get version info from api sever at first
This commit is contained in:
parent
0fd4dd97b5
commit
0f171657bd
@ -317,13 +317,17 @@ func getBenchmarkVersion(kubeVersion, benchmarkVersion string, v *viper.Viper) (
|
|||||||
if !isEmpty(kubeVersion) && !isEmpty(benchmarkVersion) {
|
if !isEmpty(kubeVersion) && !isEmpty(benchmarkVersion) {
|
||||||
return "", fmt.Errorf("It is an error to specify both --version and --benchmark flags")
|
return "", fmt.Errorf("It is an error to specify both --version and --benchmark flags")
|
||||||
}
|
}
|
||||||
|
if isEmpty(benchmarkVersion) && isEmpty(kubeVersion) {
|
||||||
|
benchmarkVersion = getPlatformBenchmarkVersion(getPlatformName())
|
||||||
|
}
|
||||||
|
|
||||||
if isEmpty(benchmarkVersion) {
|
if isEmpty(benchmarkVersion) {
|
||||||
if isEmpty(kubeVersion) {
|
if isEmpty(kubeVersion) {
|
||||||
kubeVersion, err = getKubeVersion()
|
kv, err := getKubeVersion()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("Version check failed: %s\nAlternatively, you can specify the version with --version", err)
|
return "", fmt.Errorf("Version check failed: %s\nAlternatively, you can specify the version with --version", err)
|
||||||
}
|
}
|
||||||
|
kubeVersion = kv.BaseVersion()
|
||||||
}
|
}
|
||||||
|
|
||||||
kubeToBenchmarkMap, err := loadVersionMapping(v)
|
kubeToBenchmarkMap, err := loadVersionMapping(v)
|
||||||
@ -377,7 +381,7 @@ func isThisNodeRunning(nodeType check.NodeType) bool {
|
|||||||
|
|
||||||
func exitCodeSelection(controlsCollection []*check.Controls) int {
|
func exitCodeSelection(controlsCollection []*check.Controls) int {
|
||||||
for _, control := range controlsCollection {
|
for _, control := range controlsCollection {
|
||||||
if control.Fail > 0 {
|
if control.Fail > 0 {
|
||||||
return exitCode
|
return exitCode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -324,7 +324,7 @@ func TestGetBenchmarkVersion(t *testing.T) {
|
|||||||
|
|
||||||
withFakeKubectl := func(kubeVersion, benchmarkVersion string, v *viper.Viper, fn getBenchmarkVersionFnToTest) (string, error) {
|
withFakeKubectl := func(kubeVersion, benchmarkVersion string, v *viper.Viper, fn getBenchmarkVersionFnToTest) (string, error) {
|
||||||
execCode := `#!/bin/sh
|
execCode := `#!/bin/sh
|
||||||
echo "Server Version: v1.15.10"
|
echo '{"serverVersion": {"major": "1", "minor": "15", "gitVersion": "v1.15.10"}}'
|
||||||
`
|
`
|
||||||
restore, err := fakeExecutableInPath("kubectl", execCode)
|
restore, err := fakeExecutableInPath("kubectl", execCode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -14,7 +14,25 @@ import (
|
|||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getKubeVersionFromRESTAPI() (string, error) {
|
type KubeVersion struct {
|
||||||
|
Major string
|
||||||
|
Minor string
|
||||||
|
baseVersion string
|
||||||
|
GitVersion string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KubeVersion) BaseVersion() string {
|
||||||
|
if k.baseVersion != "" {
|
||||||
|
return k.baseVersion
|
||||||
|
}
|
||||||
|
// Some provides return the minor version like "15+"
|
||||||
|
minor := strings.Replace(k.Minor, "+", "", -1)
|
||||||
|
ver := fmt.Sprintf("%s.%s", k.Major, minor)
|
||||||
|
k.baseVersion = ver
|
||||||
|
return ver
|
||||||
|
}
|
||||||
|
|
||||||
|
func getKubeVersionFromRESTAPI() (*KubeVersion, error) {
|
||||||
k8sVersionURL := getKubernetesURL()
|
k8sVersionURL := getKubernetesURL()
|
||||||
serviceaccount := "/var/run/secrets/kubernetes.io/serviceaccount"
|
serviceaccount := "/var/run/secrets/kubernetes.io/serviceaccount"
|
||||||
cacertfile := fmt.Sprintf("%s/ca.crt", serviceaccount)
|
cacertfile := fmt.Sprintf("%s/ca.crt", serviceaccount)
|
||||||
@ -22,23 +40,23 @@ func getKubeVersionFromRESTAPI() (string, error) {
|
|||||||
|
|
||||||
tlsCert, err := loadCertficate(cacertfile)
|
tlsCert, err := loadCertficate(cacertfile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
tb, err := ioutil.ReadFile(tokenfile)
|
tb, err := ioutil.ReadFile(tokenfile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
token := strings.TrimSpace(string(tb))
|
token := strings.TrimSpace(string(tb))
|
||||||
|
|
||||||
data, err := getWebDataWithRetry(k8sVersionURL, token, tlsCert)
|
data, err := getWebDataWithRetry(k8sVersionURL, token, tlsCert)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
k8sVersion, err := extractVersion(data)
|
k8sVersion, err := extractVersion(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
return k8sVersion, nil
|
return k8sVersion, nil
|
||||||
}
|
}
|
||||||
@ -61,31 +79,32 @@ func getWebDataWithRetry(k8sVersionURL, token string, cacert *tls.Certificate) (
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func extractVersion(data []byte) (string, error) {
|
type VersionResponse struct {
|
||||||
type versionResponse struct {
|
Major string
|
||||||
Major string
|
Minor string
|
||||||
Minor string
|
GitVersion string
|
||||||
GitVersion string
|
GitCommit string
|
||||||
GitCommit string
|
GitTreeState string
|
||||||
GitTreeState string
|
BuildDate string
|
||||||
BuildDate string
|
GoVersion string
|
||||||
GoVersion string
|
Compiler string
|
||||||
Compiler string
|
Platform string
|
||||||
Platform string
|
}
|
||||||
}
|
|
||||||
|
|
||||||
vrObj := &versionResponse{}
|
func extractVersion(data []byte) (*KubeVersion, error) {
|
||||||
|
vrObj := &VersionResponse{}
|
||||||
glog.V(2).Info(fmt.Sprintf("vd: %s\n", string(data)))
|
glog.V(2).Info(fmt.Sprintf("vd: %s\n", string(data)))
|
||||||
err := json.Unmarshal(data, vrObj)
|
err := json.Unmarshal(data, vrObj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
glog.V(2).Info(fmt.Sprintf("vrObj: %#v\n", vrObj))
|
glog.V(2).Info(fmt.Sprintf("vrObj: %#v\n", vrObj))
|
||||||
|
|
||||||
// Some provides return the minor version like "15+"
|
return &KubeVersion{
|
||||||
minor := strings.Replace(vrObj.Minor, "+", "", -1)
|
Major: vrObj.Major,
|
||||||
ver := fmt.Sprintf("%s.%s", vrObj.Major, minor)
|
Minor: vrObj.Minor,
|
||||||
return ver, nil
|
GitVersion: vrObj.GitVersion,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getWebData(srvURL, token string, cacert *tls.Certificate) ([]byte, error) {
|
func getWebData(srvURL, token string, cacert *tls.Certificate) ([]byte, error) {
|
||||||
|
@ -218,7 +218,7 @@ func TestExtractVersion(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("unexpected error: %v", err)
|
t.Errorf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
if c.expectedVer != ver {
|
if c.expectedVer != ver.BaseVersion() {
|
||||||
t.Errorf("Expected %q but Got %q", c.expectedVer, ver)
|
t.Errorf("Expected %q but Got %q", c.expectedVer, ver)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
67
cmd/util.go
67
cmd/util.go
@ -1,6 +1,7 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
@ -279,7 +280,7 @@ Alternatively, you can specify the version with --version
|
|||||||
kube-bench --version <VERSION> ...
|
kube-bench --version <VERSION> ...
|
||||||
`
|
`
|
||||||
|
|
||||||
func getKubeVersion() (string, error) {
|
func getKubeVersion() (*KubeVersion, error) {
|
||||||
|
|
||||||
if k8sVer, err := getKubeVersionFromRESTAPI(); err == nil {
|
if k8sVer, err := getKubeVersionFromRESTAPI(); err == nil {
|
||||||
glog.V(2).Info(fmt.Sprintf("Kubernetes REST API Reported version: %s", k8sVer))
|
glog.V(2).Info(fmt.Sprintf("Kubernetes REST API Reported version: %s", k8sVer))
|
||||||
@ -300,7 +301,7 @@ func getKubeVersion() (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
glog.Warning(missingKubectlKubeletMessage)
|
glog.Warning(missingKubectlKubeletMessage)
|
||||||
return "", fmt.Errorf("unable to find the programs kubectl or kubelet in the PATH")
|
return nil, fmt.Errorf("unable to find the programs kubectl or kubelet in the PATH")
|
||||||
}
|
}
|
||||||
return getKubeVersionFromKubelet(), nil
|
return getKubeVersionFromKubelet(), nil
|
||||||
}
|
}
|
||||||
@ -308,8 +309,8 @@ func getKubeVersion() (string, error) {
|
|||||||
return getKubeVersionFromKubectl(), nil
|
return getKubeVersionFromKubectl(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getKubeVersionFromKubectl() string {
|
func getKubeVersionFromKubectl() *KubeVersion {
|
||||||
cmd := exec.Command("kubectl", "version", "--short")
|
cmd := exec.Command("kubectl", "version", "-o", "json")
|
||||||
out, err := cmd.CombinedOutput()
|
out, err := cmd.CombinedOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.V(2).Info(err)
|
glog.V(2).Info(err)
|
||||||
@ -318,7 +319,7 @@ func getKubeVersionFromKubectl() string {
|
|||||||
return getVersionFromKubectlOutput(string(out))
|
return getVersionFromKubectlOutput(string(out))
|
||||||
}
|
}
|
||||||
|
|
||||||
func getKubeVersionFromKubelet() string {
|
func getKubeVersionFromKubelet() *KubeVersion {
|
||||||
cmd := exec.Command("kubelet", "--version")
|
cmd := exec.Command("kubelet", "--version")
|
||||||
out, err := cmd.CombinedOutput()
|
out, err := cmd.CombinedOutput()
|
||||||
|
|
||||||
@ -329,28 +330,38 @@ func getKubeVersionFromKubelet() string {
|
|||||||
return getVersionFromKubeletOutput(string(out))
|
return getVersionFromKubeletOutput(string(out))
|
||||||
}
|
}
|
||||||
|
|
||||||
func getVersionFromKubectlOutput(s string) string {
|
func getVersionFromKubectlOutput(s string) *KubeVersion {
|
||||||
serverVersionRe := regexp.MustCompile(`Server Version: v(\d+.\d+)`)
|
glog.V(2).Info(s)
|
||||||
subs := serverVersionRe.FindStringSubmatch(s)
|
type versionResult struct {
|
||||||
if len(subs) < 2 {
|
ServerVersion VersionResponse
|
||||||
|
}
|
||||||
|
vrObj := &versionResult{}
|
||||||
|
if err := json.Unmarshal([]byte(s), vrObj); err != nil {
|
||||||
|
glog.V(2).Info(err)
|
||||||
if strings.Contains(s, "The connection to the server") {
|
if strings.Contains(s, "The connection to the server") {
|
||||||
msg := fmt.Sprintf(`Warning: Kubernetes version was not auto-detected because kubectl could not connect to the Kubernetes server. This may be because the kubeconfig information is missing or has credentials that do not match the server. Assuming default version %s`, defaultKubeVersion)
|
msg := fmt.Sprintf(`Warning: Kubernetes version was not auto-detected because kubectl could not connect to the Kubernetes server. This may be because the kubeconfig information is missing or has credentials that do not match the server. Assuming default version %s`, defaultKubeVersion)
|
||||||
fmt.Fprintln(os.Stderr, msg)
|
fmt.Fprintln(os.Stderr, msg)
|
||||||
}
|
}
|
||||||
glog.V(1).Info(fmt.Sprintf("Unable to get Kubernetes version from kubectl, using default version: %s", defaultKubeVersion))
|
glog.V(1).Info(fmt.Sprintf("Unable to get Kubernetes version from kubectl, using default version: %s", defaultKubeVersion))
|
||||||
return defaultKubeVersion
|
return &KubeVersion{baseVersion: defaultKubeVersion}
|
||||||
|
}
|
||||||
|
sv := vrObj.ServerVersion
|
||||||
|
return &KubeVersion{
|
||||||
|
Major: sv.Major,
|
||||||
|
Minor: sv.Minor,
|
||||||
|
GitVersion: sv.GitVersion,
|
||||||
}
|
}
|
||||||
return subs[1]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getVersionFromKubeletOutput(s string) string {
|
func getVersionFromKubeletOutput(s string) *KubeVersion {
|
||||||
|
glog.V(2).Info(s)
|
||||||
serverVersionRe := regexp.MustCompile(`Kubernetes v(\d+.\d+)`)
|
serverVersionRe := regexp.MustCompile(`Kubernetes v(\d+.\d+)`)
|
||||||
subs := serverVersionRe.FindStringSubmatch(s)
|
subs := serverVersionRe.FindStringSubmatch(s)
|
||||||
if len(subs) < 2 {
|
if len(subs) < 2 {
|
||||||
glog.V(1).Info(fmt.Sprintf("Unable to get Kubernetes version from kubelet, using default version: %s", defaultKubeVersion))
|
glog.V(1).Info(fmt.Sprintf("Unable to get Kubernetes version from kubelet, using default version: %s", defaultKubeVersion))
|
||||||
return defaultKubeVersion
|
return &KubeVersion{baseVersion: defaultKubeVersion}
|
||||||
}
|
}
|
||||||
return subs[1]
|
return &KubeVersion{baseVersion: subs[1]}
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeSubstitutions(s string, ext string, m map[string]string) (string, []string) {
|
func makeSubstitutions(s string, ext string, m map[string]string) (string, []string) {
|
||||||
@ -408,3 +419,31 @@ These program names are provided in the config.yaml, section '%s.%s.bins'
|
|||||||
|
|
||||||
return fmt.Sprintf(errMessageTemplate, component, componentRoleName, binList, componentType, component)
|
return fmt.Sprintf(errMessageTemplate, component, componentRoleName, binList, componentType, component)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getPlatformName() string {
|
||||||
|
kv, err := getKubeVersion()
|
||||||
|
if err != nil {
|
||||||
|
glog.V(2).Info(err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return getPlatformNameFromVersion(kv.GitVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPlatformNameFromVersion(s string) string {
|
||||||
|
versionRe := regexp.MustCompile(`v\d+\.\d+\.\d+-(\w+)(?:[.\-])\w+`)
|
||||||
|
subs := versionRe.FindStringSubmatch(s)
|
||||||
|
if len(subs) < 2 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return subs[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPlatformBenchmarkVersion(platform string) string {
|
||||||
|
switch platform {
|
||||||
|
case "eks":
|
||||||
|
return "eks-1.0"
|
||||||
|
case "gke":
|
||||||
|
return "gke-1.0"
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
106
cmd/util_test.go
106
cmd/util_test.go
@ -202,17 +202,21 @@ func TestMultiWordReplace(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestKubeVersionRegex(t *testing.T) {
|
func Test_getVersionFromKubectlOutput(t *testing.T) {
|
||||||
ver := getVersionFromKubectlOutput(`Client Version: v1.8.0
|
ver := getVersionFromKubectlOutput(`{
|
||||||
Server Version: v1.8.12
|
"serverVersion": {
|
||||||
`)
|
"major": "1",
|
||||||
if ver != "1.8" {
|
"minor": "8",
|
||||||
t.Fatalf("Expected 1.8 got %s", ver)
|
"gitVersion": "v1.8.0"
|
||||||
|
}
|
||||||
|
}`)
|
||||||
|
if ver.BaseVersion() != "1.8" {
|
||||||
|
t.Fatalf("Expected 1.8 got %s", ver.BaseVersion())
|
||||||
}
|
}
|
||||||
|
|
||||||
ver = getVersionFromKubectlOutput("Something completely different")
|
ver = getVersionFromKubectlOutput("Something completely different")
|
||||||
if ver != defaultKubeVersion {
|
if ver.BaseVersion() != defaultKubeVersion {
|
||||||
t.Fatalf("Expected %s got %s", defaultKubeVersion, ver)
|
t.Fatalf("Expected %s got %s", defaultKubeVersion, ver.BaseVersion())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -512,3 +516,89 @@ func TestGetYamlFilesFromDir(t *testing.T) {
|
|||||||
t.Fatalf("Expected to find something.yaml, found %s", files[0])
|
t.Fatalf("Expected to find something.yaml, found %s", files[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_getPlatformNameFromKubectlOutput(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
s string
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "eks",
|
||||||
|
args: args{s: "v1.17.9-eks-4c6976"},
|
||||||
|
want: "eks",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "gke",
|
||||||
|
args: args{s: "v1.17.6-gke.1"},
|
||||||
|
want: "gke",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "unknown",
|
||||||
|
args: args{s: "v1.17.6"},
|
||||||
|
want: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty string",
|
||||||
|
args: args{s: ""},
|
||||||
|
want: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if got := getPlatformNameFromVersion(tt.args.s); got != tt.want {
|
||||||
|
t.Errorf("getPlatformNameFromKubectlOutput() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_getPlatformBenchmarkVersion(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
platform string
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "eks",
|
||||||
|
args: args{
|
||||||
|
platform: "eks",
|
||||||
|
},
|
||||||
|
want: "eks-1.0",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "gke",
|
||||||
|
args: args{
|
||||||
|
platform: "gke",
|
||||||
|
},
|
||||||
|
want: "gke-1.0",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "unknown",
|
||||||
|
args: args{
|
||||||
|
platform: "rh",
|
||||||
|
},
|
||||||
|
want: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty",
|
||||||
|
args: args{
|
||||||
|
platform: "",
|
||||||
|
},
|
||||||
|
want: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if got := getPlatformBenchmarkVersion(tt.args.platform); got != tt.want {
|
||||||
|
t.Errorf("getPlatformBenchmarkVersion() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user