open-> closed check not takingin to account annotation and filtering out

cases where the From is already a CIDR.
This commit is contained in:
Erik Brakkee 2025-01-17 20:43:55 +01:00
parent b7a0b6a557
commit 2066aad656
4 changed files with 123 additions and 38 deletions

View File

@ -17,7 +17,7 @@ build: vet
mkdir -p bin mkdir -p bin
go build -o bin ./cmd/... go build -o bin ./cmd/...
install: install: build
go install ./... go install ./...
test: build test: build

103
cmd/policygen/cluster.go Normal file
View File

@ -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
}

View File

@ -70,6 +70,13 @@ type Application struct {
Namespace *Namespace `yaml:"-" validate:"-"` Namespace *Namespace `yaml:"-" validate:"-"`
} }
func (a Application) Selector() *metav1.LabelSelector {
return &metav1.LabelSelector{
MatchLabels: a.MatchLabels,
MatchExpressions: a.MatchExpressions,
}
}
type Namespace struct { type Namespace struct {
Name string `yaml:"name"` Name string `yaml:"name"`
Open bool `yaml:"open"` Open bool `yaml:"open"`

View File

@ -6,7 +6,6 @@ import (
"iter" "iter"
"k8s.io/api/core/v1" "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"log" "log"
"maps" "maps"
"os" "os"
@ -44,14 +43,14 @@ func validate(files []string, options *Options) error {
if err != nil { if err != nil {
return err return err
} }
cluster, err := NewCluster(clientset)
if err != nil {
return err
}
config.Infer(func(application *Application) []string { config.Infer(func(application *Application) []string {
pods := FindPods(application, clientset) res := cluster.ServiceAccounts(application)
var res []string
for _, pod := range pods {
if !slices.Contains(res, pod.Spec.ServiceAccountName) {
res = append(res, pod.Spec.ServiceAccountName)
}
}
log.Printf("Inferred service accounts: %s/%s: %v", application.Namespace.Name, application.Name, log.Printf("Inferred service accounts: %s/%s: %v", application.Namespace.Name, application.Name,
res) res)
return res return res
@ -75,21 +74,15 @@ func validate(files []string, options *Options) error {
serviceAccountMap := make(map[string][]string) serviceAccountMap := make(map[string][]string)
for _, application := range ns.Applications { for _, application := range ns.Applications {
pods := FindPods(application, clientset) pods := cluster.Pods(application)
applicationPods[application.Name] = pods applicationPods[application.Name] = pods
//log.Printf(namespace + "/" + application.Name) //log.Printf(namespace + "/" + application.Name)
if len(pods) == 0 { if len(pods) == 0 {
LogValidationMsg(Error, "application %s: no running pods found", application.Name) LogValidationMsg(Error, "application %s: no running pods found", application.Name)
} }
ownerReferences := make(map[string]bool) ownerReferences := cluster.OwnerReferences(application)
for _, pod := range pods {
//log.Printf(" %s %v", pod.Name, pod.OwnerReferences)
for _, ownerReference := range pod.OwnerReferences {
ownerReferences[ownerReference.Kind+"/"+ownerReference.Name] = true
}
}
if len(ownerReferences) > 1 { 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 // check ports
for _, port := range application.Ports { for _, port := range application.Ports {
@ -153,7 +146,7 @@ func validate(files []string, options *Options) error {
continue continue
} }
for _, port := range communication.Ports { for _, port := range communication.Ports {
pods := FindPods(application, clientset) pods := cluster.Pods(application)
for _, pod := range pods { for _, pod := range pods {
if !HasPort(pod, port) { if !HasPort(pod, port) {
LogValidationMsg(Error, "communication %v -> %v: port %v is not configured in pod %s/%s", 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 { if applicationFrom != nil && !applicationFrom.Namespace.Open {
continue continue
} }
if networkFrom == nil { if networkFrom == nil && cluster.IsLinkerdEnabled(application) {
openToClosedAccess[applicationNameFrom] = applicationName openToClosedAccess[applicationNameFrom] = applicationName
} }
} }
@ -204,24 +197,6 @@ func validate(files []string, options *Options) error {
return nil 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 { func HasPort(pod v1.Pod, port Port) bool {
if port.Protocol == "" { if port.Protocol == "" {
port.Protocol = "TCP" port.Protocol = "TCP"