From 6dd690cbec9ee6f9cb7f98c94d3b51b85cece329 Mon Sep 17 00:00:00 2001 From: Edouard Schweisguth Date: Wed, 14 Jun 2023 13:39:48 +0200 Subject: [PATCH 01/25] WIP --- .gitignore | 4 + pkg/kubehound/graph/vertex/container.go | 4 +- pkg/kubehound/graph/vertex/identity.go | 4 +- pkg/kubehound/graph/vertex/node.go | 4 +- pkg/kubehound/graph/vertex/pod.go | 4 +- pkg/kubehound/graph/vertex/role.go | 4 +- pkg/kubehound/graph/vertex/token.go | 4 +- pkg/kubehound/graph/vertex/volume.go | 4 +- test/setup/create-cluster-resources.sh | 1 + test/setup/destroy-cluster.sh | 1 + test/setup/setup-cluster.sh | 1 + test/system/graph_vertex_test.go | 109 ++++++++++++++++++++++++ 12 files changed, 130 insertions(+), 14 deletions(-) create mode 100644 test/system/graph_vertex_test.go diff --git a/.gitignore b/.gitignore index 5af966a69..cd77a8687 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,7 @@ deployments/kubehound/data deployments/kubehound/data/* # System test json files test/**/*.json + + +# kind config +test/setup/.kube \ No newline at end of file diff --git a/pkg/kubehound/graph/vertex/container.go b/pkg/kubehound/graph/vertex/container.go index cc6eb00b2..1b82c0495 100644 --- a/pkg/kubehound/graph/vertex/container.go +++ b/pkg/kubehound/graph/vertex/container.go @@ -7,7 +7,7 @@ import ( ) const ( - containerLabel = "Container" + ContainerLabel = "Container" ) var _ Builder = (*Container)(nil) @@ -16,7 +16,7 @@ type Container struct { } func (v Container) Label() string { - return containerLabel + return ContainerLabel } func (v Container) BatchSize() int { diff --git a/pkg/kubehound/graph/vertex/identity.go b/pkg/kubehound/graph/vertex/identity.go index 4fa0f043d..7eab15434 100644 --- a/pkg/kubehound/graph/vertex/identity.go +++ b/pkg/kubehound/graph/vertex/identity.go @@ -7,7 +7,7 @@ import ( ) const ( - identityLabel = "Identity" + IdentityLabel = "Identity" ) var _ Builder = (*Identity)(nil) @@ -16,7 +16,7 @@ type Identity struct { } func (v Identity) Label() string { - return identityLabel + return IdentityLabel } func (v Identity) BatchSize() int { diff --git a/pkg/kubehound/graph/vertex/node.go b/pkg/kubehound/graph/vertex/node.go index 207b41ccc..d5a714c29 100644 --- a/pkg/kubehound/graph/vertex/node.go +++ b/pkg/kubehound/graph/vertex/node.go @@ -7,7 +7,7 @@ import ( ) const ( - nodeLabel = "Node" + NodeLabel = "Node" ) var _ Builder = (*Node)(nil) @@ -16,7 +16,7 @@ type Node struct { } func (v Node) Label() string { - return nodeLabel + return NodeLabel } func (v Node) BatchSize() int { diff --git a/pkg/kubehound/graph/vertex/pod.go b/pkg/kubehound/graph/vertex/pod.go index e7f7ae79c..1806203e3 100644 --- a/pkg/kubehound/graph/vertex/pod.go +++ b/pkg/kubehound/graph/vertex/pod.go @@ -7,7 +7,7 @@ import ( ) const ( - podLabel = "Pod" + PodLabel = "Pod" ) var _ Builder = (*Pod)(nil) @@ -16,7 +16,7 @@ type Pod struct { } func (v Pod) Label() string { - return podLabel + return PodLabel } func (v Pod) BatchSize() int { diff --git a/pkg/kubehound/graph/vertex/role.go b/pkg/kubehound/graph/vertex/role.go index f9523b19d..9b4f87088 100644 --- a/pkg/kubehound/graph/vertex/role.go +++ b/pkg/kubehound/graph/vertex/role.go @@ -8,7 +8,7 @@ import ( ) const ( - roleLabel = "Role" + RoleLabel = "Role" ) var _ Builder = (*Role)(nil) @@ -17,7 +17,7 @@ type Role struct { } func (v Role) Label() string { - return roleLabel + return RoleLabel } func (v Role) BatchSize() int { diff --git a/pkg/kubehound/graph/vertex/token.go b/pkg/kubehound/graph/vertex/token.go index a00943c11..f29cf5462 100644 --- a/pkg/kubehound/graph/vertex/token.go +++ b/pkg/kubehound/graph/vertex/token.go @@ -1,12 +1,12 @@ package vertex const ( - tokenLabel = "Token" + TokenLabel = "Token" ) type Token struct { } func (v Token) Label() string { - return tokenLabel + return TokenLabel } diff --git a/pkg/kubehound/graph/vertex/volume.go b/pkg/kubehound/graph/vertex/volume.go index e00a34ce2..80f82a101 100644 --- a/pkg/kubehound/graph/vertex/volume.go +++ b/pkg/kubehound/graph/vertex/volume.go @@ -7,7 +7,7 @@ import ( ) const ( - volumeLabel = "Volume" + VolumeLabel = "Volume" ) var _ Builder = (*Volume)(nil) @@ -16,7 +16,7 @@ type Volume struct { } func (v Volume) Label() string { - return volumeLabel + return VolumeLabel } func (v Volume) BatchSize() int { diff --git a/test/setup/create-cluster-resources.sh b/test/setup/create-cluster-resources.sh index 27266be32..969364ac3 100755 --- a/test/setup/create-cluster-resources.sh +++ b/test/setup/create-cluster-resources.sh @@ -3,6 +3,7 @@ set -e CLUSTER_NAME=kubehound.test.local CONFIG_DIR=./test-cluster +export KUBECONFIG=.kube/config echo "[*] Deploying test resources via kubectl apply" for attack in ${CONFIG_DIR}/attacks/*.yaml; do diff --git a/test/setup/destroy-cluster.sh b/test/setup/destroy-cluster.sh index 4bbd14c00..04397692c 100755 --- a/test/setup/destroy-cluster.sh +++ b/test/setup/destroy-cluster.sh @@ -2,6 +2,7 @@ set -e CLUSTER_NAME=kubehound.test.local +export KUBECONFIG=.kube/config echo "[*] Destroying test cluster "${CLUSTER_NAME}" via kind" kind delete cluster --name "${CLUSTER_NAME}" \ No newline at end of file diff --git a/test/setup/setup-cluster.sh b/test/setup/setup-cluster.sh index ebe5d6bfb..6eddca9b7 100755 --- a/test/setup/setup-cluster.sh +++ b/test/setup/setup-cluster.sh @@ -3,6 +3,7 @@ set -e CLUSTER_NAME=kubehound.test.local CONFIG_DIR=./test-cluster +export KUBECONFIG=.kube/config echo "[*] Creating test cluster "${CLUSTER_NAME}" via kind" kind create cluster \ diff --git a/test/system/graph_vertex_test.go b/test/system/graph_vertex_test.go new file mode 100644 index 000000000..6f6e13d46 --- /dev/null +++ b/test/system/graph_vertex_test.go @@ -0,0 +1,109 @@ +package system + +import ( + "context" + "testing" + + "github.com/DataDog/KubeHound/pkg/config" + "github.com/DataDog/KubeHound/pkg/kubehound/graph/vertex" + "github.com/DataDog/KubeHound/pkg/kubehound/storage/graphdb" + gremlingo "github.com/apache/tinkerpop/gremlin-go/driver" + "github.com/stretchr/testify/suite" +) + +// Optional syntactic sugar. +var __ = gremlingo.T__ + +type VertexTestSuite struct { + suite.Suite + gdb graphdb.Provider + client *gremlingo.DriverRemoteConnection +} + +func (suite *VertexTestSuite) SetupTest() { + gdb, err := graphdb.Factory(context.Background(), config.MustLoadConfig("./kubehound.yaml")) + suite.NoError(err) + suite.gdb = gdb + suite.client = gdb.Raw().(*gremlingo.DriverRemoteConnection) + err = runKubeHound() + suite.NoError(err) +} + +func (suite *VertexTestSuite) TestVertexContainer() { + g := gremlingo.Traversal_().WithRemote(suite.client) + results, err := g.V().HasLabel(vertex.ContainerLabel).ElementMap().ToList() + suite.NoError(err) + suite.T().Errorf("results: %s", results) + for _, res := range results { + suite.T().Errorf("res: %s", res.String()) + } +} + +func (suite *VertexTestSuite) TestVertexIdentity() { + g := gremlingo.Traversal_().WithRemote(suite.client) + results, err := g.V().HasLabel(vertex.IdentityLabel).ElementMap().ToList() + suite.NoError(err) + suite.T().Errorf("results: %s", results) + for _, res := range results { + suite.T().Errorf("res: %s", res.String()) + } +} + +func (suite *VertexTestSuite) TestVertexNode() { + g := gremlingo.Traversal_().WithRemote(suite.client) + results, err := g.V().HasLabel(vertex.NodeLabel).ElementMap().ToList() + suite.NoError(err) + suite.T().Errorf("results: %s", results) + + suite.Equal(3, len(results)) + expectedNodeNames := map[string]bool{ + "kubehound.test.local-control-plane": true, + "kubehound.test.local-worker": true, + "kubehound.test.local-worker2": true, + } + + for _, res := range results { + suite.T().Errorf("res: %s", res) + // cast me + // resConverted := ?? + suite.Contains(expectedNodeNames, resConverted.Name) + } +} + +func (suite *VertexTestSuite) TestVertexPod() { + g := gremlingo.Traversal_().WithRemote(suite.client) + results, err := g.V().HasLabel(vertex.PodLabel).ElementMap().ToList() + suite.NoError(err) + suite.T().Errorf("results: %s", results) + for _, res := range results { + suite.T().Errorf("res: %s", res.String()) + } +} + +func (suite *VertexTestSuite) TestVertexRole() { + g := gremlingo.Traversal_().WithRemote(suite.client) + results, err := g.V().HasLabel(vertex.RoleLabel).ElementMap().ToList() + suite.NoError(err) + suite.T().Errorf("results: %s", results) + for _, res := range results { + suite.T().Errorf("res: %s", res.String()) + } +} + +func (suite *VertexTestSuite) TestVertexVolume() { + g := gremlingo.Traversal_().WithRemote(suite.client) + results, err := g.V().HasLabel(vertex.VolumeLabel).ElementMap().ToList() + suite.NoError(err) + suite.T().Errorf("results: %s", results) + for _, res := range results { + suite.T().Errorf("res: %s", res.String()) + } +} + +func TestVertexTestSuite(t *testing.T) { + suite.Run(t, new(VertexTestSuite)) +} + +func (suite *VertexTestSuite) TearDownTest() { + suite.gdb.Close(context.Background()) +} From a16da06a563c2f2bdb471b0672c55ae0eb2f8827 Mon Sep 17 00:00:00 2001 From: Edouard Schweisguth Date: Wed, 14 Jun 2023 21:37:06 +0200 Subject: [PATCH 02/25] WIP lol --- .gitignore | 5 +- test/system/generator/generator.go | 271 +++++++++++++++++++++++++++++ test/system/graph_vertex_test.go | 199 +++++++++++++++------ 3 files changed, 425 insertions(+), 50 deletions(-) create mode 100644 test/system/generator/generator.go diff --git a/.gitignore b/.gitignore index cd77a8687..fa0639373 100644 --- a/.gitignore +++ b/.gitignore @@ -35,4 +35,7 @@ test/**/*.json # kind config -test/setup/.kube \ No newline at end of file +test/setup/.kube + +# binary for the autogen of fixtures +test/system/generator/generator \ No newline at end of file diff --git a/test/system/generator/generator.go b/test/system/generator/generator.go new file mode 100644 index 000000000..18da7a59f --- /dev/null +++ b/test/system/generator/generator.go @@ -0,0 +1,271 @@ +package main + +import ( + "bytes" + "fmt" + "io/ioutil" + "log" + "os" + "path/filepath" + "text/template" + + "github.com/DataDog/KubeHound/pkg/kubehound/models/converter" + "github.com/DataDog/KubeHound/pkg/kubehound/models/graph" + "github.com/DataDog/KubeHound/pkg/kubehound/models/store" + "gopkg.in/yaml.v3" + corev1 "k8s.io/api/core/v1" + v1 "k8s.io/api/core/v1" + "k8s.io/api/rbac/v1beta1" + + "k8s.io/client-go/kubernetes/scheme" +) + +type Cluster struct { + Kind string `yaml:"kind"` + APIVersion string `yaml:"apiVersion"` + Name string `yaml:"name"` + Nodes []struct { + Role string `yaml:"role"` + } `yaml:"nodes"` +} + +var ( + Containers = make(map[string]graph.Container) + Pods = make(map[string]graph.Pod) + Nodes = make(map[string]graph.Node) + Roles = make(map[string]graph.Role) + Volumes = make(map[string]graph.Volume) +) + +var ( + globalHeaders = []byte(` +// PLEASE DO NOT EDIT +// THIS HAS BEEN GENERATED AUTOMATICALLY +// +// FIXME: +// cd test/system/generator && go build && ./generator ../../setup/test-cluster/ + +package system + +import ( + "github.com/DataDog/KubeHound/pkg/kubehound/models/graph" + "github.com/DataDog/KubeHound/pkg/kubehound/models/shared" +) + +`) +) + +func main() { + // todo: check length + path := os.Args[1] + + clusterFile, err := ioutil.ReadFile(filepath.Join(path, "cluster.yaml")) + if err != nil { + log.Fatal(err) + } + err = ProcessCluster(clusterFile) + if err != nil { + log.Fatal(err) + } + + attackPath := filepath.Join(path, "attacks") + filesAttack, err := ioutil.ReadDir(attackPath) + if err != nil { + log.Fatal(err) + } + for _, file := range filesAttack { + ProcessFile(attackPath, file) + } + + fmt.Printf("Pod stored: %+v\n", Pods) + fmt.Printf("Node stored: %+v\n", Nodes) + fmt.Printf("Volume stored: %+v\n", Volumes) + outPods, err := GeneratePodTemplate() + if err != nil { + fmt.Println("failed to write pods: ", err) + } + outNodes, err := GenerateNodeTemplate() + if err != nil { + fmt.Println("failed to write pods: ", err) + } + WriteTemplatesToFile("outfile.gen.go", globalHeaders, outPods, outNodes) +} + +func ProcessCluster(content []byte) error { + var cluster Cluster + err := yaml.Unmarshal(content, &cluster) + if err != nil { + return err + } + for _, n := range cluster.Nodes { + node := n.Role + Nodes[node] = graph.Node{ + StoreID: "", + Name: node, + IsNamespaced: false, + Namespace: "", + Compromised: 0, + Critical: false, + } + } + return nil +} + +func ProcessFile(basePath string, file os.FileInfo) { + fmt.Println(file.Name(), file.IsDir()) + data, err := os.ReadFile(filepath.Join(basePath, file.Name())) + if err != nil { + fmt.Printf("failed to read file: %v", err) + return + } + decode := scheme.Codecs.UniversalDeserializer().Decode + obj, _, err := decode(data, nil, nil) + if err != nil { + fmt.Println("Error while decoding YAML object. Err was: ", err) + return + } + + // now use switch over the type of the object + // and match each type-case + switch o := obj.(type) { + case *v1.Node: + AddNodeToList(o) + case *v1.Pod: + AddPodToList(o) + for _, vol := range o.Spec.Volumes { + p := store.Pod{ + K8: *o, + } + AddVolumeToList(&vol, &p) + } + case *v1beta1.Role: + // TODO + case *v1beta1.RoleBinding: + // TODO + case *v1beta1.ClusterRole: + // TODO + case *v1beta1.ClusterRoleBinding: + // TODO + case *v1.ServiceAccount: + default: + fmt.Printf("Unknown object type: %+v\n", o) + //o is unknown for us + } +} + +func AddPodToList(pod *corev1.Pod) error { + fmt.Printf("pod name: %s\n", pod.Name) + storePod := store.Pod{ + K8: *pod, + } + conv := converter.GraphConverter{} + convertedPod, _ := conv.Pod(&storePod) + Pods[pod.Name] = *convertedPod + + return nil +} + +func AddNodeToList(node *corev1.Node) error { + fmt.Printf("Node name: %s\n", node.Name) + storeNode := store.Node{ + K8: *node, + } + conv := converter.GraphConverter{} + convertedNode, _ := conv.Node(&storeNode) + Nodes[node.Name] = *convertedNode + + return nil +} + +func AddVolumeToList(volume *corev1.Volume, storePod *store.Pod) error { + fmt.Printf("Volume name: %s\n", volume.Name) + storeVolume := store.Volume{ + Source: *volume, + } + conv := converter.GraphConverter{} + convertedVolume, _ := conv.Volume(&storeVolume, storePod) + Volumes[volume.Name] = *convertedVolume + + return nil +} + +func GenerateNodeTemplate() ([]byte, error) { + tmpl := `var expectedNodeNames = map[string]graph.Node{ + {{range $val := . -}} + "{{.Name}}": { + StoreID: "", + Name: "{{.Name}}", + IsNamespaced: {{.IsNamespaced}}, + Namespace: "{{.Namespace}}", + Compromised: shared.CompromiseNone, + Critical: false, + }, + {{- end}} + } +` + + t := template.Must(template.New("tmpl").Parse(tmpl)) + outbuf := bytes.NewBuffer([]byte{}) + t.Execute(outbuf, Nodes) + + return outbuf.Bytes(), nil +} + +func GeneratePodTemplate() ([]byte, error) { + tmpl := `var expectedPodNames = map[string]graph.Pod{ + {{range $val := . -}} + "{{.Name}}": { + StoreID: "", + Name: "{{.Name}}", + IsNamespaced: {{.IsNamespaced}}, + Namespace: "{{.Namespace}}", + Compromised: shared.CompromiseNone, + Critical: false, + },{{ end }} + } +` + + t := template.Must(template.New("tmpl").Parse(tmpl)) + outbuf := bytes.NewBuffer([]byte{}) + fmt.Println("seen pods total:", len(Pods)) + t.Execute(outbuf, Pods) + + return outbuf.Bytes(), nil +} + +func GenerateContainerTemplate() ([]byte, error) { + tmpl := `var expectedContainerNames = map[string]graph.Container{ + {{range $val := .}} + "{{.Name}}": { + StoreID: "", + Name: "{{.Name}}", + IsNamespaced: {{.IsNamespaced}}, + Namespace: "{{.Namespace}}", + Compromised: shared.CompromiseNone, + Critical: false, + }, + {{end}} + } +` + + t := template.Must(template.New("tmpl").Parse(tmpl)) + outbuf := bytes.NewBuffer([]byte{}) + t.Execute(outbuf, Nodes) + + return outbuf.Bytes(), nil +} + +func WriteTemplatesToFile(path string, templates ...[]byte) error { + f, err := os.Create(path) + if err != nil { + return err + } + + for _, t := range templates { + _, err := f.Write(t) + if err != nil { + return err + } + } + return nil +} diff --git a/test/system/graph_vertex_test.go b/test/system/graph_vertex_test.go index 6f6e13d46..d63ce7d1d 100644 --- a/test/system/graph_vertex_test.go +++ b/test/system/graph_vertex_test.go @@ -6,6 +6,8 @@ import ( "github.com/DataDog/KubeHound/pkg/config" "github.com/DataDog/KubeHound/pkg/kubehound/graph/vertex" + "github.com/DataDog/KubeHound/pkg/kubehound/models/graph" + "github.com/DataDog/KubeHound/pkg/kubehound/models/shared" "github.com/DataDog/KubeHound/pkg/kubehound/storage/graphdb" gremlingo "github.com/apache/tinkerpop/gremlin-go/driver" "github.com/stretchr/testify/suite" @@ -25,81 +27,180 @@ func (suite *VertexTestSuite) SetupTest() { suite.NoError(err) suite.gdb = gdb suite.client = gdb.Raw().(*gremlingo.DriverRemoteConnection) - err = runKubeHound() - suite.NoError(err) + // g := gremlingo.Traversal_().WithRemote(suite.client) + // errChan := g.V().Drop().Iterate() + // err = <-errChan + // if err != nil { + // suite.Errorf(err, "error deleting the graphdb:\n") + // } + // err = runKubeHound() + // suite.NoError(err) } func (suite *VertexTestSuite) TestVertexContainer() { g := gremlingo.Traversal_().WithRemote(suite.client) results, err := g.V().HasLabel(vertex.ContainerLabel).ElementMap().ToList() suite.NoError(err) - suite.T().Errorf("results: %s", results) - for _, res := range results { - suite.T().Errorf("res: %s", res.String()) + + expectedContainers := map[string]graph.Container{ + "netadmin-pod": { + Name: "netadmin-pod", + Compromised: shared.CompromiseNone, + Critical: false, + }, + "pod-create-pod": { + Name: "pod-create-pod", + Compromised: shared.CompromiseNone, + Critical: false, + }, + "priv-pod": { + Name: "priv-pod", + Compromised: shared.CompromiseNone, + Critical: false, + }, + "kube-apiserver": { + Name: "kube-apiserver", + Compromised: shared.CompromiseNone, + Critical: false, + }, + "kube-proxy": { + Name: "kube-proxy", + Compromised: shared.CompromiseNone, + Critical: false, + }, + "impersonate-pod": { + Name: "impersonate-pod", + Compromised: shared.CompromiseNone, + Critical: false, + }, } -} -func (suite *VertexTestSuite) TestVertexIdentity() { - g := gremlingo.Traversal_().WithRemote(suite.client) - results, err := g.V().HasLabel(vertex.IdentityLabel).ElementMap().ToList() - suite.NoError(err) - suite.T().Errorf("results: %s", results) + suite.Equal(len(expectedContainers), len(results)) + resultsMap := map[string]graph.Container{} for _, res := range results { - suite.T().Errorf("res: %s", res.String()) + res := res.GetInterface() + converted := res.(map[any]any) + + nodeName, ok := converted["name"].(string) + suite.True(ok, "failed to convert node name to string") + + compromised, ok := converted["compromised"].(int) + suite.True(ok, "failed to convert compromised field to CompromiseType") + + critical, ok := converted["critical"].(bool) + suite.True(ok, "failed to convert critical field to bool") + + resultsMap[nodeName] = graph.Container{ + Name: nodeName, + Compromised: shared.CompromiseType(compromised), + Critical: critical, + } } + suite.Equal(expectedContainers, resultsMap) } +// func (suite *VertexTestSuite) TestVertexIdentity() { +// g := gremlingo.Traversal_().WithRemote(suite.client) +// results, err := g.V().HasLabel(vertex.IdentityLabel).ElementMap().ToList() +// suite.NoError(err) +// suite.T().Errorf("results: %s", results) +// for _, res := range results { +// suite.T().Errorf("res: %s", res.String()) +// } +// } + func (suite *VertexTestSuite) TestVertexNode() { g := gremlingo.Traversal_().WithRemote(suite.client) results, err := g.V().HasLabel(vertex.NodeLabel).ElementMap().ToList() suite.NoError(err) - suite.T().Errorf("results: %s", results) - suite.Equal(3, len(results)) - expectedNodeNames := map[string]bool{ - "kubehound.test.local-control-plane": true, - "kubehound.test.local-worker": true, - "kubehound.test.local-worker2": true, + expectedNodeNames := map[string]graph.Node{ + "kubehound.test.local-control-plane": { + StoreID: "", + Name: "kubehound.test.local-control-plane", + IsNamespaced: false, + Namespace: "", + Compromised: shared.CompromiseNone, + Critical: false, + }, + "kubehound.test.local-worker": { + Name: "kubehound.test.local-worker", + IsNamespaced: false, + Namespace: "", + Compromised: shared.CompromiseNone, + Critical: false, + }, + "kubehound.test.local-worker2": { + Name: "kubehound.test.local-worker2", + IsNamespaced: false, + Namespace: "", + Compromised: shared.CompromiseNone, + Critical: false, + }, } + suite.Equal(len(expectedNodeNames), len(results)) + resultsMap := map[string]graph.Node{} for _, res := range results { - suite.T().Errorf("res: %s", res) - // cast me - // resConverted := ?? - suite.Contains(expectedNodeNames, resConverted.Name) - } -} + res := res.GetInterface() + converted := res.(map[any]any) -func (suite *VertexTestSuite) TestVertexPod() { - g := gremlingo.Traversal_().WithRemote(suite.client) - results, err := g.V().HasLabel(vertex.PodLabel).ElementMap().ToList() - suite.NoError(err) - suite.T().Errorf("results: %s", results) - for _, res := range results { - suite.T().Errorf("res: %s", res.String()) - } -} + nodeName, ok := converted["name"].(string) + suite.True(ok, "failed to convert node name to string") -func (suite *VertexTestSuite) TestVertexRole() { - g := gremlingo.Traversal_().WithRemote(suite.client) - results, err := g.V().HasLabel(vertex.RoleLabel).ElementMap().ToList() - suite.NoError(err) - suite.T().Errorf("results: %s", results) - for _, res := range results { - suite.T().Errorf("res: %s", res.String()) - } -} + compromised, ok := converted["compromised"].(int) + suite.True(ok, "failed to convert compromised field to CompromiseType") -func (suite *VertexTestSuite) TestVertexVolume() { - g := gremlingo.Traversal_().WithRemote(suite.client) - results, err := g.V().HasLabel(vertex.VolumeLabel).ElementMap().ToList() - suite.NoError(err) - suite.T().Errorf("results: %s", results) - for _, res := range results { - suite.T().Errorf("res: %s", res.String()) + isNamespaced, ok := converted["isNamespaced"].(bool) + suite.True(ok, "failed to convert isNamespaced field to bool") + + namespace, ok := converted["namespace"].(string) + suite.True(ok, "failed to convert namespace field to string") + + critical, ok := converted["critical"].(bool) + suite.True(ok, "failed to convert critical field to bool") + + resultsMap[nodeName] = graph.Node{ + Name: nodeName, + Compromised: shared.CompromiseType(compromised), + IsNamespaced: isNamespaced, + Namespace: namespace, + Critical: critical, + } } + suite.Equal(expectedNodeNames, resultsMap) } +// func (suite *VertexTestSuite) TestVertexPod() { +// g := gremlingo.Traversal_().WithRemote(suite.client) +// results, err := g.V().HasLabel(vertex.PodLabel).ElementMap().ToList() +// suite.NoError(err) +// suite.T().Errorf("results: %s", results) +// for _, res := range results { +// suite.T().Errorf("res: %s", res.String()) +// } +// } + +// func (suite *VertexTestSuite) TestVertexRole() { +// g := gremlingo.Traversal_().WithRemote(suite.client) +// results, err := g.V().HasLabel(vertex.RoleLabel).ElementMap().ToList() +// suite.NoError(err) +// suite.T().Errorf("results: %s", results) +// for _, res := range results { +// suite.T().Errorf("res: %s", res.String()) +// } +// } + +// func (suite *VertexTestSuite) TestVertexVolume() { +// g := gremlingo.Traversal_().WithRemote(suite.client) +// results, err := g.V().HasLabel(vertex.VolumeLabel).ElementMap().ToList() +// suite.NoError(err) +// suite.T().Errorf("results: %s", results) +// for _, res := range results { +// suite.T().Errorf("res: %s", res.String()) +// } +// } + func TestVertexTestSuite(t *testing.T) { suite.Run(t, new(VertexTestSuite)) } From dd5c122fcf84598cbc3a75229ad3e1fd0dc6e9b4 Mon Sep 17 00:00:00 2001 From: Edouard Schweisguth Date: Thu, 15 Jun 2023 11:00:14 +0200 Subject: [PATCH 03/25] Generator OK --- test/system/generator/generator.go | 184 ++++++++++++++++----- test/system/graph_vertex_test.go | 64 +------- test/system/vertex.gen.go | 256 +++++++++++++++++++++++++++++ 3 files changed, 402 insertions(+), 102 deletions(-) create mode 100644 test/system/vertex.gen.go diff --git a/test/system/generator/generator.go b/test/system/generator/generator.go index 18da7a59f..91a5ea9d1 100644 --- a/test/system/generator/generator.go +++ b/test/system/generator/generator.go @@ -8,6 +8,7 @@ import ( "os" "path/filepath" "text/template" + "time" "github.com/DataDog/KubeHound/pkg/kubehound/models/converter" "github.com/DataDog/KubeHound/pkg/kubehound/models/graph" @@ -38,12 +39,18 @@ var ( ) var ( - globalHeaders = []byte(` -// PLEASE DO NOT EDIT -// THIS HAS BEEN GENERATED AUTOMATICALLY + globalHeaders = []byte(`// PLEASE DO NOT EDIT +// THIS HAS BEEN GENERATED AUTOMATICALLY on ` + time.Now().Format("2006-01-02 15:04") + ` // -// FIXME: -// cd test/system/generator && go build && ./generator ../../setup/test-cluster/ +// Generate it with "go generate ./..." +// +// currently support only: +// - nodes +// - pods +// - containers +// - volumes +// +// TODO: roles, rolebinding, clusterrole, clusterrolebindings package system @@ -55,11 +62,20 @@ import ( `) ) +func usage() { + fmt.Println(`Usage: + ./generator `) +} + func main() { - // todo: check length - path := os.Args[1] + if len(os.Args) != 3 { + usage() + return + } + k8sDefinitionPath := os.Args[1] + codegenPath := os.Args[2] - clusterFile, err := ioutil.ReadFile(filepath.Join(path, "cluster.yaml")) + clusterFile, err := ioutil.ReadFile(filepath.Join(k8sDefinitionPath, "cluster.yaml")) if err != nil { log.Fatal(err) } @@ -68,7 +84,7 @@ func main() { log.Fatal(err) } - attackPath := filepath.Join(path, "attacks") + attackPath := filepath.Join(k8sDefinitionPath, "attacks") filesAttack, err := ioutil.ReadDir(attackPath) if err != nil { log.Fatal(err) @@ -77,9 +93,6 @@ func main() { ProcessFile(attackPath, file) } - fmt.Printf("Pod stored: %+v\n", Pods) - fmt.Printf("Node stored: %+v\n", Nodes) - fmt.Printf("Volume stored: %+v\n", Volumes) outPods, err := GeneratePodTemplate() if err != nil { fmt.Println("failed to write pods: ", err) @@ -88,7 +101,19 @@ func main() { if err != nil { fmt.Println("failed to write pods: ", err) } - WriteTemplatesToFile("outfile.gen.go", globalHeaders, outPods, outNodes) + outContainers, err := GenerateContainerTemplate() + if err != nil { + fmt.Println("failed to write pods: ", err) + } + outVolumes, err := GenerateVolumeTemplate() + if err != nil { + fmt.Println("failed to write pods: ", err) + } + fmt.Printf("volumes: %+v\n", Volumes) + err = WriteTemplatesToFile(codegenPath, globalHeaders, outPods, outNodes, outVolumes, outContainers) + if err != nil { + fmt.Println(err) + } } func ProcessCluster(content []byte) error { @@ -129,14 +154,29 @@ func ProcessFile(basePath string, file os.FileInfo) { // and match each type-case switch o := obj.(type) { case *v1.Node: - AddNodeToList(o) + err = AddNodeToList(o) + if err != nil { + fmt.Println("Failed to add node to list:", err) + } case *v1.Pod: - AddPodToList(o) + err = AddPodToList(o) + if err != nil { + fmt.Println("Failed to add pod to list:", err) + } + p := store.Pod{ + K8: *o, + } for _, vol := range o.Spec.Volumes { - p := store.Pod{ - K8: *o, + err = AddVolumeToList(&vol, &p) + if err != nil { + fmt.Println("Failed to add volume to list:", err) + } + } + for _, cont := range o.Spec.Containers { + err = AddContainerToList(&cont, &p) + if err != nil { + fmt.Println("Failed to add container to list:", err) } - AddVolumeToList(&vol, &p) } case *v1beta1.Role: // TODO @@ -159,7 +199,10 @@ func AddPodToList(pod *corev1.Pod) error { K8: *pod, } conv := converter.GraphConverter{} - convertedPod, _ := conv.Pod(&storePod) + convertedPod, err := conv.Pod(&storePod) + if err != nil { + return err + } Pods[pod.Name] = *convertedPod return nil @@ -171,27 +214,50 @@ func AddNodeToList(node *corev1.Node) error { K8: *node, } conv := converter.GraphConverter{} - convertedNode, _ := conv.Node(&storeNode) + convertedNode, err := conv.Node(&storeNode) + if err != nil { + return err + } Nodes[node.Name] = *convertedNode return nil } +func AddContainerToList(Container *corev1.Container, storePod *store.Pod) error { + fmt.Printf("Container name: %s\n", Container.Name) + storeContainer := store.Container{ + K8: *Container, + } + conv := converter.GraphConverter{} + convertedContainer, err := conv.Container(&storeContainer) + if err != nil { + return err + } + convertedContainer.Pod = storePod.K8.Name + Containers[Container.Name] = *convertedContainer + + return nil +} + func AddVolumeToList(volume *corev1.Volume, storePod *store.Pod) error { fmt.Printf("Volume name: %s\n", volume.Name) storeVolume := store.Volume{ + Name: volume.Name, Source: *volume, } conv := converter.GraphConverter{} - convertedVolume, _ := conv.Volume(&storeVolume, storePod) + convertedVolume, err := conv.Volume(&storeVolume, storePod) + if err != nil { + return err + } Volumes[volume.Name] = *convertedVolume return nil } func GenerateNodeTemplate() ([]byte, error) { - tmpl := `var expectedNodeNames = map[string]graph.Node{ - {{range $val := . -}} + tmpl := `var expectedNodes = map[string]graph.Node{ + {{- range $val := .}} "{{.Name}}": { StoreID: "", Name: "{{.Name}}", @@ -199,8 +265,7 @@ func GenerateNodeTemplate() ([]byte, error) { Namespace: "{{.Namespace}}", Compromised: shared.CompromiseNone, Critical: false, - }, - {{- end}} + },{{ end }} } ` @@ -212,8 +277,8 @@ func GenerateNodeTemplate() ([]byte, error) { } func GeneratePodTemplate() ([]byte, error) { - tmpl := `var expectedPodNames = map[string]graph.Pod{ - {{range $val := . -}} + tmpl := `var expectedPods = map[string]graph.Pod{ + {{- range $val := .}} "{{.Name}}": { StoreID: "", Name: "{{.Name}}", @@ -234,23 +299,53 @@ func GeneratePodTemplate() ([]byte, error) { } func GenerateContainerTemplate() ([]byte, error) { - tmpl := `var expectedContainerNames = map[string]graph.Container{ - {{range $val := .}} + tmpl := `var expectedContainers = map[string]graph.Container{ + {{- range $val := .}} "{{.Name}}": { StoreID: "", Name: "{{.Name}}", - IsNamespaced: {{.IsNamespaced}}, - Namespace: "{{.Namespace}}", - Compromised: shared.CompromiseNone, - Critical: false, - }, - {{end}} + Image: "{{.Image}}", + Command: []string{}, + Args: []string{}, + Capabilities: []string{}, + Privileged: {{.Privileged}}, + PrivEsc: {{.PrivEsc}}, + HostPID: {{.HostPID}}, + HostPath: {{.HostPath}}, + HostIPC: {{.HostIPC}}, + HostNetwork: {{.HostNetwork}}, + RunAsUser: {{.RunAsUser}}, + Ports: []int{}, + Pod: "{{.Pod}}", + Node: "{{.Node}}", + Compromised: 0, + Critical: {{.Critical}}, + },{{ end }} } ` t := template.Must(template.New("tmpl").Parse(tmpl)) outbuf := bytes.NewBuffer([]byte{}) - t.Execute(outbuf, Nodes) + t.Execute(outbuf, Containers) + + return outbuf.Bytes(), nil +} + +func GenerateVolumeTemplate() ([]byte, error) { + tmpl := `var expectedVolumes = map[string]graph.Volume{ + {{- range $val := .}} + "{{.Name}}": { + StoreID: "", + Name: "{{.Name}}", + Type: "{{.Type}}", + Path: "{{.Path}}", + },{{ end }} + } +` + + t := template.Must(template.New("tmpl").Parse(tmpl)) + outbuf := bytes.NewBuffer([]byte{}) + t.Execute(outbuf, Volumes) return outbuf.Bytes(), nil } @@ -260,12 +355,17 @@ func WriteTemplatesToFile(path string, templates ...[]byte) error { if err != nil { return err } - - for _, t := range templates { - _, err := f.Write(t) - if err != nil { - return err - } + in := bytes.Join(templates, []byte("\n")) + // We run go fmt on it so it's "clean" + // The formatting is not as strict as our editors config & linter + // clean, err := format.Source(in) + // if err != nil { + // return err + // } + clean := in + _, err = f.Write(clean) + if err != nil { + return err } return nil } diff --git a/test/system/graph_vertex_test.go b/test/system/graph_vertex_test.go index d63ce7d1d..733a79f06 100644 --- a/test/system/graph_vertex_test.go +++ b/test/system/graph_vertex_test.go @@ -13,6 +13,8 @@ import ( "github.com/stretchr/testify/suite" ) +//go:generate go run ./generator ../setup/test-cluster ./vertex.gen.go + // Optional syntactic sugar. var __ = gremlingo.T__ @@ -42,39 +44,6 @@ func (suite *VertexTestSuite) TestVertexContainer() { results, err := g.V().HasLabel(vertex.ContainerLabel).ElementMap().ToList() suite.NoError(err) - expectedContainers := map[string]graph.Container{ - "netadmin-pod": { - Name: "netadmin-pod", - Compromised: shared.CompromiseNone, - Critical: false, - }, - "pod-create-pod": { - Name: "pod-create-pod", - Compromised: shared.CompromiseNone, - Critical: false, - }, - "priv-pod": { - Name: "priv-pod", - Compromised: shared.CompromiseNone, - Critical: false, - }, - "kube-apiserver": { - Name: "kube-apiserver", - Compromised: shared.CompromiseNone, - Critical: false, - }, - "kube-proxy": { - Name: "kube-proxy", - Compromised: shared.CompromiseNone, - Critical: false, - }, - "impersonate-pod": { - Name: "impersonate-pod", - Compromised: shared.CompromiseNone, - Critical: false, - }, - } - suite.Equal(len(expectedContainers), len(results)) resultsMap := map[string]graph.Container{} for _, res := range results { @@ -114,32 +83,7 @@ func (suite *VertexTestSuite) TestVertexNode() { results, err := g.V().HasLabel(vertex.NodeLabel).ElementMap().ToList() suite.NoError(err) - expectedNodeNames := map[string]graph.Node{ - "kubehound.test.local-control-plane": { - StoreID: "", - Name: "kubehound.test.local-control-plane", - IsNamespaced: false, - Namespace: "", - Compromised: shared.CompromiseNone, - Critical: false, - }, - "kubehound.test.local-worker": { - Name: "kubehound.test.local-worker", - IsNamespaced: false, - Namespace: "", - Compromised: shared.CompromiseNone, - Critical: false, - }, - "kubehound.test.local-worker2": { - Name: "kubehound.test.local-worker2", - IsNamespaced: false, - Namespace: "", - Compromised: shared.CompromiseNone, - Critical: false, - }, - } - - suite.Equal(len(expectedNodeNames), len(results)) + suite.Equal(len(expectedNodes), len(results)) resultsMap := map[string]graph.Node{} for _, res := range results { res := res.GetInterface() @@ -168,7 +112,7 @@ func (suite *VertexTestSuite) TestVertexNode() { Critical: critical, } } - suite.Equal(expectedNodeNames, resultsMap) + suite.Equal(expectedNodes, resultsMap) } // func (suite *VertexTestSuite) TestVertexPod() { diff --git a/test/system/vertex.gen.go b/test/system/vertex.gen.go new file mode 100644 index 000000000..e22d02d93 --- /dev/null +++ b/test/system/vertex.gen.go @@ -0,0 +1,256 @@ +// PLEASE DO NOT EDIT +// THIS HAS BEEN GENERATED AUTOMATICALLY on 2023-06-15 10:59 +// +// Generate it with "go generate ./..." +// +// currently support only: +// - nodes +// - pods +// - containers +// - volumes +// +// TODO: roles, rolebinding, clusterrole, clusterrolebindings + +package system + +import ( + "github.com/DataDog/KubeHound/pkg/kubehound/models/graph" + "github.com/DataDog/KubeHound/pkg/kubehound/models/shared" +) + + +var expectedPods = map[string]graph.Pod{ + "modload-pod": { + StoreID: "", + Name: "modload-pod", + IsNamespaced: false, + Namespace: "", + Compromised: shared.CompromiseNone, + Critical: false, + }, + "netadmin-pod": { + StoreID: "", + Name: "netadmin-pod", + IsNamespaced: false, + Namespace: "", + Compromised: shared.CompromiseNone, + Critical: false, + }, + "nsenter-pod": { + StoreID: "", + Name: "nsenter-pod", + IsNamespaced: false, + Namespace: "", + Compromised: shared.CompromiseNone, + Critical: false, + }, + "priv-pod": { + StoreID: "", + Name: "priv-pod", + IsNamespaced: false, + Namespace: "", + Compromised: shared.CompromiseNone, + Critical: false, + }, + "sharedps-pod": { + StoreID: "", + Name: "sharedps-pod", + IsNamespaced: false, + Namespace: "", + Compromised: shared.CompromiseNone, + Critical: false, + }, + "umh-core-pod": { + StoreID: "", + Name: "umh-core-pod", + IsNamespaced: false, + Namespace: "", + Compromised: shared.CompromiseNone, + Critical: false, + }, + "varlog-pod": { + StoreID: "", + Name: "varlog-pod", + IsNamespaced: false, + Namespace: "", + Compromised: shared.CompromiseNone, + Critical: false, + }, + } + +var expectedNodes = map[string]graph.Node{ + "control-plane": { + StoreID: "", + Name: "control-plane", + IsNamespaced: false, + Namespace: "", + Compromised: shared.CompromiseNone, + Critical: false, + }, + "worker": { + StoreID: "", + Name: "worker", + IsNamespaced: false, + Namespace: "", + Compromised: shared.CompromiseNone, + Critical: false, + }, + } + +var expectedVolumes = map[string]graph.Volume{ + "nodelog": { + StoreID: "", + Name: "nodelog", + Type: "HostPath", + Path: "/var/log", + }, + "nodeproc": { + StoreID: "", + Name: "nodeproc", + Type: "HostPath", + Path: "/proc/sys/kernel", + }, + } + +var expectedContainers = map[string]graph.Container{ + "modload-pod": { + StoreID: "", + Name: "modload-pod", + Image: "ubuntu", + Command: []string{}, + Args: []string{}, + Capabilities: []string{}, + Privileged: false, + PrivEsc: false, + HostPID: false, + HostPath: false, + HostIPC: false, + HostNetwork: false, + RunAsUser: 0, + Ports: []int{}, + Pod: "modload-pod", + Node: "", + Compromised: 0, + Critical: false, + }, + "netadmin-pod": { + StoreID: "", + Name: "netadmin-pod", + Image: "ubuntu", + Command: []string{}, + Args: []string{}, + Capabilities: []string{}, + Privileged: false, + PrivEsc: false, + HostPID: false, + HostPath: false, + HostIPC: false, + HostNetwork: false, + RunAsUser: 0, + Ports: []int{}, + Pod: "netadmin-pod", + Node: "", + Compromised: 0, + Critical: false, + }, + "nsenter-pod": { + StoreID: "", + Name: "nsenter-pod", + Image: "ubuntu", + Command: []string{}, + Args: []string{}, + Capabilities: []string{}, + Privileged: true, + PrivEsc: false, + HostPID: false, + HostPath: false, + HostIPC: false, + HostNetwork: false, + RunAsUser: 0, + Ports: []int{}, + Pod: "nsenter-pod", + Node: "", + Compromised: 0, + Critical: false, + }, + "priv-pod": { + StoreID: "", + Name: "priv-pod", + Image: "ubuntu", + Command: []string{}, + Args: []string{}, + Capabilities: []string{}, + Privileged: true, + PrivEsc: false, + HostPID: false, + HostPath: false, + HostIPC: false, + HostNetwork: false, + RunAsUser: 0, + Ports: []int{}, + Pod: "priv-pod", + Node: "", + Compromised: 0, + Critical: false, + }, + "sharedps-pod": { + StoreID: "", + Name: "sharedps-pod", + Image: "ubuntu", + Command: []string{}, + Args: []string{}, + Capabilities: []string{}, + Privileged: false, + PrivEsc: false, + HostPID: false, + HostPath: false, + HostIPC: false, + HostNetwork: false, + RunAsUser: 0, + Ports: []int{}, + Pod: "sharedps-pod", + Node: "", + Compromised: 0, + Critical: false, + }, + "umh-core-pod": { + StoreID: "", + Name: "umh-core-pod", + Image: "ubuntu", + Command: []string{}, + Args: []string{}, + Capabilities: []string{}, + Privileged: false, + PrivEsc: false, + HostPID: false, + HostPath: false, + HostIPC: false, + HostNetwork: false, + RunAsUser: 0, + Ports: []int{}, + Pod: "umh-core-pod", + Node: "", + Compromised: 0, + Critical: false, + }, + "varlog-pod": { + StoreID: "", + Name: "varlog-pod", + Image: "ubuntu", + Command: []string{}, + Args: []string{}, + Capabilities: []string{}, + Privileged: false, + PrivEsc: false, + HostPID: false, + HostPath: false, + HostIPC: false, + HostNetwork: false, + RunAsUser: 0, + Ports: []int{}, + Pod: "varlog-pod", + Node: "", + Compromised: 0, + Critical: false, + }, + } From bace532046db60dd7485be4945d05ecc44963070 Mon Sep 17 00:00:00 2001 From: Edouard Schweisguth Date: Thu, 15 Jun 2023 12:10:36 +0200 Subject: [PATCH 04/25] Updates tests --- go.mod | 1 + go.sum | 2 + test/system/generator/generator.go | 1 + test/system/graph_vertex_test.go | 64 +++++++++++++++++++++++++++--- 4 files changed, 63 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index a9c3e1619..2c88378a5 100644 --- a/go.mod +++ b/go.mod @@ -11,6 +11,7 @@ require ( github.com/stretchr/testify v1.8.2 go.mongodb.org/mongo-driver v1.11.6 go.uber.org/ratelimit v0.2.0 + golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 gopkg.in/DataDog/dd-trace-go.v1 v1.51.0 k8s.io/api v0.27.2 k8s.io/apimachinery v0.27.2 diff --git a/go.sum b/go.sum index 2649ddd94..68906ee1f 100644 --- a/go.sum +++ b/go.sum @@ -398,6 +398,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc= +golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= diff --git a/test/system/generator/generator.go b/test/system/generator/generator.go index 91a5ea9d1..79619f318 100644 --- a/test/system/generator/generator.go +++ b/test/system/generator/generator.go @@ -213,6 +213,7 @@ func AddNodeToList(node *corev1.Node) error { storeNode := store.Node{ K8: *node, } + storeNode.K8.Name = "kubehound.test.local-" + storeNode.K8.Name conv := converter.GraphConverter{} convertedNode, err := conv.Node(&storeNode) if err != nil { diff --git a/test/system/graph_vertex_test.go b/test/system/graph_vertex_test.go index 733a79f06..679e63fc8 100644 --- a/test/system/graph_vertex_test.go +++ b/test/system/graph_vertex_test.go @@ -2,6 +2,7 @@ package system import ( "context" + "fmt" "testing" "github.com/DataDog/KubeHound/pkg/config" @@ -11,13 +12,25 @@ import ( "github.com/DataDog/KubeHound/pkg/kubehound/storage/graphdb" gremlingo "github.com/apache/tinkerpop/gremlin-go/driver" "github.com/stretchr/testify/suite" + "golang.org/x/exp/slices" ) //go:generate go run ./generator ../setup/test-cluster ./vertex.gen.go +const ( + nodePrefix = "kubehound.test.local-" +) + // Optional syntactic sugar. var __ = gremlingo.T__ +var containerToVerify = []string{ + "kube-apiserver", + "kindnet-cni", + "kube-controller-manager", + "kube-apiserver", +} + type VertexTestSuite struct { suite.Suite gdb graphdb.Provider @@ -50,19 +63,48 @@ func (suite *VertexTestSuite) TestVertexContainer() { res := res.GetInterface() converted := res.(map[any]any) - nodeName, ok := converted["name"].(string) + containerName, ok := converted["name"].(string) + suite.True(ok, "failed to convert container name to string") + + nodeName, ok := converted["node"].(string) suite.True(ok, "failed to convert node name to string") + podName, ok := converted["pod"].(string) + suite.True(ok, "failed to convert pod name to string") + + imageName, ok := converted["image"].(string) + suite.True(ok, "failed to convert image name to string") + compromised, ok := converted["compromised"].(int) suite.True(ok, "failed to convert compromised field to CompromiseType") critical, ok := converted["critical"].(bool) suite.True(ok, "failed to convert critical field to bool") - resultsMap[nodeName] = graph.Container{ - Name: nodeName, - Compromised: shared.CompromiseType(compromised), - Critical: critical, + // We skip these because they are built by Kind itself + if slices.Contains(containerToVerify, containerName) { + continue + } + + resultsMap[containerName] = graph.Container{ + StoreID: "", + Name: containerName, + Image: imageName, + Command: []string{}, + Args: []string{}, + Capabilities: []string{}, + Privileged: false, + PrivEsc: false, + HostPID: false, + HostPath: false, + HostIPC: false, + HostNetwork: false, + RunAsUser: 0, + Ports: []int{}, + Pod: podName, + Node: nodeName, + Compromised: shared.CompromiseType(compromised), + Critical: critical, } } suite.Equal(expectedContainers, resultsMap) @@ -104,6 +146,18 @@ func (suite *VertexTestSuite) TestVertexNode() { critical, ok := converted["critical"].(bool) suite.True(ok, "failed to convert critical field to bool") + // Prefix the node with the kind prefix + // nodeName = nodePrefix + nodeName + for { + orig := nodeName + count := 0 + _, exist := resultsMap[nodeName] + if exist { + nodeName = fmt.Sprintf("%s%d", orig, count) + continue + } + break + } resultsMap[nodeName] = graph.Node{ Name: nodeName, Compromised: shared.CompromiseType(compromised), From ba96fea647cc920c2a07a9eb63f643a1dacdf56d Mon Sep 17 00:00:00 2001 From: Edouard Schweisguth Date: Thu, 15 Jun 2023 13:02:38 +0200 Subject: [PATCH 05/25] Node ok --- Makefile | 4 ++-- test/system/generator/generator.go | 22 +++++++++++++++++----- test/system/graph_vertex_test.go | 30 ------------------------------ test/system/vertex.gen.go | 18 +++++++++++++----- 4 files changed, 32 insertions(+), 42 deletions(-) diff --git a/Makefile b/Makefile index 60a374944..491ed3bfa 100644 --- a/Makefile +++ b/Makefile @@ -44,8 +44,8 @@ test: ## Run the full suite of unit tests .PHONY: system-test system-test: ## Run the system tests - $(MAKE) infra-rm - $(MAKE) infra-up + # $(MAKE) infra-rm + # $(MAKE) infra-up cd test/system && go test -v -timeout "60s" -race ./... .PHONY: local-cluster-reset diff --git a/test/system/generator/generator.go b/test/system/generator/generator.go index 79619f318..4d8d9adbb 100644 --- a/test/system/generator/generator.go +++ b/test/system/generator/generator.go @@ -122,11 +122,23 @@ func ProcessCluster(content []byte) error { if err != nil { return err } + for _, n := range cluster.Nodes { - node := n.Role - Nodes[node] = graph.Node{ + nodeName := "kubehound.test.local-" + n.Role + for { + orig := nodeName + count := 2 + _, exist := Nodes[nodeName] + if exist { + nodeName = fmt.Sprintf("%s%d", orig, count) + continue + } + break + } + + Nodes[nodeName] = graph.Node{ StoreID: "", - Name: node, + Name: nodeName, IsNamespaced: false, Namespace: "", Compromised: 0, @@ -213,13 +225,13 @@ func AddNodeToList(node *corev1.Node) error { storeNode := store.Node{ K8: *node, } - storeNode.K8.Name = "kubehound.test.local-" + storeNode.K8.Name conv := converter.GraphConverter{} convertedNode, err := conv.Node(&storeNode) if err != nil { return err } - Nodes[node.Name] = *convertedNode + fmt.Printf("Adding %+v to nodes", convertedNode) + Nodes[convertedNode.Name] = *convertedNode return nil } diff --git a/test/system/graph_vertex_test.go b/test/system/graph_vertex_test.go index 679e63fc8..dc6366103 100644 --- a/test/system/graph_vertex_test.go +++ b/test/system/graph_vertex_test.go @@ -2,7 +2,6 @@ package system import ( "context" - "fmt" "testing" "github.com/DataDog/KubeHound/pkg/config" @@ -17,13 +16,6 @@ import ( //go:generate go run ./generator ../setup/test-cluster ./vertex.gen.go -const ( - nodePrefix = "kubehound.test.local-" -) - -// Optional syntactic sugar. -var __ = gremlingo.T__ - var containerToVerify = []string{ "kube-apiserver", "kindnet-cni", @@ -110,16 +102,6 @@ func (suite *VertexTestSuite) TestVertexContainer() { suite.Equal(expectedContainers, resultsMap) } -// func (suite *VertexTestSuite) TestVertexIdentity() { -// g := gremlingo.Traversal_().WithRemote(suite.client) -// results, err := g.V().HasLabel(vertex.IdentityLabel).ElementMap().ToList() -// suite.NoError(err) -// suite.T().Errorf("results: %s", results) -// for _, res := range results { -// suite.T().Errorf("res: %s", res.String()) -// } -// } - func (suite *VertexTestSuite) TestVertexNode() { g := gremlingo.Traversal_().WithRemote(suite.client) results, err := g.V().HasLabel(vertex.NodeLabel).ElementMap().ToList() @@ -146,18 +128,6 @@ func (suite *VertexTestSuite) TestVertexNode() { critical, ok := converted["critical"].(bool) suite.True(ok, "failed to convert critical field to bool") - // Prefix the node with the kind prefix - // nodeName = nodePrefix + nodeName - for { - orig := nodeName - count := 0 - _, exist := resultsMap[nodeName] - if exist { - nodeName = fmt.Sprintf("%s%d", orig, count) - continue - } - break - } resultsMap[nodeName] = graph.Node{ Name: nodeName, Compromised: shared.CompromiseType(compromised), diff --git a/test/system/vertex.gen.go b/test/system/vertex.gen.go index e22d02d93..2bb96a436 100644 --- a/test/system/vertex.gen.go +++ b/test/system/vertex.gen.go @@ -1,5 +1,5 @@ // PLEASE DO NOT EDIT -// THIS HAS BEEN GENERATED AUTOMATICALLY on 2023-06-15 10:59 +// THIS HAS BEEN GENERATED AUTOMATICALLY on 2023-06-15 13:01 // // Generate it with "go generate ./..." // @@ -79,17 +79,25 @@ var expectedPods = map[string]graph.Pod{ } var expectedNodes = map[string]graph.Node{ - "control-plane": { + "kubehound.test.local-control-plane": { StoreID: "", - Name: "control-plane", + Name: "kubehound.test.local-control-plane", IsNamespaced: false, Namespace: "", Compromised: shared.CompromiseNone, Critical: false, }, - "worker": { + "kubehound.test.local-worker": { StoreID: "", - Name: "worker", + Name: "kubehound.test.local-worker", + IsNamespaced: false, + Namespace: "", + Compromised: shared.CompromiseNone, + Critical: false, + }, + "kubehound.test.local-worker2": { + StoreID: "", + Name: "kubehound.test.local-worker2", IsNamespaced: false, Namespace: "", Compromised: shared.CompromiseNone, From 0a6f59087ab0ffb5aef5438cc0c4f4c094bcb2e3 Mon Sep 17 00:00:00 2001 From: Edouard Schweisguth Date: Thu, 15 Jun 2023 19:26:31 +0200 Subject: [PATCH 06/25] Codegen & test updates --- Makefile | 2 +- test/system/generator/generator.go | 87 ++++++++------- test/system/graph_vertex_test.go | 71 +++++++----- test/system/vertex.gen.go | 170 ++++++++++++++++++++++++++++- 4 files changed, 262 insertions(+), 68 deletions(-) diff --git a/Makefile b/Makefile index 491ed3bfa..880fae193 100644 --- a/Makefile +++ b/Makefile @@ -46,7 +46,7 @@ test: ## Run the full suite of unit tests system-test: ## Run the system tests # $(MAKE) infra-rm # $(MAKE) infra-up - cd test/system && go test -v -timeout "60s" -race ./... + cd test/system && export KUBECONFIG=/home/edouard/dd/KubeHound/test/setup/.kube/config && go test -v -timeout "60s" -race ./... .PHONY: local-cluster-reset local-cluster-reset: ## Destroy the current kind cluster and creates a new one diff --git a/test/system/generator/generator.go b/test/system/generator/generator.go index 4d8d9adbb..c8fda470a 100644 --- a/test/system/generator/generator.go +++ b/test/system/generator/generator.go @@ -149,59 +149,64 @@ func ProcessCluster(content []byte) error { } func ProcessFile(basePath string, file os.FileInfo) { - fmt.Println(file.Name(), file.IsDir()) + fmt.Println("Processing: " + file.Name()) data, err := os.ReadFile(filepath.Join(basePath, file.Name())) if err != nil { fmt.Printf("failed to read file: %v", err) return } - decode := scheme.Codecs.UniversalDeserializer().Decode - obj, _, err := decode(data, nil, nil) - if err != nil { - fmt.Println("Error while decoding YAML object. Err was: ", err) - return - } + for _, subfile := range bytes.Split(data, []byte("\n---\n")) { - // now use switch over the type of the object - // and match each type-case - switch o := obj.(type) { - case *v1.Node: - err = AddNodeToList(o) + decode := scheme.Codecs.UniversalDeserializer().Decode + obj, _, err := decode(subfile, nil, nil) if err != nil { - fmt.Println("Failed to add node to list:", err) - } - case *v1.Pod: - err = AddPodToList(o) - if err != nil { - fmt.Println("Failed to add pod to list:", err) - } - p := store.Pod{ - K8: *o, + fmt.Println("Error while decoding YAML object. Err was: ", err) + return } - for _, vol := range o.Spec.Volumes { - err = AddVolumeToList(&vol, &p) + + // now use switch over the type of the object + // and match each type-case + switch o := obj.(type) { + case *v1.List: + panic("list!") + case *v1.Node: + err = AddNodeToList(o) if err != nil { - fmt.Println("Failed to add volume to list:", err) + fmt.Println("Failed to add node to list:", err) } - } - for _, cont := range o.Spec.Containers { - err = AddContainerToList(&cont, &p) + case *v1.Pod: + err = AddPodToList(o) if err != nil { - fmt.Println("Failed to add container to list:", err) + fmt.Println("Failed to add pod to list:", err) + } + p := store.Pod{ + K8: *o, + } + for _, vol := range o.Spec.Volumes { + err = AddVolumeToList(&vol, &p) + if err != nil { + fmt.Println("Failed to add volume to list:", err) + } + } + for _, cont := range o.Spec.Containers { + err = AddContainerToList(&cont, &p) + if err != nil { + fmt.Println("Failed to add container to list:", err) + } } + case *v1beta1.Role: + // TODO + case *v1beta1.RoleBinding: + // TODO + case *v1beta1.ClusterRole: + // TODO + case *v1beta1.ClusterRoleBinding: + // TODO + case *v1.ServiceAccount: + default: + fmt.Printf("Unknown object type: %+v\n", o) + //o is unknown for us } - case *v1beta1.Role: - // TODO - case *v1beta1.RoleBinding: - // TODO - case *v1beta1.ClusterRole: - // TODO - case *v1beta1.ClusterRoleBinding: - // TODO - case *v1.ServiceAccount: - default: - fmt.Printf("Unknown object type: %+v\n", o) - //o is unknown for us } } @@ -330,7 +335,7 @@ func GenerateContainerTemplate() ([]byte, error) { RunAsUser: {{.RunAsUser}}, Ports: []int{}, Pod: "{{.Pod}}", - Node: "{{.Node}}", + // Node: "{{.Node}}", Compromised: 0, Critical: {{.Critical}}, },{{ end }} diff --git a/test/system/graph_vertex_test.go b/test/system/graph_vertex_test.go index dc6366103..0a3bbf3f6 100644 --- a/test/system/graph_vertex_test.go +++ b/test/system/graph_vertex_test.go @@ -3,14 +3,16 @@ package system import ( "context" "testing" + "time" "github.com/DataDog/KubeHound/pkg/config" "github.com/DataDog/KubeHound/pkg/kubehound/graph/vertex" "github.com/DataDog/KubeHound/pkg/kubehound/models/graph" - "github.com/DataDog/KubeHound/pkg/kubehound/models/shared" "github.com/DataDog/KubeHound/pkg/kubehound/storage/graphdb" + "github.com/DataDog/KubeHound/pkg/kubehound/storage/storedb" gremlingo "github.com/apache/tinkerpop/gremlin-go/driver" "github.com/stretchr/testify/suite" + "go.mongodb.org/mongo-driver/mongo" "golang.org/x/exp/slices" ) @@ -21,32 +23,47 @@ var containerToVerify = []string{ "kindnet-cni", "kube-controller-manager", "kube-apiserver", + "kube-proxy", + "kube-scheduler", + "coredns", + "etcd", + "local-path-provisioner", } type VertexTestSuite struct { suite.Suite gdb graphdb.Provider client *gremlingo.DriverRemoteConnection + g *gremlingo.GraphTraversalSource } -func (suite *VertexTestSuite) SetupTest() { - gdb, err := graphdb.Factory(context.Background(), config.MustLoadConfig("./kubehound.yaml")) +func (suite *VertexTestSuite) SetupSuite() { + ctx := context.Background() + gdb, err := graphdb.Factory(ctx, config.MustLoadConfig("./kubehound.yaml")) suite.NoError(err) suite.gdb = gdb suite.client = gdb.Raw().(*gremlingo.DriverRemoteConnection) - // g := gremlingo.Traversal_().WithRemote(suite.client) - // errChan := g.V().Drop().Iterate() - // err = <-errChan - // if err != nil { - // suite.Errorf(err, "error deleting the graphdb:\n") - // } - // err = runKubeHound() - // suite.NoError(err) + suite.g = gremlingo.Traversal_().WithRemote(suite.client) + errChan := suite.g.V().Drop().Iterate() + err = <-errChan + if err != nil { + suite.Errorf(err, "error deleting the graphdb:\n") + } + + provider, err := storedb.NewMongoProvider(ctx, storedb.MongoLocalDatabaseURL, 1*time.Second) + mongoclient := provider.Raw().(*mongo.Client) + db := mongoclient.Database("kubehound") + err = db.Drop(ctx) + if err != nil { + suite.Errorf(err, "error deleting the mongodb:\n") + } + time.Sleep(2 * time.Second) + err = runKubeHound() + suite.NoError(err) } func (suite *VertexTestSuite) TestVertexContainer() { - g := gremlingo.Traversal_().WithRemote(suite.client) - results, err := g.V().HasLabel(vertex.ContainerLabel).ElementMap().ToList() + results, err := suite.g.V().HasLabel(vertex.ContainerLabel).ElementMap().ToList() suite.NoError(err) suite.Equal(len(expectedContainers), len(results)) @@ -58,8 +75,9 @@ func (suite *VertexTestSuite) TestVertexContainer() { containerName, ok := converted["name"].(string) suite.True(ok, "failed to convert container name to string") - nodeName, ok := converted["node"].(string) - suite.True(ok, "failed to convert node name to string") + // This is most likely going to be flaky if we try to match these + // nodeName, ok := converted["node"].(string) + // suite.True(ok, "failed to convert node name to string") podName, ok := converted["pod"].(string) suite.True(ok, "failed to convert pod name to string") @@ -67,12 +85,15 @@ func (suite *VertexTestSuite) TestVertexContainer() { imageName, ok := converted["image"].(string) suite.True(ok, "failed to convert image name to string") - compromised, ok := converted["compromised"].(int) - suite.True(ok, "failed to convert compromised field to CompromiseType") + // compromised, ok := converted["compromised"].(int) + // suite.True(ok, "failed to convert compromised field to CompromiseType") critical, ok := converted["critical"].(bool) suite.True(ok, "failed to convert critical field to bool") + privileged, ok := converted["privileged"].(bool) + suite.True(ok, "failed to convert privileged field to bool") + // We skip these because they are built by Kind itself if slices.Contains(containerToVerify, containerName) { continue @@ -85,7 +106,7 @@ func (suite *VertexTestSuite) TestVertexContainer() { Command: []string{}, Args: []string{}, Capabilities: []string{}, - Privileged: false, + Privileged: privileged, PrivEsc: false, HostPID: false, HostPath: false, @@ -94,9 +115,9 @@ func (suite *VertexTestSuite) TestVertexContainer() { RunAsUser: 0, Ports: []int{}, Pod: podName, - Node: nodeName, - Compromised: shared.CompromiseType(compromised), - Critical: critical, + // Node: nodeName, + // Compromised: shared.CompromiseType(compromised), + Critical: critical, } } suite.Equal(expectedContainers, resultsMap) @@ -116,8 +137,8 @@ func (suite *VertexTestSuite) TestVertexNode() { nodeName, ok := converted["name"].(string) suite.True(ok, "failed to convert node name to string") - compromised, ok := converted["compromised"].(int) - suite.True(ok, "failed to convert compromised field to CompromiseType") + // compromised, ok := converted["compromised"].(int) + // suite.True(ok, "failed to convert compromised field to CompromiseType") isNamespaced, ok := converted["isNamespaced"].(bool) suite.True(ok, "failed to convert isNamespaced field to bool") @@ -129,8 +150,8 @@ func (suite *VertexTestSuite) TestVertexNode() { suite.True(ok, "failed to convert critical field to bool") resultsMap[nodeName] = graph.Node{ - Name: nodeName, - Compromised: shared.CompromiseType(compromised), + Name: nodeName, + // Compromised: shared.CompromiseType(compromised), IsNamespaced: isNamespaced, Namespace: namespace, Critical: critical, diff --git a/test/system/vertex.gen.go b/test/system/vertex.gen.go index 2bb96a436..193ea0332 100644 --- a/test/system/vertex.gen.go +++ b/test/system/vertex.gen.go @@ -1,5 +1,5 @@ // PLEASE DO NOT EDIT -// THIS HAS BEEN GENERATED AUTOMATICALLY on 2023-06-15 13:01 +// THIS HAS BEEN GENERATED AUTOMATICALLY on 2023-06-15 19:10 // // Generate it with "go generate ./..." // @@ -20,6 +20,14 @@ import ( var expectedPods = map[string]graph.Pod{ + "impersonate-pod": { + StoreID: "", + Name: "impersonate-pod", + IsNamespaced: false, + Namespace: "", + Compromised: shared.CompromiseNone, + Critical: false, + }, "modload-pod": { StoreID: "", Name: "modload-pod", @@ -44,6 +52,22 @@ var expectedPods = map[string]graph.Pod{ Compromised: shared.CompromiseNone, Critical: false, }, + "pod-create-pod": { + StoreID: "", + Name: "pod-create-pod", + IsNamespaced: false, + Namespace: "", + Compromised: shared.CompromiseNone, + Critical: false, + }, + "pod-patch-pod": { + StoreID: "", + Name: "pod-patch-pod", + IsNamespaced: false, + Namespace: "", + Compromised: shared.CompromiseNone, + Critical: false, + }, "priv-pod": { StoreID: "", Name: "priv-pod", @@ -52,6 +76,14 @@ var expectedPods = map[string]graph.Pod{ Compromised: shared.CompromiseNone, Critical: false, }, + "rolebind-pod": { + StoreID: "", + Name: "rolebind-pod", + IsNamespaced: false, + Namespace: "", + Compromised: shared.CompromiseNone, + Critical: false, + }, "sharedps-pod": { StoreID: "", Name: "sharedps-pod", @@ -60,6 +92,22 @@ var expectedPods = map[string]graph.Pod{ Compromised: shared.CompromiseNone, Critical: false, }, + "tokenget-pod": { + StoreID: "", + Name: "tokenget-pod", + IsNamespaced: false, + Namespace: "", + Compromised: shared.CompromiseNone, + Critical: false, + }, + "tokenlist-pod": { + StoreID: "", + Name: "tokenlist-pod", + IsNamespaced: false, + Namespace: "", + Compromised: shared.CompromiseNone, + Critical: false, + }, "umh-core-pod": { StoreID: "", Name: "umh-core-pod", @@ -121,6 +169,26 @@ var expectedVolumes = map[string]graph.Volume{ } var expectedContainers = map[string]graph.Container{ + "impersonate-pod": { + StoreID: "", + Name: "impersonate-pod", + Image: "ubuntu", + Command: []string{}, + Args: []string{}, + Capabilities: []string{}, + Privileged: false, + PrivEsc: false, + HostPID: false, + HostPath: false, + HostIPC: false, + HostNetwork: false, + RunAsUser: 0, + Ports: []int{}, + Pod: "impersonate-pod", + Node: "", + Compromised: 0, + Critical: false, + }, "modload-pod": { StoreID: "", Name: "modload-pod", @@ -181,6 +249,46 @@ var expectedContainers = map[string]graph.Container{ Compromised: 0, Critical: false, }, + "pod-create-pod": { + StoreID: "", + Name: "pod-create-pod", + Image: "ubuntu", + Command: []string{}, + Args: []string{}, + Capabilities: []string{}, + Privileged: false, + PrivEsc: false, + HostPID: false, + HostPath: false, + HostIPC: false, + HostNetwork: false, + RunAsUser: 0, + Ports: []int{}, + Pod: "pod-create-pod", + Node: "", + Compromised: 0, + Critical: false, + }, + "pod-patch-pod": { + StoreID: "", + Name: "pod-patch-pod", + Image: "ubuntu", + Command: []string{}, + Args: []string{}, + Capabilities: []string{}, + Privileged: false, + PrivEsc: false, + HostPID: false, + HostPath: false, + HostIPC: false, + HostNetwork: false, + RunAsUser: 0, + Ports: []int{}, + Pod: "pod-patch-pod", + Node: "", + Compromised: 0, + Critical: false, + }, "priv-pod": { StoreID: "", Name: "priv-pod", @@ -201,6 +309,26 @@ var expectedContainers = map[string]graph.Container{ Compromised: 0, Critical: false, }, + "rolebind-pod": { + StoreID: "", + Name: "rolebind-pod", + Image: "ubuntu", + Command: []string{}, + Args: []string{}, + Capabilities: []string{}, + Privileged: false, + PrivEsc: false, + HostPID: false, + HostPath: false, + HostIPC: false, + HostNetwork: false, + RunAsUser: 0, + Ports: []int{}, + Pod: "rolebind-pod", + Node: "", + Compromised: 0, + Critical: false, + }, "sharedps-pod": { StoreID: "", Name: "sharedps-pod", @@ -221,6 +349,46 @@ var expectedContainers = map[string]graph.Container{ Compromised: 0, Critical: false, }, + "tokenget-pod": { + StoreID: "", + Name: "tokenget-pod", + Image: "ubuntu", + Command: []string{}, + Args: []string{}, + Capabilities: []string{}, + Privileged: false, + PrivEsc: false, + HostPID: false, + HostPath: false, + HostIPC: false, + HostNetwork: false, + RunAsUser: 0, + Ports: []int{}, + Pod: "tokenget-pod", + Node: "", + Compromised: 0, + Critical: false, + }, + "tokenlist-pod": { + StoreID: "", + Name: "tokenlist-pod", + Image: "ubuntu", + Command: []string{}, + Args: []string{}, + Capabilities: []string{}, + Privileged: false, + PrivEsc: false, + HostPID: false, + HostPath: false, + HostIPC: false, + HostNetwork: false, + RunAsUser: 0, + Ports: []int{}, + Pod: "tokenlist-pod", + Node: "", + Compromised: 0, + Critical: false, + }, "umh-core-pod": { StoreID: "", Name: "umh-core-pod", From 525165cf3773f660e3c46bba090c9787a050eae2 Mon Sep 17 00:00:00 2001 From: Edouard Schweisguth Date: Fri, 16 Jun 2023 08:27:32 +0200 Subject: [PATCH 07/25] it works (node, containers) --- test/system/graph_vertex_test.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/system/graph_vertex_test.go b/test/system/graph_vertex_test.go index 0a3bbf3f6..0b27fa199 100644 --- a/test/system/graph_vertex_test.go +++ b/test/system/graph_vertex_test.go @@ -30,6 +30,8 @@ var containerToVerify = []string{ "local-path-provisioner", } +const numberOfKindDefaultContainer = 13 + type VertexTestSuite struct { suite.Suite gdb graphdb.Provider @@ -57,7 +59,6 @@ func (suite *VertexTestSuite) SetupSuite() { if err != nil { suite.Errorf(err, "error deleting the mongodb:\n") } - time.Sleep(2 * time.Second) err = runKubeHound() suite.NoError(err) } @@ -66,7 +67,7 @@ func (suite *VertexTestSuite) TestVertexContainer() { results, err := suite.g.V().HasLabel(vertex.ContainerLabel).ElementMap().ToList() suite.NoError(err) - suite.Equal(len(expectedContainers), len(results)) + suite.Equal(len(expectedContainers)+numberOfKindDefaultContainer, len(results)) resultsMap := map[string]graph.Container{} for _, res := range results { res := res.GetInterface() @@ -124,8 +125,7 @@ func (suite *VertexTestSuite) TestVertexContainer() { } func (suite *VertexTestSuite) TestVertexNode() { - g := gremlingo.Traversal_().WithRemote(suite.client) - results, err := g.V().HasLabel(vertex.NodeLabel).ElementMap().ToList() + results, err := suite.g.V().HasLabel(vertex.NodeLabel).ElementMap().ToList() suite.NoError(err) suite.Equal(len(expectedNodes), len(results)) @@ -194,6 +194,6 @@ func TestVertexTestSuite(t *testing.T) { suite.Run(t, new(VertexTestSuite)) } -func (suite *VertexTestSuite) TearDownTest() { +func (suite *VertexTestSuite) TearDownSuite() { suite.gdb.Close(context.Background()) } From 0e86cd63dc7c74fffe3ab5c6e9b38db08fba39ac Mon Sep 17 00:00:00 2001 From: Edouard Schweisguth Date: Fri, 16 Jun 2023 10:40:47 +0200 Subject: [PATCH 08/25] Pods test pass --- pkg/kubehound/models/converter/graph.go | 4 +- test/system/generator/generator.go | 15 +++- test/system/graph_vertex_test.go | 105 ++++++++++++++++++++--- test/system/vertex.gen.go | 106 +++++++++++++++--------- 4 files changed, 177 insertions(+), 53 deletions(-) diff --git a/pkg/kubehound/models/converter/graph.go b/pkg/kubehound/models/converter/graph.go index 48460e04f..f015bfdd9 100644 --- a/pkg/kubehound/models/converter/graph.go +++ b/pkg/kubehound/models/converter/graph.go @@ -94,10 +94,12 @@ func (c *GraphConverter) Pod(input *store.Pod) (*graph.Pod, error) { ServiceAccount: input.K8.Spec.ServiceAccountName, Node: input.K8.Spec.NodeName, } - if input.K8.Spec.ShareProcessNamespace != nil { output.SharedProcessNamespace = *input.K8.Spec.ShareProcessNamespace } + if output.Namespace != "" { + output.IsNamespaced = true + } return output, nil } diff --git a/test/system/generator/generator.go b/test/system/generator/generator.go index c8fda470a..4dc6f3341 100644 --- a/test/system/generator/generator.go +++ b/test/system/generator/generator.go @@ -30,6 +30,11 @@ type Cluster struct { } `yaml:"nodes"` } +const ( + defaultNamespace = "default" + defaultServiceAccount = "default" +) + var ( Containers = make(map[string]graph.Container) Pods = make(map[string]graph.Pod) @@ -211,12 +216,18 @@ func ProcessFile(basePath string, file os.FileInfo) { } func AddPodToList(pod *corev1.Pod) error { - fmt.Printf("pod name: %s\n", pod.Name) + fmt.Printf("pod name: %s (%+v)\n", pod.Name, pod) + pod.Namespace = defaultNamespace storePod := store.Pod{ K8: *pod, } conv := converter.GraphConverter{} convertedPod, err := conv.Pod(&storePod) + // if we haven't defined the service account in the yaml file, k8s will do it for us. + if convertedPod.ServiceAccount == "" { + convertedPod.ServiceAccount = defaultServiceAccount + } + fmt.Printf("pod converted: %+v\n", convertedPod) if err != nil { return err } @@ -303,6 +314,8 @@ func GeneratePodTemplate() ([]byte, error) { IsNamespaced: {{.IsNamespaced}}, Namespace: "{{.Namespace}}", Compromised: shared.CompromiseNone, + ServiceAccount: "{{.ServiceAccount}}", + SharedProcessNamespace: {{.SharedProcessNamespace}}, Critical: false, },{{ end }} } diff --git a/test/system/graph_vertex_test.go b/test/system/graph_vertex_test.go index 0b27fa199..930be7e7c 100644 --- a/test/system/graph_vertex_test.go +++ b/test/system/graph_vertex_test.go @@ -2,6 +2,7 @@ package system import ( "context" + "strings" "testing" "time" @@ -18,7 +19,7 @@ import ( //go:generate go run ./generator ../setup/test-cluster ./vertex.gen.go -var containerToVerify = []string{ +var containerToSkip = []string{ "kube-apiserver", "kindnet-cni", "kube-controller-manager", @@ -30,8 +31,51 @@ var containerToVerify = []string{ "local-path-provisioner", } +var podToSkip = []string{ + "kube-apiserver", + "kindnet", + "kube-controller-manager", + "kube-apiserver", + "kube-proxy", + "kube-scheduler", + "coredns", + "etcd", + "local-path-provisioner", +} + +func prefixInSlice(str string, list []string) bool { + for _, l := range list { + if strings.HasPrefix(str, l) { + return true + } + } + return false +} + +// numberOfKindDefaultContainer represent the current base count of containers created by Kind +// 13 is composed of: +// - 3 "kindnet" (1 per node) +// - 3 "kubeproxy" (1 per node) +// - 2 coredns +// - 1 etcd +// - 1 kube-scheduler +// - 1 kube-apiserver +// - 1 kube-controller +// - 1 local-path-provisioner const numberOfKindDefaultContainer = 13 +// numberOfKindDefaultPod represent the current base count of containers created by Kind +// 13 is composed of: +// - 3 "kindnet" (1 per node) +// - 3 "kubeproxy" (1 per node) +// - 2 coredns +// - 1 etcd +// - 1 kube-scheduler +// - 1 kube-apiserver +// - 1 kube-controller +// - 1 local-path-provisioner +const numberOfKindDefaultPod = 13 + type VertexTestSuite struct { suite.Suite gdb graphdb.Provider @@ -96,7 +140,7 @@ func (suite *VertexTestSuite) TestVertexContainer() { suite.True(ok, "failed to convert privileged field to bool") // We skip these because they are built by Kind itself - if slices.Contains(containerToVerify, containerName) { + if slices.Contains(containerToSkip, containerName) { continue } @@ -160,15 +204,54 @@ func (suite *VertexTestSuite) TestVertexNode() { suite.Equal(expectedNodes, resultsMap) } -// func (suite *VertexTestSuite) TestVertexPod() { -// g := gremlingo.Traversal_().WithRemote(suite.client) -// results, err := g.V().HasLabel(vertex.PodLabel).ElementMap().ToList() -// suite.NoError(err) -// suite.T().Errorf("results: %s", results) -// for _, res := range results { -// suite.T().Errorf("res: %s", res.String()) -// } -// } +func (suite *VertexTestSuite) TestVertexPod() { + results, err := suite.g.V().HasLabel(vertex.PodLabel).ElementMap().ToList() + suite.NoError(err) + + suite.Equal(len(expectedPods)+numberOfKindDefaultPod, len(results)) + resultsMap := map[string]graph.Pod{} + for _, res := range results { + res := res.GetInterface() + converted := res.(map[any]any) + + podName, ok := converted["name"].(string) + suite.True(ok, "failed to convert pod name to string") + + // compromised, ok := converted["compromised"].(int) + // suite.True(ok, "failed to convert compromised field to CompromiseType") + + isNamespaced, ok := converted["isNamespaced"].(bool) + suite.True(ok, "failed to convert isNamespaced field to bool") + + namespace, ok := converted["namespace"].(string) + suite.True(ok, "failed to convert namespace field to string") + + critical, ok := converted["critical"].(bool) + suite.True(ok, "failed to convert critical field to bool") + + sharedProcessNamespace, ok := converted["sharedProcessNamespace"].(bool) + suite.True(ok, "failed to convert sharedProcessNamespace field to bool") + + serviceAccount, ok := converted["serviceAccount"].(string) + suite.True(ok, "failed to convert serviceAccount field to bool") + + // We skip pods created by kind automatically + if prefixInSlice(podName, podToSkip) { + continue + } + + resultsMap[podName] = graph.Pod{ + Name: podName, + ServiceAccount: serviceAccount, + // Compromised: shared.CompromiseType(compromised), + SharedProcessNamespace: sharedProcessNamespace, + IsNamespaced: isNamespaced, + Namespace: namespace, + Critical: critical, + } + } + suite.Equal(expectedPods, resultsMap) +} // func (suite *VertexTestSuite) TestVertexRole() { // g := gremlingo.Traversal_().WithRemote(suite.client) diff --git a/test/system/vertex.gen.go b/test/system/vertex.gen.go index 193ea0332..5d0ab8db0 100644 --- a/test/system/vertex.gen.go +++ b/test/system/vertex.gen.go @@ -1,5 +1,5 @@ // PLEASE DO NOT EDIT -// THIS HAS BEEN GENERATED AUTOMATICALLY on 2023-06-15 19:10 +// THIS HAS BEEN GENERATED AUTOMATICALLY on 2023-06-16 10:38 // // Generate it with "go generate ./..." // @@ -23,105 +23,131 @@ var expectedPods = map[string]graph.Pod{ "impersonate-pod": { StoreID: "", Name: "impersonate-pod", - IsNamespaced: false, - Namespace: "", + IsNamespaced: true, + Namespace: "default", Compromised: shared.CompromiseNone, + ServiceAccount: "impersonate-sa", + SharedProcessNamespace: false, Critical: false, }, "modload-pod": { StoreID: "", Name: "modload-pod", - IsNamespaced: false, - Namespace: "", + IsNamespaced: true, + Namespace: "default", Compromised: shared.CompromiseNone, + ServiceAccount: "default", + SharedProcessNamespace: false, Critical: false, }, "netadmin-pod": { StoreID: "", Name: "netadmin-pod", - IsNamespaced: false, - Namespace: "", + IsNamespaced: true, + Namespace: "default", Compromised: shared.CompromiseNone, + ServiceAccount: "default", + SharedProcessNamespace: false, Critical: false, }, "nsenter-pod": { StoreID: "", Name: "nsenter-pod", - IsNamespaced: false, - Namespace: "", + IsNamespaced: true, + Namespace: "default", Compromised: shared.CompromiseNone, + ServiceAccount: "default", + SharedProcessNamespace: false, Critical: false, }, "pod-create-pod": { StoreID: "", Name: "pod-create-pod", - IsNamespaced: false, - Namespace: "", + IsNamespaced: true, + Namespace: "default", Compromised: shared.CompromiseNone, + ServiceAccount: "pod-create-sa", + SharedProcessNamespace: false, Critical: false, }, "pod-patch-pod": { StoreID: "", Name: "pod-patch-pod", - IsNamespaced: false, - Namespace: "", + IsNamespaced: true, + Namespace: "default", Compromised: shared.CompromiseNone, + ServiceAccount: "pod-patch-sa", + SharedProcessNamespace: false, Critical: false, }, "priv-pod": { StoreID: "", Name: "priv-pod", - IsNamespaced: false, - Namespace: "", + IsNamespaced: true, + Namespace: "default", Compromised: shared.CompromiseNone, + ServiceAccount: "default", + SharedProcessNamespace: false, Critical: false, }, "rolebind-pod": { StoreID: "", Name: "rolebind-pod", - IsNamespaced: false, - Namespace: "", + IsNamespaced: true, + Namespace: "default", Compromised: shared.CompromiseNone, + ServiceAccount: "rolebind-sa", + SharedProcessNamespace: false, Critical: false, }, "sharedps-pod": { StoreID: "", Name: "sharedps-pod", - IsNamespaced: false, - Namespace: "", + IsNamespaced: true, + Namespace: "default", Compromised: shared.CompromiseNone, + ServiceAccount: "default", + SharedProcessNamespace: true, Critical: false, }, "tokenget-pod": { StoreID: "", Name: "tokenget-pod", - IsNamespaced: false, - Namespace: "", + IsNamespaced: true, + Namespace: "default", Compromised: shared.CompromiseNone, + ServiceAccount: "tokenget-sa", + SharedProcessNamespace: false, Critical: false, }, "tokenlist-pod": { StoreID: "", Name: "tokenlist-pod", - IsNamespaced: false, - Namespace: "", + IsNamespaced: true, + Namespace: "default", Compromised: shared.CompromiseNone, + ServiceAccount: "tokenlist-sa", + SharedProcessNamespace: false, Critical: false, }, "umh-core-pod": { StoreID: "", Name: "umh-core-pod", - IsNamespaced: false, - Namespace: "", + IsNamespaced: true, + Namespace: "default", Compromised: shared.CompromiseNone, + ServiceAccount: "default", + SharedProcessNamespace: false, Critical: false, }, "varlog-pod": { StoreID: "", Name: "varlog-pod", - IsNamespaced: false, - Namespace: "", + IsNamespaced: true, + Namespace: "default", Compromised: shared.CompromiseNone, + ServiceAccount: "default", + SharedProcessNamespace: false, Critical: false, }, } @@ -185,7 +211,7 @@ var expectedContainers = map[string]graph.Container{ RunAsUser: 0, Ports: []int{}, Pod: "impersonate-pod", - Node: "", + // Node: "", Compromised: 0, Critical: false, }, @@ -205,7 +231,7 @@ var expectedContainers = map[string]graph.Container{ RunAsUser: 0, Ports: []int{}, Pod: "modload-pod", - Node: "", + // Node: "", Compromised: 0, Critical: false, }, @@ -225,7 +251,7 @@ var expectedContainers = map[string]graph.Container{ RunAsUser: 0, Ports: []int{}, Pod: "netadmin-pod", - Node: "", + // Node: "", Compromised: 0, Critical: false, }, @@ -245,7 +271,7 @@ var expectedContainers = map[string]graph.Container{ RunAsUser: 0, Ports: []int{}, Pod: "nsenter-pod", - Node: "", + // Node: "", Compromised: 0, Critical: false, }, @@ -265,7 +291,7 @@ var expectedContainers = map[string]graph.Container{ RunAsUser: 0, Ports: []int{}, Pod: "pod-create-pod", - Node: "", + // Node: "", Compromised: 0, Critical: false, }, @@ -285,7 +311,7 @@ var expectedContainers = map[string]graph.Container{ RunAsUser: 0, Ports: []int{}, Pod: "pod-patch-pod", - Node: "", + // Node: "", Compromised: 0, Critical: false, }, @@ -305,7 +331,7 @@ var expectedContainers = map[string]graph.Container{ RunAsUser: 0, Ports: []int{}, Pod: "priv-pod", - Node: "", + // Node: "", Compromised: 0, Critical: false, }, @@ -325,7 +351,7 @@ var expectedContainers = map[string]graph.Container{ RunAsUser: 0, Ports: []int{}, Pod: "rolebind-pod", - Node: "", + // Node: "", Compromised: 0, Critical: false, }, @@ -345,7 +371,7 @@ var expectedContainers = map[string]graph.Container{ RunAsUser: 0, Ports: []int{}, Pod: "sharedps-pod", - Node: "", + // Node: "", Compromised: 0, Critical: false, }, @@ -365,7 +391,7 @@ var expectedContainers = map[string]graph.Container{ RunAsUser: 0, Ports: []int{}, Pod: "tokenget-pod", - Node: "", + // Node: "", Compromised: 0, Critical: false, }, @@ -385,7 +411,7 @@ var expectedContainers = map[string]graph.Container{ RunAsUser: 0, Ports: []int{}, Pod: "tokenlist-pod", - Node: "", + // Node: "", Compromised: 0, Critical: false, }, @@ -405,7 +431,7 @@ var expectedContainers = map[string]graph.Container{ RunAsUser: 0, Ports: []int{}, Pod: "umh-core-pod", - Node: "", + // Node: "", Compromised: 0, Critical: false, }, @@ -425,7 +451,7 @@ var expectedContainers = map[string]graph.Container{ RunAsUser: 0, Ports: []int{}, Pod: "varlog-pod", - Node: "", + // Node: "", Compromised: 0, Critical: false, }, From 314d564856ff7e83818ce350903100e5dd9fddc2 Mon Sep 17 00:00:00 2001 From: Edouard Schweisguth Date: Fri, 16 Jun 2023 11:00:19 +0200 Subject: [PATCH 09/25] Add identity test --- test/system/graph_vertex_test.go | 88 +++++++++++++++++++++++++------- 1 file changed, 69 insertions(+), 19 deletions(-) diff --git a/test/system/graph_vertex_test.go b/test/system/graph_vertex_test.go index 930be7e7c..e1224e78f 100644 --- a/test/system/graph_vertex_test.go +++ b/test/system/graph_vertex_test.go @@ -253,25 +253,75 @@ func (suite *VertexTestSuite) TestVertexPod() { suite.Equal(expectedPods, resultsMap) } -// func (suite *VertexTestSuite) TestVertexRole() { -// g := gremlingo.Traversal_().WithRemote(suite.client) -// results, err := g.V().HasLabel(vertex.RoleLabel).ElementMap().ToList() -// suite.NoError(err) -// suite.T().Errorf("results: %s", results) -// for _, res := range results { -// suite.T().Errorf("res: %s", res.String()) -// } -// } - -// func (suite *VertexTestSuite) TestVertexVolume() { -// g := gremlingo.Traversal_().WithRemote(suite.client) -// results, err := g.V().HasLabel(vertex.VolumeLabel).ElementMap().ToList() -// suite.NoError(err) -// suite.T().Errorf("results: %s", results) -// for _, res := range results { -// suite.T().Errorf("res: %s", res.String()) -// } -// } +func (suite *VertexTestSuite) TestVertexRole() { + results, err := suite.g.V().HasLabel(vertex.RoleLabel).Has("name", "impersonate").ElementMap().ToList() + suite.NoError(err) + suite.Equal(1, len(results)) + + results, err = suite.g.V().HasLabel(vertex.RoleLabel).Has("name", "read-secrets").ElementMap().ToList() + suite.NoError(err) + suite.Equal(1, len(results)) + + results, err = suite.g.V().HasLabel(vertex.RoleLabel).Has("name", "create-pods").ElementMap().ToList() + suite.NoError(err) + suite.Equal(1, len(results)) + + results, err = suite.g.V().HasLabel(vertex.RoleLabel).Has("name", "patch-pods").ElementMap().ToList() + suite.NoError(err) + suite.Equal(1, len(results)) + + results, err = suite.g.V().HasLabel(vertex.RoleLabel).Has("name", "rolebind").ElementMap().ToList() + suite.NoError(err) + suite.Equal(1, len(results)) +} + +func (suite *VertexTestSuite) TestVertexVolume() { + results, err := suite.g.V().HasLabel(vertex.VolumeLabel).ElementMap().ToList() + suite.NoError(err) + suite.Equal(54, len(results)) + + results, err = suite.g.V().HasLabel(vertex.VolumeLabel).Has("path", "/proc/sys/kernel").Has("name", "nodeproc").ElementMap().ToList() + suite.NoError(err) + suite.Equal(1, len(results)) + + results, err = suite.g.V().HasLabel(vertex.VolumeLabel).Has("path", "/lib/modules").Has("name", "lib-modules").ElementMap().ToList() + suite.NoError(err) + suite.Greater(len(results), 1) // Not sure why it has "6" + + results, err = suite.g.V().HasLabel(vertex.VolumeLabel).Has("path", "/var/log").Has("name", "nodelog").ElementMap().ToList() + suite.NoError(err) + suite.Equal(len(results), 1) +} + +func (suite *VertexTestSuite) TestVertexIdentity() { + results, err := suite.g.V().HasLabel(vertex.IdentityLabel).ElementMap().ToList() + suite.NoError(err) + suite.Greater(len(results), 50) + + results, err = suite.g.V().HasLabel(vertex.IdentityLabel).Has("name", "tokenget-sa").ElementMap().ToList() + suite.NoError(err) + suite.Equal(len(results), 1) + + results, err = suite.g.V().HasLabel(vertex.IdentityLabel).Has("name", "impersonate-sa").ElementMap().ToList() + suite.NoError(err) + suite.Equal(len(results), 1) + + results, err = suite.g.V().HasLabel(vertex.IdentityLabel).Has("name", "tokenlist-sa").ElementMap().ToList() + suite.NoError(err) + suite.Equal(len(results), 1) + + results, err = suite.g.V().HasLabel(vertex.IdentityLabel).Has("name", "pod-patch-sa").ElementMap().ToList() + suite.NoError(err) + suite.Equal(len(results), 1) + + results, err = suite.g.V().HasLabel(vertex.IdentityLabel).Has("name", "rolebind-sa").ElementMap().ToList() + suite.NoError(err) + suite.Equal(len(results), 1) + + results, err = suite.g.V().HasLabel(vertex.IdentityLabel).Has("name", "pod-create-sa").ElementMap().ToList() + suite.NoError(err) + suite.Equal(len(results), 1) +} func TestVertexTestSuite(t *testing.T) { suite.Run(t, new(VertexTestSuite)) From 7ac69f067ccc126cfd0ffa36ae25bbac61a0ffc6 Mon Sep 17 00:00:00 2001 From: Edouard Schweisguth Date: Fri, 16 Jun 2023 11:05:04 +0200 Subject: [PATCH 10/25] Add token tests --- test/system/graph_vertex_test.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/system/graph_vertex_test.go b/test/system/graph_vertex_test.go index e1224e78f..295cc92b3 100644 --- a/test/system/graph_vertex_test.go +++ b/test/system/graph_vertex_test.go @@ -275,6 +275,16 @@ func (suite *VertexTestSuite) TestVertexRole() { suite.Equal(1, len(results)) } +func (suite *VertexTestSuite) TestVertexToken() { + results, err := suite.g.V().HasLabel(vertex.TokenLabel).Has("type", "ServiceAccount").ElementMap().ToList() + suite.NoError(err) + suite.Equal(6, len(results)) + + results, err = suite.g.V().HasLabel(vertex.TokenLabel).Has("identity", "pod-patch-sa").Has("namespace", "default").Has("critical", false).ElementMap().ToList() + suite.NoError(err) + suite.Equal(1, len(results)) +} + func (suite *VertexTestSuite) TestVertexVolume() { results, err := suite.g.V().HasLabel(vertex.VolumeLabel).ElementMap().ToList() suite.NoError(err) From 9d12b8b3a5f9163676c3a83ddb42cf240c368654 Mon Sep 17 00:00:00 2001 From: Edouard Schweisguth Date: Fri, 16 Jun 2023 11:54:02 +0200 Subject: [PATCH 11/25] Fix CI / kind cluster setup --- .github/workflows/system-test.yml | 8 +++++++- Makefile | 5 ++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/.github/workflows/system-test.yml b/.github/workflows/system-test.yml index b1c26ee43..d3e1fc936 100644 --- a/.github/workflows/system-test.yml +++ b/.github/workflows/system-test.yml @@ -16,10 +16,14 @@ jobs: cluster_name: kubehound.test.local config: test/setup/test-cluster/cluster.yaml wait: 5m + env: + KUBECONFIG: ./test/setup/.kube/config - name: Create K8s resources working-directory: test/setup/ run: bash create-cluster-resources.sh + env: + KUBECONFIG: .kube/config - name: Setup Golang uses: actions/setup-go@v4 @@ -27,4 +31,6 @@ jobs: go-version: "1.20" - name: Run integration Tests - run: make system-test \ No newline at end of file + run: make system-test + env: + KUBECONFIG: .kube/config \ No newline at end of file diff --git a/Makefile b/Makefile index 880fae193..b41418c4d 100644 --- a/Makefile +++ b/Makefile @@ -44,9 +44,8 @@ test: ## Run the full suite of unit tests .PHONY: system-test system-test: ## Run the system tests - # $(MAKE) infra-rm - # $(MAKE) infra-up - cd test/system && export KUBECONFIG=/home/edouard/dd/KubeHound/test/setup/.kube/config && go test -v -timeout "60s" -race ./... + # we print the KUBECONFIG envvar here to make it easier to see what is actively used + cd test/system && export KUBECONFIG=$(ROOT_DIR)/test/setup/.kube/config && bash -c "printenv KUBECONFIG" && go test -v -timeout "60s" -count=1 -race ./... .PHONY: local-cluster-reset local-cluster-reset: ## Destroy the current kind cluster and creates a new one From c18e14cac695eb65d3f46ea42e6d52b4cc1fb735 Mon Sep 17 00:00:00 2001 From: Edouard Schweisguth Date: Fri, 16 Jun 2023 12:20:55 +0200 Subject: [PATCH 12/25] Cleanup --- test/system/generator/generator.go | 20 ++++---------------- test/system/graph_vertex_test.go | 23 +++++++++++++---------- test/system/vertex.gen.go | 2 +- 3 files changed, 18 insertions(+), 27 deletions(-) diff --git a/test/system/generator/generator.go b/test/system/generator/generator.go index 4dc6f3341..c76d5dae1 100644 --- a/test/system/generator/generator.go +++ b/test/system/generator/generator.go @@ -16,7 +16,6 @@ import ( "gopkg.in/yaml.v3" corev1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1" - "k8s.io/api/rbac/v1beta1" "k8s.io/client-go/kubernetes/scheme" ) @@ -172,8 +171,6 @@ func ProcessFile(basePath string, file os.FileInfo) { // now use switch over the type of the object // and match each type-case switch o := obj.(type) { - case *v1.List: - panic("list!") case *v1.Node: err = AddNodeToList(o) if err != nil { @@ -199,24 +196,16 @@ func ProcessFile(basePath string, file os.FileInfo) { fmt.Println("Failed to add container to list:", err) } } - case *v1beta1.Role: - // TODO - case *v1beta1.RoleBinding: - // TODO - case *v1beta1.ClusterRole: - // TODO - case *v1beta1.ClusterRoleBinding: - // TODO - case *v1.ServiceAccount: + //TODO: + // case *v1beta1.Role, *v1beta1.RoleBinding, *v1beta1.ClusterRole, *v1beta1.ClusterRoleBinding: default: - fmt.Printf("Unknown object type: %+v\n", o) - //o is unknown for us + fmt.Printf("(TODO) %T object has not yet been implememented: %+v", o, o) } } } func AddPodToList(pod *corev1.Pod) error { - fmt.Printf("pod name: %s (%+v)\n", pod.Name, pod) + fmt.Printf("pod name: %s\n", pod.Name) pod.Namespace = defaultNamespace storePod := store.Pod{ K8: *pod, @@ -227,7 +216,6 @@ func AddPodToList(pod *corev1.Pod) error { if convertedPod.ServiceAccount == "" { convertedPod.ServiceAccount = defaultServiceAccount } - fmt.Printf("pod converted: %+v\n", convertedPod) if err != nil { return err } diff --git a/test/system/graph_vertex_test.go b/test/system/graph_vertex_test.go index 295cc92b3..cd687a349 100644 --- a/test/system/graph_vertex_test.go +++ b/test/system/graph_vertex_test.go @@ -84,25 +84,26 @@ type VertexTestSuite struct { } func (suite *VertexTestSuite) SetupSuite() { + require := suite.Require() ctx := context.Background() + + // JanusGraph gdb, err := graphdb.Factory(ctx, config.MustLoadConfig("./kubehound.yaml")) - suite.NoError(err) + require.NoError(err, "error deleting the graphdb") suite.gdb = gdb suite.client = gdb.Raw().(*gremlingo.DriverRemoteConnection) suite.g = gremlingo.Traversal_().WithRemote(suite.client) errChan := suite.g.V().Drop().Iterate() err = <-errChan - if err != nil { - suite.Errorf(err, "error deleting the graphdb:\n") - } + require.NoError(err, "error deleting the graphdb") + // Mongo provider, err := storedb.NewMongoProvider(ctx, storedb.MongoLocalDatabaseURL, 1*time.Second) mongoclient := provider.Raw().(*mongo.Client) db := mongoclient.Database("kubehound") err = db.Drop(ctx) - if err != nil { - suite.Errorf(err, "error deleting the mongodb:\n") - } + require.NoError(err, "error deleting the mongodb") + err = runKubeHound() suite.NoError(err) } @@ -121,8 +122,10 @@ func (suite *VertexTestSuite) TestVertexContainer() { suite.True(ok, "failed to convert container name to string") // This is most likely going to be flaky if we try to match these - // nodeName, ok := converted["node"].(string) - // suite.True(ok, "failed to convert node name to string") + // because the node may change between run if they have the same specs + // so we ignore the return value and just check that it exists and is a readable as a string + _, ok = converted["node"].(string) + suite.True(ok, "failed to convert node name to string") podName, ok := converted["pod"].(string) suite.True(ok, "failed to convert pod name to string") @@ -160,7 +163,7 @@ func (suite *VertexTestSuite) TestVertexContainer() { RunAsUser: 0, Ports: []int{}, Pod: podName, - // Node: nodeName, + // Node: nodeName, // see comments for converted["node"].(string) // Compromised: shared.CompromiseType(compromised), Critical: critical, } diff --git a/test/system/vertex.gen.go b/test/system/vertex.gen.go index 5d0ab8db0..f12990d2c 100644 --- a/test/system/vertex.gen.go +++ b/test/system/vertex.gen.go @@ -1,5 +1,5 @@ // PLEASE DO NOT EDIT -// THIS HAS BEEN GENERATED AUTOMATICALLY on 2023-06-16 10:38 +// THIS HAS BEEN GENERATED AUTOMATICALLY on 2023-06-16 12:20 // // Generate it with "go generate ./..." // From f9251e7c54d7e920ce613dc6db672c76e4e59e9f Mon Sep 17 00:00:00 2001 From: Edouard Schweisguth Date: Fri, 16 Jun 2023 12:24:03 +0200 Subject: [PATCH 13/25] cleanup makefile --- Makefile | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index b41418c4d..343b3c031 100644 --- a/Makefile +++ b/Makefile @@ -24,8 +24,12 @@ endif all: build +.PHONEY: generate +generate: ## generate code the application + go generate ./... + .PHONEY: build -build: ## Build the application +build: generate ## Build the application cd cmd && go build -ldflags="-X pkg/config.BuildVersion=$(BUILD_VERSION)" -o ../bin/kubehound kubehound/*.go .PHONY: infra-rm @@ -37,13 +41,13 @@ infra-up: ## Spwan the testing stack docker compose $(DOCKER_COMPOSE_FILE_PATH) up -d .PHONY: test -test: ## Run the full suite of unit tests +test: generate ## Run the full suite of unit tests $(MAKE) infra-rm $(MAKE) infra-up cd pkg && go test ./... .PHONY: system-test -system-test: ## Run the system tests +system-test: generate ## Run the system tests # we print the KUBECONFIG envvar here to make it easier to see what is actively used cd test/system && export KUBECONFIG=$(ROOT_DIR)/test/setup/.kube/config && bash -c "printenv KUBECONFIG" && go test -v -timeout "60s" -count=1 -race ./... From 31c34fb965e8edb4ea5045fd5934e7a7210190d9 Mon Sep 17 00:00:00 2001 From: Edouard Schweisguth Date: Fri, 16 Jun 2023 12:30:25 +0200 Subject: [PATCH 14/25] Update readme --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 9d10f3b30..7df4dcd5a 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,11 @@ The repository includes a suite of system tests that will do the following: The cluster setup and running instances can be found under [test/setup](./test/setup/) +If you need to manually access the system test environement with kubectl and other commands, you'll need to set (assuming you are at the root dir): +```bash +cd test/setup/ && export KUBECONFIG=$(pwd)/.kube/config +``` + ### Requirements + Kind: https://kind.sigs.k8s.io/docs/user/quick-start/#installing-with-a-package-manager From bceebb0b6fdd14f0d4fdd31d7ad0e230f26ab3c1 Mon Sep 17 00:00:00 2001 From: Edouard Schweisguth Date: Fri, 16 Jun 2023 12:37:09 +0200 Subject: [PATCH 15/25] removing generate from tests --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 343b3c031..14dd74403 100644 --- a/Makefile +++ b/Makefile @@ -41,13 +41,13 @@ infra-up: ## Spwan the testing stack docker compose $(DOCKER_COMPOSE_FILE_PATH) up -d .PHONY: test -test: generate ## Run the full suite of unit tests +test: ## Run the full suite of unit tests $(MAKE) infra-rm $(MAKE) infra-up cd pkg && go test ./... .PHONY: system-test -system-test: generate ## Run the system tests +system-test: ## Run the system tests # we print the KUBECONFIG envvar here to make it easier to see what is actively used cd test/system && export KUBECONFIG=$(ROOT_DIR)/test/setup/.kube/config && bash -c "printenv KUBECONFIG" && go test -v -timeout "60s" -count=1 -race ./... From 1dd407133901c21b307f1c3d5b5bce479bf4e603 Mon Sep 17 00:00:00 2001 From: Edouard Schweisguth Date: Fri, 16 Jun 2023 13:05:07 +0200 Subject: [PATCH 16/25] update test config --- test/system/graph_vertex_test.go | 8 +++++--- test/system/kubehound.yaml | 7 ++++++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/test/system/graph_vertex_test.go b/test/system/graph_vertex_test.go index cd687a349..a57e7298f 100644 --- a/test/system/graph_vertex_test.go +++ b/test/system/graph_vertex_test.go @@ -4,7 +4,6 @@ import ( "context" "strings" "testing" - "time" "github.com/DataDog/KubeHound/pkg/config" "github.com/DataDog/KubeHound/pkg/kubehound/graph/vertex" @@ -86,19 +85,22 @@ type VertexTestSuite struct { func (suite *VertexTestSuite) SetupSuite() { require := suite.Require() ctx := context.Background() + cfg := config.MustLoadConfig("./kubehound.yaml") // JanusGraph - gdb, err := graphdb.Factory(ctx, config.MustLoadConfig("./kubehound.yaml")) + gdb, err := graphdb.Factory(ctx, cfg) require.NoError(err, "error deleting the graphdb") suite.gdb = gdb suite.client = gdb.Raw().(*gremlingo.DriverRemoteConnection) + suite.g = gremlingo.Traversal_().WithRemote(suite.client) errChan := suite.g.V().Drop().Iterate() err = <-errChan require.NoError(err, "error deleting the graphdb") // Mongo - provider, err := storedb.NewMongoProvider(ctx, storedb.MongoLocalDatabaseURL, 1*time.Second) + provider, err := storedb.Factory(ctx, cfg) + require.NoError(err, "failed to connect to the mongodb") mongoclient := provider.Raw().(*mongo.Client) db := mongoclient.Database("kubehound") err = db.Drop(ctx) diff --git a/test/system/kubehound.yaml b/test/system/kubehound.yaml index 1548f6ea8..25e96c324 100644 --- a/test/system/kubehound.yaml +++ b/test/system/kubehound.yaml @@ -1,6 +1,11 @@ +storage: + retry_delay: 10s + retry: 5 collector: type: live-k8s-api-collector janusgraph: url: "ws://localhost:8182/gremlin" + connection_timeout: 5s mongodb: - url: "mongodb://localhost:27017" \ No newline at end of file + url: "mongodb://localhost:27017" + connection_timeout: 5s \ No newline at end of file From 81b54a2888727c019605484b868165eaff349302 Mon Sep 17 00:00:00 2001 From: Edouard Schweisguth Date: Fri, 16 Jun 2023 13:55:09 +0200 Subject: [PATCH 17/25] hope? --- Makefile | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 14dd74403..e924c25c4 100644 --- a/Makefile +++ b/Makefile @@ -22,6 +22,13 @@ ifeq (${DD_API_KEY},) DOCKER_COMPOSE_FILE_PATH := -f test/system/docker-compose.yaml endif + +DOCKER_CMD = docker +UNAME_S := $(shell uname -s) +ifeq ($(UNAME_S),Linux) + DOCKER_CMD = sudo docker +endif + all: build .PHONEY: generate @@ -34,11 +41,11 @@ build: generate ## Build the application .PHONY: infra-rm infra-rm: ## Delete the testing stack - docker compose $(DOCKER_COMPOSE_FILE_PATH) rm -fvs + $(DOCKER_CMD) compose $(DOCKER_COMPOSE_FILE_PATH) rm -fvs .PHONY: infra-up infra-up: ## Spwan the testing stack - docker compose $(DOCKER_COMPOSE_FILE_PATH) up -d + $(DOCKER_CMD) compose $(DOCKER_COMPOSE_FILE_PATH) up -d .PHONY: test test: ## Run the full suite of unit tests @@ -48,6 +55,8 @@ test: ## Run the full suite of unit tests .PHONY: system-test system-test: ## Run the system tests + $(MAKE) infra-rm + $(MAKE) infra-up # we print the KUBECONFIG envvar here to make it easier to see what is actively used cd test/system && export KUBECONFIG=$(ROOT_DIR)/test/setup/.kube/config && bash -c "printenv KUBECONFIG" && go test -v -timeout "60s" -count=1 -race ./... From 9507b9c892335e5be9c77cfdb13df1bc0d2a1bf5 Mon Sep 17 00:00:00 2001 From: Edouard Schweisguth Date: Fri, 16 Jun 2023 14:10:14 +0200 Subject: [PATCH 18/25] run go fmt on generated code --- test/system/generator/generator.go | 10 +- test/system/vertex.gen.go | 867 ++++++++++++++--------------- 2 files changed, 438 insertions(+), 439 deletions(-) diff --git a/test/system/generator/generator.go b/test/system/generator/generator.go index c76d5dae1..d1996f907 100644 --- a/test/system/generator/generator.go +++ b/test/system/generator/generator.go @@ -3,6 +3,7 @@ package main import ( "bytes" "fmt" + "go/format" "io/ioutil" "log" "os" @@ -377,11 +378,10 @@ func WriteTemplatesToFile(path string, templates ...[]byte) error { in := bytes.Join(templates, []byte("\n")) // We run go fmt on it so it's "clean" // The formatting is not as strict as our editors config & linter - // clean, err := format.Source(in) - // if err != nil { - // return err - // } - clean := in + clean, err := format.Source(in) + if err != nil { + return err + } _, err = f.Write(clean) if err != nil { return err diff --git a/test/system/vertex.gen.go b/test/system/vertex.gen.go index f12990d2c..7b7c45fd6 100644 --- a/test/system/vertex.gen.go +++ b/test/system/vertex.gen.go @@ -1,8 +1,8 @@ // PLEASE DO NOT EDIT -// THIS HAS BEEN GENERATED AUTOMATICALLY on 2023-06-16 12:20 -// +// THIS HAS BEEN GENERATED AUTOMATICALLY on 2023-06-16 14:09 +// // Generate it with "go generate ./..." -// +// // currently support only: // - nodes // - pods @@ -18,441 +18,440 @@ import ( "github.com/DataDog/KubeHound/pkg/kubehound/models/shared" ) - var expectedPods = map[string]graph.Pod{ - "impersonate-pod": { - StoreID: "", - Name: "impersonate-pod", - IsNamespaced: true, - Namespace: "default", - Compromised: shared.CompromiseNone, - ServiceAccount: "impersonate-sa", - SharedProcessNamespace: false, - Critical: false, - }, - "modload-pod": { - StoreID: "", - Name: "modload-pod", - IsNamespaced: true, - Namespace: "default", - Compromised: shared.CompromiseNone, - ServiceAccount: "default", - SharedProcessNamespace: false, - Critical: false, - }, - "netadmin-pod": { - StoreID: "", - Name: "netadmin-pod", - IsNamespaced: true, - Namespace: "default", - Compromised: shared.CompromiseNone, - ServiceAccount: "default", - SharedProcessNamespace: false, - Critical: false, - }, - "nsenter-pod": { - StoreID: "", - Name: "nsenter-pod", - IsNamespaced: true, - Namespace: "default", - Compromised: shared.CompromiseNone, - ServiceAccount: "default", - SharedProcessNamespace: false, - Critical: false, - }, - "pod-create-pod": { - StoreID: "", - Name: "pod-create-pod", - IsNamespaced: true, - Namespace: "default", - Compromised: shared.CompromiseNone, - ServiceAccount: "pod-create-sa", - SharedProcessNamespace: false, - Critical: false, - }, - "pod-patch-pod": { - StoreID: "", - Name: "pod-patch-pod", - IsNamespaced: true, - Namespace: "default", - Compromised: shared.CompromiseNone, - ServiceAccount: "pod-patch-sa", - SharedProcessNamespace: false, - Critical: false, - }, - "priv-pod": { - StoreID: "", - Name: "priv-pod", - IsNamespaced: true, - Namespace: "default", - Compromised: shared.CompromiseNone, - ServiceAccount: "default", - SharedProcessNamespace: false, - Critical: false, - }, - "rolebind-pod": { - StoreID: "", - Name: "rolebind-pod", - IsNamespaced: true, - Namespace: "default", - Compromised: shared.CompromiseNone, - ServiceAccount: "rolebind-sa", - SharedProcessNamespace: false, - Critical: false, - }, - "sharedps-pod": { - StoreID: "", - Name: "sharedps-pod", - IsNamespaced: true, - Namespace: "default", - Compromised: shared.CompromiseNone, - ServiceAccount: "default", - SharedProcessNamespace: true, - Critical: false, - }, - "tokenget-pod": { - StoreID: "", - Name: "tokenget-pod", - IsNamespaced: true, - Namespace: "default", - Compromised: shared.CompromiseNone, - ServiceAccount: "tokenget-sa", - SharedProcessNamespace: false, - Critical: false, - }, - "tokenlist-pod": { - StoreID: "", - Name: "tokenlist-pod", - IsNamespaced: true, - Namespace: "default", - Compromised: shared.CompromiseNone, - ServiceAccount: "tokenlist-sa", - SharedProcessNamespace: false, - Critical: false, - }, - "umh-core-pod": { - StoreID: "", - Name: "umh-core-pod", - IsNamespaced: true, - Namespace: "default", - Compromised: shared.CompromiseNone, - ServiceAccount: "default", - SharedProcessNamespace: false, - Critical: false, - }, - "varlog-pod": { - StoreID: "", - Name: "varlog-pod", - IsNamespaced: true, - Namespace: "default", - Compromised: shared.CompromiseNone, - ServiceAccount: "default", - SharedProcessNamespace: false, - Critical: false, - }, - } + "impersonate-pod": { + StoreID: "", + Name: "impersonate-pod", + IsNamespaced: true, + Namespace: "default", + Compromised: shared.CompromiseNone, + ServiceAccount: "impersonate-sa", + SharedProcessNamespace: false, + Critical: false, + }, + "modload-pod": { + StoreID: "", + Name: "modload-pod", + IsNamespaced: true, + Namespace: "default", + Compromised: shared.CompromiseNone, + ServiceAccount: "default", + SharedProcessNamespace: false, + Critical: false, + }, + "netadmin-pod": { + StoreID: "", + Name: "netadmin-pod", + IsNamespaced: true, + Namespace: "default", + Compromised: shared.CompromiseNone, + ServiceAccount: "default", + SharedProcessNamespace: false, + Critical: false, + }, + "nsenter-pod": { + StoreID: "", + Name: "nsenter-pod", + IsNamespaced: true, + Namespace: "default", + Compromised: shared.CompromiseNone, + ServiceAccount: "default", + SharedProcessNamespace: false, + Critical: false, + }, + "pod-create-pod": { + StoreID: "", + Name: "pod-create-pod", + IsNamespaced: true, + Namespace: "default", + Compromised: shared.CompromiseNone, + ServiceAccount: "pod-create-sa", + SharedProcessNamespace: false, + Critical: false, + }, + "pod-patch-pod": { + StoreID: "", + Name: "pod-patch-pod", + IsNamespaced: true, + Namespace: "default", + Compromised: shared.CompromiseNone, + ServiceAccount: "pod-patch-sa", + SharedProcessNamespace: false, + Critical: false, + }, + "priv-pod": { + StoreID: "", + Name: "priv-pod", + IsNamespaced: true, + Namespace: "default", + Compromised: shared.CompromiseNone, + ServiceAccount: "default", + SharedProcessNamespace: false, + Critical: false, + }, + "rolebind-pod": { + StoreID: "", + Name: "rolebind-pod", + IsNamespaced: true, + Namespace: "default", + Compromised: shared.CompromiseNone, + ServiceAccount: "rolebind-sa", + SharedProcessNamespace: false, + Critical: false, + }, + "sharedps-pod": { + StoreID: "", + Name: "sharedps-pod", + IsNamespaced: true, + Namespace: "default", + Compromised: shared.CompromiseNone, + ServiceAccount: "default", + SharedProcessNamespace: true, + Critical: false, + }, + "tokenget-pod": { + StoreID: "", + Name: "tokenget-pod", + IsNamespaced: true, + Namespace: "default", + Compromised: shared.CompromiseNone, + ServiceAccount: "tokenget-sa", + SharedProcessNamespace: false, + Critical: false, + }, + "tokenlist-pod": { + StoreID: "", + Name: "tokenlist-pod", + IsNamespaced: true, + Namespace: "default", + Compromised: shared.CompromiseNone, + ServiceAccount: "tokenlist-sa", + SharedProcessNamespace: false, + Critical: false, + }, + "umh-core-pod": { + StoreID: "", + Name: "umh-core-pod", + IsNamespaced: true, + Namespace: "default", + Compromised: shared.CompromiseNone, + ServiceAccount: "default", + SharedProcessNamespace: false, + Critical: false, + }, + "varlog-pod": { + StoreID: "", + Name: "varlog-pod", + IsNamespaced: true, + Namespace: "default", + Compromised: shared.CompromiseNone, + ServiceAccount: "default", + SharedProcessNamespace: false, + Critical: false, + }, +} var expectedNodes = map[string]graph.Node{ - "kubehound.test.local-control-plane": { - StoreID: "", - Name: "kubehound.test.local-control-plane", - IsNamespaced: false, - Namespace: "", - Compromised: shared.CompromiseNone, - Critical: false, - }, - "kubehound.test.local-worker": { - StoreID: "", - Name: "kubehound.test.local-worker", - IsNamespaced: false, - Namespace: "", - Compromised: shared.CompromiseNone, - Critical: false, - }, - "kubehound.test.local-worker2": { - StoreID: "", - Name: "kubehound.test.local-worker2", - IsNamespaced: false, - Namespace: "", - Compromised: shared.CompromiseNone, - Critical: false, - }, - } + "kubehound.test.local-control-plane": { + StoreID: "", + Name: "kubehound.test.local-control-plane", + IsNamespaced: false, + Namespace: "", + Compromised: shared.CompromiseNone, + Critical: false, + }, + "kubehound.test.local-worker": { + StoreID: "", + Name: "kubehound.test.local-worker", + IsNamespaced: false, + Namespace: "", + Compromised: shared.CompromiseNone, + Critical: false, + }, + "kubehound.test.local-worker2": { + StoreID: "", + Name: "kubehound.test.local-worker2", + IsNamespaced: false, + Namespace: "", + Compromised: shared.CompromiseNone, + Critical: false, + }, +} var expectedVolumes = map[string]graph.Volume{ - "nodelog": { - StoreID: "", - Name: "nodelog", - Type: "HostPath", - Path: "/var/log", - }, - "nodeproc": { - StoreID: "", - Name: "nodeproc", - Type: "HostPath", - Path: "/proc/sys/kernel", - }, - } + "nodelog": { + StoreID: "", + Name: "nodelog", + Type: "HostPath", + Path: "/var/log", + }, + "nodeproc": { + StoreID: "", + Name: "nodeproc", + Type: "HostPath", + Path: "/proc/sys/kernel", + }, +} var expectedContainers = map[string]graph.Container{ - "impersonate-pod": { - StoreID: "", - Name: "impersonate-pod", - Image: "ubuntu", - Command: []string{}, - Args: []string{}, - Capabilities: []string{}, - Privileged: false, - PrivEsc: false, - HostPID: false, - HostPath: false, - HostIPC: false, - HostNetwork: false, - RunAsUser: 0, - Ports: []int{}, - Pod: "impersonate-pod", - // Node: "", - Compromised: 0, - Critical: false, - }, - "modload-pod": { - StoreID: "", - Name: "modload-pod", - Image: "ubuntu", - Command: []string{}, - Args: []string{}, - Capabilities: []string{}, - Privileged: false, - PrivEsc: false, - HostPID: false, - HostPath: false, - HostIPC: false, - HostNetwork: false, - RunAsUser: 0, - Ports: []int{}, - Pod: "modload-pod", - // Node: "", - Compromised: 0, - Critical: false, - }, - "netadmin-pod": { - StoreID: "", - Name: "netadmin-pod", - Image: "ubuntu", - Command: []string{}, - Args: []string{}, - Capabilities: []string{}, - Privileged: false, - PrivEsc: false, - HostPID: false, - HostPath: false, - HostIPC: false, - HostNetwork: false, - RunAsUser: 0, - Ports: []int{}, - Pod: "netadmin-pod", - // Node: "", - Compromised: 0, - Critical: false, - }, - "nsenter-pod": { - StoreID: "", - Name: "nsenter-pod", - Image: "ubuntu", - Command: []string{}, - Args: []string{}, - Capabilities: []string{}, - Privileged: true, - PrivEsc: false, - HostPID: false, - HostPath: false, - HostIPC: false, - HostNetwork: false, - RunAsUser: 0, - Ports: []int{}, - Pod: "nsenter-pod", - // Node: "", - Compromised: 0, - Critical: false, - }, - "pod-create-pod": { - StoreID: "", - Name: "pod-create-pod", - Image: "ubuntu", - Command: []string{}, - Args: []string{}, - Capabilities: []string{}, - Privileged: false, - PrivEsc: false, - HostPID: false, - HostPath: false, - HostIPC: false, - HostNetwork: false, - RunAsUser: 0, - Ports: []int{}, - Pod: "pod-create-pod", - // Node: "", - Compromised: 0, - Critical: false, - }, - "pod-patch-pod": { - StoreID: "", - Name: "pod-patch-pod", - Image: "ubuntu", - Command: []string{}, - Args: []string{}, - Capabilities: []string{}, - Privileged: false, - PrivEsc: false, - HostPID: false, - HostPath: false, - HostIPC: false, - HostNetwork: false, - RunAsUser: 0, - Ports: []int{}, - Pod: "pod-patch-pod", - // Node: "", - Compromised: 0, - Critical: false, - }, - "priv-pod": { - StoreID: "", - Name: "priv-pod", - Image: "ubuntu", - Command: []string{}, - Args: []string{}, - Capabilities: []string{}, - Privileged: true, - PrivEsc: false, - HostPID: false, - HostPath: false, - HostIPC: false, - HostNetwork: false, - RunAsUser: 0, - Ports: []int{}, - Pod: "priv-pod", - // Node: "", - Compromised: 0, - Critical: false, - }, - "rolebind-pod": { - StoreID: "", - Name: "rolebind-pod", - Image: "ubuntu", - Command: []string{}, - Args: []string{}, - Capabilities: []string{}, - Privileged: false, - PrivEsc: false, - HostPID: false, - HostPath: false, - HostIPC: false, - HostNetwork: false, - RunAsUser: 0, - Ports: []int{}, - Pod: "rolebind-pod", - // Node: "", - Compromised: 0, - Critical: false, - }, - "sharedps-pod": { - StoreID: "", - Name: "sharedps-pod", - Image: "ubuntu", - Command: []string{}, - Args: []string{}, - Capabilities: []string{}, - Privileged: false, - PrivEsc: false, - HostPID: false, - HostPath: false, - HostIPC: false, - HostNetwork: false, - RunAsUser: 0, - Ports: []int{}, - Pod: "sharedps-pod", - // Node: "", - Compromised: 0, - Critical: false, - }, - "tokenget-pod": { - StoreID: "", - Name: "tokenget-pod", - Image: "ubuntu", - Command: []string{}, - Args: []string{}, - Capabilities: []string{}, - Privileged: false, - PrivEsc: false, - HostPID: false, - HostPath: false, - HostIPC: false, - HostNetwork: false, - RunAsUser: 0, - Ports: []int{}, - Pod: "tokenget-pod", - // Node: "", - Compromised: 0, - Critical: false, - }, - "tokenlist-pod": { - StoreID: "", - Name: "tokenlist-pod", - Image: "ubuntu", - Command: []string{}, - Args: []string{}, - Capabilities: []string{}, - Privileged: false, - PrivEsc: false, - HostPID: false, - HostPath: false, - HostIPC: false, - HostNetwork: false, - RunAsUser: 0, - Ports: []int{}, - Pod: "tokenlist-pod", - // Node: "", - Compromised: 0, - Critical: false, - }, - "umh-core-pod": { - StoreID: "", - Name: "umh-core-pod", - Image: "ubuntu", - Command: []string{}, - Args: []string{}, - Capabilities: []string{}, - Privileged: false, - PrivEsc: false, - HostPID: false, - HostPath: false, - HostIPC: false, - HostNetwork: false, - RunAsUser: 0, - Ports: []int{}, - Pod: "umh-core-pod", - // Node: "", - Compromised: 0, - Critical: false, - }, - "varlog-pod": { - StoreID: "", - Name: "varlog-pod", - Image: "ubuntu", - Command: []string{}, - Args: []string{}, - Capabilities: []string{}, - Privileged: false, - PrivEsc: false, - HostPID: false, - HostPath: false, - HostIPC: false, - HostNetwork: false, - RunAsUser: 0, - Ports: []int{}, - Pod: "varlog-pod", - // Node: "", - Compromised: 0, - Critical: false, - }, - } + "impersonate-pod": { + StoreID: "", + Name: "impersonate-pod", + Image: "ubuntu", + Command: []string{}, + Args: []string{}, + Capabilities: []string{}, + Privileged: false, + PrivEsc: false, + HostPID: false, + HostPath: false, + HostIPC: false, + HostNetwork: false, + RunAsUser: 0, + Ports: []int{}, + Pod: "impersonate-pod", + // Node: "", + Compromised: 0, + Critical: false, + }, + "modload-pod": { + StoreID: "", + Name: "modload-pod", + Image: "ubuntu", + Command: []string{}, + Args: []string{}, + Capabilities: []string{}, + Privileged: false, + PrivEsc: false, + HostPID: false, + HostPath: false, + HostIPC: false, + HostNetwork: false, + RunAsUser: 0, + Ports: []int{}, + Pod: "modload-pod", + // Node: "", + Compromised: 0, + Critical: false, + }, + "netadmin-pod": { + StoreID: "", + Name: "netadmin-pod", + Image: "ubuntu", + Command: []string{}, + Args: []string{}, + Capabilities: []string{}, + Privileged: false, + PrivEsc: false, + HostPID: false, + HostPath: false, + HostIPC: false, + HostNetwork: false, + RunAsUser: 0, + Ports: []int{}, + Pod: "netadmin-pod", + // Node: "", + Compromised: 0, + Critical: false, + }, + "nsenter-pod": { + StoreID: "", + Name: "nsenter-pod", + Image: "ubuntu", + Command: []string{}, + Args: []string{}, + Capabilities: []string{}, + Privileged: true, + PrivEsc: false, + HostPID: false, + HostPath: false, + HostIPC: false, + HostNetwork: false, + RunAsUser: 0, + Ports: []int{}, + Pod: "nsenter-pod", + // Node: "", + Compromised: 0, + Critical: false, + }, + "pod-create-pod": { + StoreID: "", + Name: "pod-create-pod", + Image: "ubuntu", + Command: []string{}, + Args: []string{}, + Capabilities: []string{}, + Privileged: false, + PrivEsc: false, + HostPID: false, + HostPath: false, + HostIPC: false, + HostNetwork: false, + RunAsUser: 0, + Ports: []int{}, + Pod: "pod-create-pod", + // Node: "", + Compromised: 0, + Critical: false, + }, + "pod-patch-pod": { + StoreID: "", + Name: "pod-patch-pod", + Image: "ubuntu", + Command: []string{}, + Args: []string{}, + Capabilities: []string{}, + Privileged: false, + PrivEsc: false, + HostPID: false, + HostPath: false, + HostIPC: false, + HostNetwork: false, + RunAsUser: 0, + Ports: []int{}, + Pod: "pod-patch-pod", + // Node: "", + Compromised: 0, + Critical: false, + }, + "priv-pod": { + StoreID: "", + Name: "priv-pod", + Image: "ubuntu", + Command: []string{}, + Args: []string{}, + Capabilities: []string{}, + Privileged: true, + PrivEsc: false, + HostPID: false, + HostPath: false, + HostIPC: false, + HostNetwork: false, + RunAsUser: 0, + Ports: []int{}, + Pod: "priv-pod", + // Node: "", + Compromised: 0, + Critical: false, + }, + "rolebind-pod": { + StoreID: "", + Name: "rolebind-pod", + Image: "ubuntu", + Command: []string{}, + Args: []string{}, + Capabilities: []string{}, + Privileged: false, + PrivEsc: false, + HostPID: false, + HostPath: false, + HostIPC: false, + HostNetwork: false, + RunAsUser: 0, + Ports: []int{}, + Pod: "rolebind-pod", + // Node: "", + Compromised: 0, + Critical: false, + }, + "sharedps-pod": { + StoreID: "", + Name: "sharedps-pod", + Image: "ubuntu", + Command: []string{}, + Args: []string{}, + Capabilities: []string{}, + Privileged: false, + PrivEsc: false, + HostPID: false, + HostPath: false, + HostIPC: false, + HostNetwork: false, + RunAsUser: 0, + Ports: []int{}, + Pod: "sharedps-pod", + // Node: "", + Compromised: 0, + Critical: false, + }, + "tokenget-pod": { + StoreID: "", + Name: "tokenget-pod", + Image: "ubuntu", + Command: []string{}, + Args: []string{}, + Capabilities: []string{}, + Privileged: false, + PrivEsc: false, + HostPID: false, + HostPath: false, + HostIPC: false, + HostNetwork: false, + RunAsUser: 0, + Ports: []int{}, + Pod: "tokenget-pod", + // Node: "", + Compromised: 0, + Critical: false, + }, + "tokenlist-pod": { + StoreID: "", + Name: "tokenlist-pod", + Image: "ubuntu", + Command: []string{}, + Args: []string{}, + Capabilities: []string{}, + Privileged: false, + PrivEsc: false, + HostPID: false, + HostPath: false, + HostIPC: false, + HostNetwork: false, + RunAsUser: 0, + Ports: []int{}, + Pod: "tokenlist-pod", + // Node: "", + Compromised: 0, + Critical: false, + }, + "umh-core-pod": { + StoreID: "", + Name: "umh-core-pod", + Image: "ubuntu", + Command: []string{}, + Args: []string{}, + Capabilities: []string{}, + Privileged: false, + PrivEsc: false, + HostPID: false, + HostPath: false, + HostIPC: false, + HostNetwork: false, + RunAsUser: 0, + Ports: []int{}, + Pod: "umh-core-pod", + // Node: "", + Compromised: 0, + Critical: false, + }, + "varlog-pod": { + StoreID: "", + Name: "varlog-pod", + Image: "ubuntu", + Command: []string{}, + Args: []string{}, + Capabilities: []string{}, + Privileged: false, + PrivEsc: false, + HostPID: false, + HostPath: false, + HostIPC: false, + HostNetwork: false, + RunAsUser: 0, + Ports: []int{}, + Pod: "varlog-pod", + // Node: "", + Compromised: 0, + Critical: false, + }, +} From 68ba5e877ed03a84529c086b1a1369355befca9c Mon Sep 17 00:00:00 2001 From: Edouard Schweisguth Date: Fri, 16 Jun 2023 15:42:15 +0200 Subject: [PATCH 19/25] Remove unneeded cleanup --- test/system/graph_vertex_test.go | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/test/system/graph_vertex_test.go b/test/system/graph_vertex_test.go index a57e7298f..d7a72151f 100644 --- a/test/system/graph_vertex_test.go +++ b/test/system/graph_vertex_test.go @@ -9,10 +9,8 @@ import ( "github.com/DataDog/KubeHound/pkg/kubehound/graph/vertex" "github.com/DataDog/KubeHound/pkg/kubehound/models/graph" "github.com/DataDog/KubeHound/pkg/kubehound/storage/graphdb" - "github.com/DataDog/KubeHound/pkg/kubehound/storage/storedb" gremlingo "github.com/apache/tinkerpop/gremlin-go/driver" "github.com/stretchr/testify/suite" - "go.mongodb.org/mongo-driver/mongo" "golang.org/x/exp/slices" ) @@ -94,20 +92,6 @@ func (suite *VertexTestSuite) SetupSuite() { suite.client = gdb.Raw().(*gremlingo.DriverRemoteConnection) suite.g = gremlingo.Traversal_().WithRemote(suite.client) - errChan := suite.g.V().Drop().Iterate() - err = <-errChan - require.NoError(err, "error deleting the graphdb") - - // Mongo - provider, err := storedb.Factory(ctx, cfg) - require.NoError(err, "failed to connect to the mongodb") - mongoclient := provider.Raw().(*mongo.Client) - db := mongoclient.Database("kubehound") - err = db.Drop(ctx) - require.NoError(err, "error deleting the mongodb") - - err = runKubeHound() - suite.NoError(err) } func (suite *VertexTestSuite) TestVertexContainer() { From 805b0bbc904a61b0536731d124c75d54de823e50 Mon Sep 17 00:00:00 2001 From: Edouard Schweisguth Date: Fri, 16 Jun 2023 19:55:16 +0200 Subject: [PATCH 20/25] this is trully annoying, EOD push --- Makefile | 4 ++-- test/setup/.env.local | 3 ++- test/setup/manage-cluster.sh | 5 +++-- test/setup/util.sh | 14 +++++++++++++- 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index e924c25c4..10df041fa 100644 --- a/Makefile +++ b/Makefile @@ -31,11 +31,11 @@ endif all: build -.PHONEY: generate +.PHONY: generate generate: ## generate code the application go generate ./... -.PHONEY: build +.PHONY: build build: generate ## Build the application cd cmd && go build -ldflags="-X pkg/config.BuildVersion=$(BUILD_VERSION)" -o ../bin/kubehound kubehound/*.go diff --git a/test/setup/.env.local b/test/setup/.env.local index 2deb9e724..9f2c046a5 100644 --- a/test/setup/.env.local +++ b/test/setup/.env.local @@ -1,2 +1,3 @@ CLUSTER_NAME=kubehound.test.local -CONFIG_DIR=./test-cluster \ No newline at end of file +CONFIG_DIR=./test/setup/test-cluster +KUBECONFIG=./test/setup/.kube/config \ No newline at end of file diff --git a/test/setup/manage-cluster.sh b/test/setup/manage-cluster.sh index 438e4152c..6be1046cd 100755 --- a/test/setup/manage-cluster.sh +++ b/test/setup/manage-cluster.sh @@ -11,10 +11,11 @@ PROJECT_MAN="options: [create | destroy]" function create_cluster(){ echo "[*] Creating test cluster "${CLUSTER_NAME}" via kind" - kind create cluster \ + $KIND create cluster \ --name "${CLUSTER_NAME}" \ --config "${CONFIG_DIR}/cluster.yaml" \ + echo "Using KUBECONFIG: $(printenv KUBECONFIG)" kubectl cluster-info --context "kind-${CLUSTER_NAME}" echo "[*] Cluster ${CLUSTER_NAME} configuration complete" @@ -22,7 +23,7 @@ function create_cluster(){ function destroy_cluster(){ echo "[*] Destroying test cluster "${CLUSTER_NAME}" via kind" - kind delete cluster --name "${CLUSTER_NAME}" + $KIND delete cluster --name "${CLUSTER_NAME}" } case $SCRIPT_ACTION in diff --git a/test/setup/util.sh b/test/setup/util.sh index d758f402e..064953688 100644 --- a/test/setup/util.sh +++ b/test/setup/util.sh @@ -33,4 +33,16 @@ function load_env(){ fi } -load_env \ No newline at end of file +load_env + +# post load env +KIND=kind +if [[ "$OSTYPE" == "linux-gnu"* ]]; then + KIND="sudo kind" +fi + +KIND="$KIND --kubeconfig $KUBECONFIG" +if [ -f $KUBECONFIG ]; then + sudo chown $USER:$USER $KUBECONFIG +fi +echo "Using KUBECONFIG: $(printenv KUBECONFIG)" \ No newline at end of file From 974538198ae37fbaba33a3602a825a209d031a7f Mon Sep 17 00:00:00 2001 From: Edouard Schweisguth Date: Mon, 19 Jun 2023 10:10:18 +0200 Subject: [PATCH 21/25] maybe now? --- test/setup/.env.local | 2 +- test/setup/manage-cluster-resources.sh | 1 + test/system/graph_vertex_test.go | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/test/setup/.env.local b/test/setup/.env.local index 9f2c046a5..ef3f60cfb 100644 --- a/test/setup/.env.local +++ b/test/setup/.env.local @@ -1,3 +1,3 @@ CLUSTER_NAME=kubehound.test.local -CONFIG_DIR=./test/setup/test-cluster +CONFIG_DIR=./test-cluster KUBECONFIG=./test/setup/.kube/config \ No newline at end of file diff --git a/test/setup/manage-cluster-resources.sh b/test/setup/manage-cluster-resources.sh index f8c3f5b64..fe5240cec 100755 --- a/test/setup/manage-cluster-resources.sh +++ b/test/setup/manage-cluster-resources.sh @@ -13,6 +13,7 @@ function handle_resources(){ _printf_warn "$2 test resources via kubectl apply" for attack in ${SCRIPT_DIR}/${CONFIG_DIR}/attacks/*.yaml; do [ -e "$attack" ] || continue + echo "$attack" # since deletion can take some times, || true to be able to retry in case of C-C kubectl $1 -f "$attack" --context "kind-${CLUSTER_NAME}" || true done diff --git a/test/system/graph_vertex_test.go b/test/system/graph_vertex_test.go index d7a72151f..6aa8774ab 100644 --- a/test/system/graph_vertex_test.go +++ b/test/system/graph_vertex_test.go @@ -98,7 +98,7 @@ func (suite *VertexTestSuite) TestVertexContainer() { results, err := suite.g.V().HasLabel(vertex.ContainerLabel).ElementMap().ToList() suite.NoError(err) - suite.Equal(len(expectedContainers)+numberOfKindDefaultContainer, len(results)) + suite.Equal(len(expectedContainers), len(results)-numberOfKindDefaultContainer) resultsMap := map[string]graph.Container{} for _, res := range results { res := res.GetInterface() @@ -197,7 +197,7 @@ func (suite *VertexTestSuite) TestVertexPod() { results, err := suite.g.V().HasLabel(vertex.PodLabel).ElementMap().ToList() suite.NoError(err) - suite.Equal(len(expectedPods)+numberOfKindDefaultPod, len(results)) + suite.Equal(len(expectedPods), len(results)-numberOfKindDefaultPod) resultsMap := map[string]graph.Pod{} for _, res := range results { res := res.GetInterface() From 2cd2a7686dd52fac940eb4a31565a9be1d4b2ce0 Mon Sep 17 00:00:00 2001 From: Edouard Schweisguth Date: Mon, 19 Jun 2023 10:22:26 +0200 Subject: [PATCH 22/25] bad main merge, missing go.mod entries --- go.mod | 3 ++- go.sum | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 5918be742..bd1236735 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.20 require ( github.com/DataDog/datadog-go/v5 v5.1.1 github.com/alitto/pond v1.8.3 + github.com/apache/tinkerpop/gremlin-go v0.0.0-20220530191148-29272fa563ec github.com/apache/tinkerpop/gremlin-go/v3 v3.6.4 github.com/hashicorp/go-multierror v1.1.1 github.com/spf13/cobra v1.6.1 @@ -13,6 +14,7 @@ require ( go.uber.org/ratelimit v0.2.0 golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 gopkg.in/DataDog/dd-trace-go.v1 v1.51.0 + gopkg.in/yaml.v3 v3.0.1 k8s.io/api v0.27.2 k8s.io/apimachinery v0.27.2 sigs.k8s.io/controller-runtime v0.15.0 @@ -103,7 +105,6 @@ require ( gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect inet.af/netaddr v0.0.0-20220811202034-502d2d690317 // indirect k8s.io/apiextensions-apiserver v0.27.2 // indirect k8s.io/component-base v0.27.2 // indirect diff --git a/go.sum b/go.sum index 3c73e0047..140a8c167 100644 --- a/go.sum +++ b/go.sum @@ -63,6 +63,8 @@ github.com/alitto/pond v1.8.3 h1:ydIqygCLVPqIX/USe5EaV/aSRXTRXDEI9JwuDdu+/xs= github.com/alitto/pond v1.8.3/go.mod h1:CmvIIGd5jKLasGI3D87qDkQxjzChdKMmnXMg3fG6M6Q= github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 h1:MzBOUgng9orim59UnfUTLRjMpd09C5uEVQ6RPGeCaVI= github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129/go.mod h1:rFgpPQZYZ8vdbc+48xibu8ALc3yeyd64IhHS+PU6Yyg= +github.com/apache/tinkerpop/gremlin-go v0.0.0-20220530191148-29272fa563ec h1:9cMmVpOQmePSOC13vDamxE79yyRFbmBv7eLaoBayGZs= +github.com/apache/tinkerpop/gremlin-go v0.0.0-20220530191148-29272fa563ec/go.mod h1:+83SLBh+WdhcTo/eUHRuU0PZKH60Mg3wR5yX2UKyhyo= github.com/apache/tinkerpop/gremlin-go/v3 v3.6.4 h1:76fnwJwUtsqLptsUuLiowpYkxXebWzD8afxXar5hT4A= github.com/apache/tinkerpop/gremlin-go/v3 v3.6.4/go.mod h1:KZ4BFULeKTVqzgX41fiTj2XYTs9meWc/TWQHsICy69I= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= From 0ce160d8a2684dd0fdd5e7c18b14d2e21045b457 Mon Sep 17 00:00:00 2001 From: Edouard Schweisguth Date: Mon, 19 Jun 2023 10:35:49 +0200 Subject: [PATCH 23/25] fix import --- test/system/graph_vertex_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/system/graph_vertex_test.go b/test/system/graph_vertex_test.go index 6aa8774ab..364c70070 100644 --- a/test/system/graph_vertex_test.go +++ b/test/system/graph_vertex_test.go @@ -9,7 +9,7 @@ import ( "github.com/DataDog/KubeHound/pkg/kubehound/graph/vertex" "github.com/DataDog/KubeHound/pkg/kubehound/models/graph" "github.com/DataDog/KubeHound/pkg/kubehound/storage/graphdb" - gremlingo "github.com/apache/tinkerpop/gremlin-go/driver" + gremlingo "github.com/apache/tinkerpop/gremlin-go/v3/driver" "github.com/stretchr/testify/suite" "golang.org/x/exp/slices" ) From 622f2f71e65c4604a33958d7bf822032093e4d5e Mon Sep 17 00:00:00 2001 From: Edouard Schweisguth Date: Mon, 19 Jun 2023 11:55:40 +0200 Subject: [PATCH 24/25] Updates namespaces, add sleep for setup --- Makefile | 3 ++- test/system/graph_vertex_test.go | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 23c88ba79..ae5ccb039 100644 --- a/Makefile +++ b/Makefile @@ -58,7 +58,8 @@ system-test: ## Run the system tests $(MAKE) infra-rm $(MAKE) infra-up # we print the KUBECONFIG envvar here to make it easier to see what is actively used - cd test/system && export KUBECONFIG=$(ROOT_DIR)/test/setup/.kube/config && bash -c "printenv KUBECONFIG" && go test -v -timeout "60s" -count=1 -race ./... + sleep 10 + cd test/system && export KUBECONFIG=$(ROOT_DIR)/test/setup/.kube/config && bash -c "printenv KUBECONFIG" && go test -v -timeout "60s" -count=1 ./... .PHONY: local-cluster-reset local-cluster-reset: ## Destroy the current kind cluster and creates a new one diff --git a/test/system/graph_vertex_test.go b/test/system/graph_vertex_test.go index 364c70070..bf3f25260 100644 --- a/test/system/graph_vertex_test.go +++ b/test/system/graph_vertex_test.go @@ -269,7 +269,7 @@ func (suite *VertexTestSuite) TestVertexToken() { suite.NoError(err) suite.Equal(6, len(results)) - results, err = suite.g.V().HasLabel(vertex.TokenLabel).Has("identity", "pod-patch-sa").Has("namespace", "default").Has("critical", false).ElementMap().ToList() + results, err = suite.g.V().HasLabel(vertex.TokenLabel).Has("type", "ServiceAccount").Has("identity", "pod-patch-sa").Has("critical", false).ElementMap().ToList() suite.NoError(err) suite.Equal(1, len(results)) } From 43cf014e0cb6e6b2eb2f9ed3fe8c484de68596ee Mon Sep 17 00:00:00 2001 From: Edouard Schweisguth Date: Mon, 19 Jun 2023 13:13:27 +0200 Subject: [PATCH 25/25] bump connection timeout --- test/system/kubehound.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/system/kubehound.yaml b/test/system/kubehound.yaml index 25e96c324..426324a31 100644 --- a/test/system/kubehound.yaml +++ b/test/system/kubehound.yaml @@ -5,7 +5,7 @@ collector: type: live-k8s-api-collector janusgraph: url: "ws://localhost:8182/gremlin" - connection_timeout: 5s + connection_timeout: 30s mongodb: url: "mongodb://localhost:27017" - connection_timeout: 5s \ No newline at end of file + connection_timeout: 30s \ No newline at end of file