2019-11-16 14:39:47 +00:00
|
|
|
// +build integration
|
|
|
|
|
|
|
|
package integration
|
|
|
|
|
|
|
|
import (
|
2020-01-21 15:36:04 +00:00
|
|
|
"bufio"
|
|
|
|
"bytes"
|
2019-11-16 14:39:47 +00:00
|
|
|
"flag"
|
|
|
|
"fmt"
|
|
|
|
"io/ioutil"
|
|
|
|
"strings"
|
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
var kubebenchImg = flag.String("kubebenchImg", "aquasec/kube-bench:latest", "kube-bench image used as part of this test")
|
2020-01-09 14:57:40 +00:00
|
|
|
var timeout = flag.Duration("timeout", 10*time.Minute, "Test Timeout")
|
2019-11-16 14:39:47 +00:00
|
|
|
|
2020-05-20 17:30:52 +00:00
|
|
|
func testCheckCISWithKind(t *testing.T, testdataDir string) {
|
2019-11-16 14:39:47 +00:00
|
|
|
flag.Parse()
|
|
|
|
fmt.Printf("kube-bench Container Image: %s\n", *kubebenchImg)
|
|
|
|
|
|
|
|
cases := []struct {
|
|
|
|
TestName string
|
|
|
|
KubebenchYAML string
|
|
|
|
ExpectedFile string
|
|
|
|
ExpectError bool
|
|
|
|
}{
|
|
|
|
{
|
2020-01-09 14:57:40 +00:00
|
|
|
TestName: "kube-bench",
|
2019-11-16 14:39:47 +00:00
|
|
|
KubebenchYAML: "../job.yaml",
|
2020-05-20 17:30:52 +00:00
|
|
|
ExpectedFile: fmt.Sprintf("./testdata/%s/job.data", testdataDir),
|
2019-11-16 14:39:47 +00:00
|
|
|
},
|
|
|
|
{
|
2020-01-09 14:57:40 +00:00
|
|
|
TestName: "kube-bench-node",
|
2019-11-16 14:39:47 +00:00
|
|
|
KubebenchYAML: "../job-node.yaml",
|
2020-05-20 17:30:52 +00:00
|
|
|
ExpectedFile: fmt.Sprintf("./testdata/%s/job-node.data", testdataDir),
|
2019-11-16 14:39:47 +00:00
|
|
|
},
|
|
|
|
{
|
2020-01-09 14:57:40 +00:00
|
|
|
TestName: "kube-bench-master",
|
2019-11-16 14:39:47 +00:00
|
|
|
KubebenchYAML: "../job-master.yaml",
|
2020-05-20 17:30:52 +00:00
|
|
|
ExpectedFile: fmt.Sprintf("./testdata/%s/job-master.data", testdataDir),
|
2019-11-16 14:39:47 +00:00
|
|
|
},
|
|
|
|
}
|
2020-05-20 17:30:52 +00:00
|
|
|
ctx, err := setupCluster("kube-bench", fmt.Sprintf("./testdata/%s/add-tls-kind.yaml", testdataDir), *timeout)
|
2020-01-09 14:57:40 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("failed to setup KIND cluster error: %v", err)
|
|
|
|
}
|
|
|
|
defer func() {
|
|
|
|
ctx.Delete()
|
|
|
|
}()
|
|
|
|
|
|
|
|
if err := loadImageFromDocker(*kubebenchImg, ctx); err != nil {
|
|
|
|
t.Fatalf("failed to load kube-bench image from Docker to KIND error: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
clientset, err := getClientSet(ctx.KubeConfigPath())
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("failed to connect to Kubernetes cluster error: %v", err)
|
|
|
|
}
|
|
|
|
|
2019-11-16 14:39:47 +00:00
|
|
|
for _, c := range cases {
|
|
|
|
t.Run(c.TestName, func(t *testing.T) {
|
2020-01-09 14:57:40 +00:00
|
|
|
resultData, err := runWithKind(ctx, clientset, c.TestName, c.KubebenchYAML, *kubebenchImg, *timeout)
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("unexpected error: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
c, err := ioutil.ReadFile(c.ExpectedFile)
|
2019-11-16 14:39:47 +00:00
|
|
|
if err != nil {
|
2020-01-09 14:57:40 +00:00
|
|
|
t.Error(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
expectedData := strings.TrimSpace(string(c))
|
|
|
|
resultData = strings.TrimSpace(resultData)
|
|
|
|
if expectedData != resultData {
|
2020-01-21 15:36:04 +00:00
|
|
|
t.Errorf("expected results\n\nExpected\t(<)\nResult\t(>)\n\n%s\n\n", generateDiff(expectedData, resultData))
|
2019-11-16 14:39:47 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2020-01-21 15:36:04 +00:00
|
|
|
|
2020-09-17 15:54:43 +00:00
|
|
|
func TestCheckCIS16WithKind(t *testing.T) {
|
|
|
|
testCheckCISWithKind(t, "cis-1.6")
|
|
|
|
}
|
|
|
|
|
2021-06-16 17:55:04 +00:00
|
|
|
func TestCheckCIS120WithKind(t *testing.T) {
|
|
|
|
testCheckCISWithKind(t, "cis-1.20")
|
|
|
|
}
|
|
|
|
|
2020-01-21 15:36:04 +00:00
|
|
|
// This is simple "diff" between 2 strings containing multiple lines.
|
|
|
|
// It's not a comprehensive diff between the 2 strings.
|
|
|
|
// It does not inditcate when lines are deleted.
|
|
|
|
func generateDiff(source, target string) string {
|
|
|
|
buf := new(bytes.Buffer)
|
|
|
|
ss := bufio.NewScanner(strings.NewReader(source))
|
|
|
|
ts := bufio.NewScanner(strings.NewReader(target))
|
|
|
|
|
|
|
|
emptySource := false
|
|
|
|
emptyTarget := false
|
|
|
|
|
|
|
|
loop:
|
|
|
|
for ln := 1; ; ln++ {
|
|
|
|
var ll, rl string
|
|
|
|
|
|
|
|
sourceScan := ss.Scan()
|
|
|
|
if sourceScan {
|
|
|
|
ll = ss.Text()
|
|
|
|
}
|
|
|
|
|
|
|
|
targetScan := ts.Scan()
|
|
|
|
if targetScan {
|
|
|
|
rl = ts.Text()
|
|
|
|
}
|
|
|
|
|
|
|
|
switch {
|
|
|
|
case !sourceScan && !targetScan:
|
|
|
|
// no more lines
|
|
|
|
break loop
|
|
|
|
case sourceScan && targetScan:
|
|
|
|
if ll != rl {
|
|
|
|
fmt.Fprintf(buf, "line: %d\n", ln)
|
|
|
|
fmt.Fprintf(buf, "< %s\n", ll)
|
|
|
|
fmt.Fprintf(buf, "> %s\n", rl)
|
|
|
|
}
|
|
|
|
case !targetScan:
|
|
|
|
if !emptyTarget {
|
|
|
|
fmt.Fprintf(buf, "line: %d\n", ln)
|
|
|
|
}
|
|
|
|
fmt.Fprintf(buf, "< %s\n", ll)
|
|
|
|
emptyTarget = true
|
|
|
|
case !sourceScan:
|
|
|
|
if !emptySource {
|
|
|
|
fmt.Fprintf(buf, "line: %d\n", ln)
|
|
|
|
}
|
|
|
|
fmt.Fprintf(buf, "> %s\n", rl)
|
|
|
|
emptySource = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if emptySource {
|
|
|
|
fmt.Fprintf(buf, "< [[NO MORE DATA]]")
|
|
|
|
}
|
|
|
|
|
|
|
|
if emptyTarget {
|
|
|
|
fmt.Fprintf(buf, "> [[NO MORE DATA]]")
|
|
|
|
}
|
|
|
|
|
|
|
|
return buf.String()
|
|
|
|
}
|