From 2066aad656809a6522c208b9b89fe859a38eb82b Mon Sep 17 00:00:00 2001 From: Erik Brakkee Date: Fri, 17 Jan 2025 20:43:55 +0100 Subject: [PATCH] open-> closed check not takingin to account annotation and filtering out cases where the From is already a CIDR. --- Makefile | 2 +- cmd/policygen/cluster.go | 103 +++++++++++++++++++++++++++++++ cmd/policygen/config.go | 7 +++ cmd/policygen/configvalidator.go | 49 ++++----------- 4 files changed, 123 insertions(+), 38 deletions(-) create mode 100644 cmd/policygen/cluster.go diff --git a/Makefile b/Makefile index b84e313..b9e4f98 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,7 @@ build: vet mkdir -p bin go build -o bin ./cmd/... -install: +install: build go install ./... test: build diff --git a/cmd/policygen/cluster.go b/cmd/policygen/cluster.go new file mode 100644 index 0000000..2866076 --- /dev/null +++ b/cmd/policygen/cluster.go @@ -0,0 +1,103 @@ +package main + +import ( + "context" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/kubernetes" + "log" + "slices" +) + +type Cluster struct { + namespaces map[string]v1.Namespace + clientset *kubernetes.Clientset + // map of namespace to list of all pods + pods map[string][]v1.Pod +} + +func NewCluster(clientset *kubernetes.Clientset) (*Cluster, error) { + cluster := &Cluster{ + clientset: clientset, + namespaces: make(map[string]v1.Namespace), + pods: make(map[string][]v1.Pod), + } + nslist, err := cluster.clientset.CoreV1().Namespaces().List(context.Background(), metav1.ListOptions{}) + if err != nil { + return nil, err + } + for _, ns := range nslist.Items { + cluster.namespaces[ns.Name] = ns + podList, err := cluster.clientset.CoreV1().Pods(ns.Name).List(context.Background(), metav1.ListOptions{}) + if err != nil { + return nil, err + } + cluster.pods[ns.Name] = podList.Items + } + return cluster, nil +} + +func (c *Cluster) Pods(application *Application) []v1.Pod { + selector, err := metav1.LabelSelectorAsSelector(application.Selector()) + if err != nil { + log.Fatalf("Error creating selector: %v", err) + } + pods := c.pods[application.Namespace.Name] + pods = slices.DeleteFunc(slices.Clone(pods), func(pod v1.Pod) bool { + return !selector.Matches(labels.Set(pod.Labels)) + }) + return pods +} + +func (c *Cluster) ServiceAccounts(application *Application) []string { + var res []string + for _, pod := range c.Pods(application) { + if !slices.Contains(res, pod.Spec.ServiceAccountName) { + res = append(res, pod.Spec.ServiceAccountName) + } + } + return res +} + +func (c *Cluster) OwnerReferences(application *Application) []string { + var ownerReferences []string + for _, pod := range c.Pods(application) { + //log.Printf(" %s %v", pod.Name, pod.OwnerReferences) + for _, ownerReference := range pod.OwnerReferences { + owner := ownerReference.Kind + "/" + ownerReference.Name + if !slices.Contains(ownerReferences, owner) { + ownerReferences = append(ownerReferences, owner) + } + } + } + return ownerReferences +} + +func (c *Cluster) IsLinkerdEnabled(application *Application) bool { + pods := c.Pods(application) + + ndisabled := 0 + for _, pod := range pods { + if pod.Annotations["linkerd.io/inject"] == "enabled" { + return true + } + if pod.Annotations["linkerd.io/inject"] == "disabled" { + ndisabled++ + } + } + if ndisabled == len(pods) { + return false + } + ns := c.namespaces[application.Namespace.Name] + return ns.Annotations["linkerd.io/inject"] == "enabled" +} + +func (c *Cluster) Ports(application *Application, nameBased bool) []Port { + // gather unique ports based on name + // or based on number. + // Warning: same name different ports + // same port different names. + // Can occur if the selector matches multiple replicasets. + return nil +} diff --git a/cmd/policygen/config.go b/cmd/policygen/config.go index bf4a525..7da91a6 100644 --- a/cmd/policygen/config.go +++ b/cmd/policygen/config.go @@ -70,6 +70,13 @@ type Application struct { Namespace *Namespace `yaml:"-" validate:"-"` } +func (a Application) Selector() *metav1.LabelSelector { + return &metav1.LabelSelector{ + MatchLabels: a.MatchLabels, + MatchExpressions: a.MatchExpressions, + } +} + type Namespace struct { Name string `yaml:"name"` Open bool `yaml:"open"` diff --git a/cmd/policygen/configvalidator.go b/cmd/policygen/configvalidator.go index 2afe153..bdf43d4 100644 --- a/cmd/policygen/configvalidator.go +++ b/cmd/policygen/configvalidator.go @@ -6,7 +6,6 @@ import ( "iter" "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes" "log" "maps" "os" @@ -44,14 +43,14 @@ func validate(files []string, options *Options) error { if err != nil { return err } + + cluster, err := NewCluster(clientset) + if err != nil { + return err + } + config.Infer(func(application *Application) []string { - pods := FindPods(application, clientset) - var res []string - for _, pod := range pods { - if !slices.Contains(res, pod.Spec.ServiceAccountName) { - res = append(res, pod.Spec.ServiceAccountName) - } - } + res := cluster.ServiceAccounts(application) log.Printf("Inferred service accounts: %s/%s: %v", application.Namespace.Name, application.Name, res) return res @@ -75,21 +74,15 @@ func validate(files []string, options *Options) error { serviceAccountMap := make(map[string][]string) for _, application := range ns.Applications { - pods := FindPods(application, clientset) + pods := cluster.Pods(application) applicationPods[application.Name] = pods //log.Printf(namespace + "/" + application.Name) if len(pods) == 0 { LogValidationMsg(Error, "application %s: no running pods found", application.Name) } - ownerReferences := make(map[string]bool) - for _, pod := range pods { - //log.Printf(" %s %v", pod.Name, pod.OwnerReferences) - for _, ownerReference := range pod.OwnerReferences { - ownerReferences[ownerReference.Kind+"/"+ownerReference.Name] = true - } - } + ownerReferences := cluster.OwnerReferences(application) if len(ownerReferences) > 1 { - LogValidationMsg(Error, "Application %s: multiple owners found: %v. The application definition can possibly be made more fine-grain", application.Name, MapKeys(ownerReferences)) + LogValidationMsg(Error, "Application %s: multiple owners found: %v. The application definition can possibly be made more fine-grain", application.Name, ownerReferences) } // check ports for _, port := range application.Ports { @@ -153,7 +146,7 @@ func validate(files []string, options *Options) error { continue } for _, port := range communication.Ports { - pods := FindPods(application, clientset) + pods := cluster.Pods(application) for _, pod := range pods { if !HasPort(pod, port) { LogValidationMsg(Error, "communication %v -> %v: port %v is not configured in pod %s/%s", @@ -187,7 +180,7 @@ func validate(files []string, options *Options) error { if applicationFrom != nil && !applicationFrom.Namespace.Open { continue } - if networkFrom == nil { + if networkFrom == nil && cluster.IsLinkerdEnabled(application) { openToClosedAccess[applicationNameFrom] = applicationName } } @@ -204,24 +197,6 @@ func validate(files []string, options *Options) error { return nil } -func FindPods(application *Application, clientset *kubernetes.Clientset) []v1.Pod { - labelSelector := &metav1.LabelSelector{ - MatchLabels: application.MatchLabels, - MatchExpressions: application.MatchExpressions, - } - selector, err := metav1.LabelSelectorAsSelector(labelSelector) - if err != nil { - log.Fatalf("Error creating selector: %v", err) - } - pods, err := clientset.CoreV1().Pods(application.Namespace.Name).List(context.TODO(), metav1.ListOptions{ - LabelSelector: selector.String(), - }) - if err != nil { - log.Fatalf("Error listing pods: %v", err) - } - return pods.Items -} - func HasPort(pod v1.Pod, port Port) bool { if port.Protocol == "" { port.Protocol = "TCP"