container ports are not checked by the tool with the validation option.
This commit is contained in:
		
							parent
							
								
									ff816a02ae
								
							
						
					
					
						commit
						ea6eb4e9ae
					
				| @ -5,7 +5,7 @@ import ( | |||||||
| 	"errors" | 	"errors" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"github.com/goccy/go-yaml" | 	"github.com/goccy/go-yaml" | ||||||
| 	"log" | 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||||
| 	"net" | 	"net" | ||||||
| 	"os" | 	"os" | ||||||
| 	"slices" | 	"slices" | ||||||
| @ -63,7 +63,8 @@ type Application struct { | |||||||
| 	Name        string            `yaml:"name"` | 	Name        string            `yaml:"name"` | ||||||
| 	Ports       []Port            `yaml:"ports,omitempty"` | 	Ports       []Port            `yaml:"ports,omitempty"` | ||||||
| 	MatchLabels map[string]string `yaml:"matchLabels"` | 	MatchLabels map[string]string `yaml:"matchLabels"` | ||||||
| 	MatchExpressions []MatchExpression `yaml:"matchExpressions" validate:"omitempty,dive"` | 	//MatchExpressions []MatchExpression `yaml:"matchExpressions" validate:"omitempty,dive"`
 | ||||||
|  | 	MatchExpressions []metav1.LabelSelectorRequirement `yaml:"matchExpressions" validate:"omitempty,dive"` | ||||||
| 	Namespace        *Namespace                        `yaml:"-" validate:"-"` | 	Namespace        *Namespace                        `yaml:"-" validate:"-"` | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -108,8 +109,7 @@ func (c Config) Validate() error { | |||||||
| 
 | 
 | ||||||
| 	// network names mus tbe unique
 | 	// network names mus tbe unique
 | ||||||
| 	networks := make(map[string]bool) | 	networks := make(map[string]bool) | ||||||
| 	for nn, network := range c.Networks { | 	for _, network := range c.Networks { | ||||||
| 		log.Printf("Network %+v %v %v\n", network, nn, c.Networks) |  | ||||||
| 		if networks[network.Name] { | 		if networks[network.Name] { | ||||||
| 			errs = append(errs, fmt.Errorf("Duplicate network name %s", network.Name)) | 			errs = append(errs, fmt.Errorf("Duplicate network name %s", network.Name)) | ||||||
| 		} | 		} | ||||||
|  | |||||||
							
								
								
									
										140
									
								
								cmd/policygen/configvalidator.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										140
									
								
								cmd/policygen/configvalidator.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,140 @@ | |||||||
|  | package main | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"fmt" | ||||||
|  | 	"iter" | ||||||
|  | 	"k8s.io/api/core/v1" | ||||||
|  | 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||||
|  | 	"k8s.io/client-go/kubernetes" | ||||||
|  | 	"log" | ||||||
|  | 	"maps" | ||||||
|  | 	"os" | ||||||
|  | 	"strconv" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type ValidationLevel int | ||||||
|  | 
 | ||||||
|  | const ( | ||||||
|  | 	Info ValidationLevel = iota | ||||||
|  | 	Warning | ||||||
|  | 	Error | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func LogValidationMsg(level ValidationLevel, msg string, v ...any) { | ||||||
|  | 	fmt.Fprintf(os.Stderr, msg+"\n", v...) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func IterToSlice[K any](i iter.Seq[K]) []K { | ||||||
|  | 	res := make([]K, 0) | ||||||
|  | 	for v := range i { | ||||||
|  | 		res = append(res, v) | ||||||
|  | 	} | ||||||
|  | 	return res | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func MapKeys[K comparable, V any](m map[K]V) []K { | ||||||
|  | 	return IterToSlice(maps.Keys(m)) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func validate(files []string, options *Options) error { | ||||||
|  | 	config, err := readConfig(files) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	clientset, _ := GetKubernetesConnection() | ||||||
|  | 	for _, ns := range config.Namespaces { | ||||||
|  | 		namespace := ns.Name | ||||||
|  | 		_, err = clientset.CoreV1().Namespaces().Get(context.Background(), namespace, metav1.GetOptions{}) | ||||||
|  | 		if err != nil { | ||||||
|  | 			LogValidationMsg(Error, "ERROR: namespace not found: %s", namespace) | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		for _, application := range ns.Applications { | ||||||
|  | 			pods := FindPods(application, clientset) | ||||||
|  | 			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 | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			if len(ownerReferences) > 1 { | ||||||
|  | 				LogValidationMsg(Error, "Application %s: multiple owners found:  %v", application.Name, MapKeys(ownerReferences)) | ||||||
|  | 			} | ||||||
|  | 			// check ports
 | ||||||
|  | 			for _, port := range application.Ports { | ||||||
|  | 				for _, pod := range pods { | ||||||
|  | 					if !HasPort(pod, port) { | ||||||
|  | 						LogValidationMsg(Error, "application %s: port %v not found in pod %s/%s", | ||||||
|  | 							application.Name, port, pod.Namespace, pod.Name) | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		for _, communication := range config.Communications { | ||||||
|  | 			if len(communication.Ports) == 0 { | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 			for _, applicationName := range communication.To { | ||||||
|  | 				application, _, _ := config.GetApplication(applicationName) | ||||||
|  | 				if application == nil { | ||||||
|  | 					continue | ||||||
|  | 				} | ||||||
|  | 				for _, port := range communication.Ports { | ||||||
|  | 					pods := FindPods(application, clientset) | ||||||
|  | 					for _, pod := range pods { | ||||||
|  | 						if !HasPort(pod, port) { | ||||||
|  | 							LogValidationMsg(Error, "communication %v -> %v: port %v is not configured in pod %s/%s", | ||||||
|  | 								communication.From, communication.To, port, pod.Namespace, pod.Name) | ||||||
|  | 						} | ||||||
|  | 					} | ||||||
|  | 
 | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 	} | ||||||
|  | 	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" | ||||||
|  | 	} | ||||||
|  | 	for _, container := range pod.Spec.Containers { | ||||||
|  | 		for _, kport := range container.Ports { | ||||||
|  | 			if kport.Protocol == "" { | ||||||
|  | 				kport.Protocol = "TCP" | ||||||
|  | 			} | ||||||
|  | 			if port.Protocol == string(kport.Protocol) && (port.Port == kport.Name || port.Port == strconv.Itoa(int(kport.ContainerPort))) { | ||||||
|  | 				return true | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return false | ||||||
|  | } | ||||||
| @ -55,10 +55,6 @@ func generate(files []string, options *Options) error { | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func validate(files []string, options *Options) error { |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func main() { | func main() { | ||||||
| 
 | 
 | ||||||
| 	options := Options{ | 	options := Options{ | ||||||
|  | |||||||
| @ -10,7 +10,7 @@ networks: | |||||||
|       - 192.168.0.0/16 |       - 192.168.0.0/16 | ||||||
|     ports: |     ports: | ||||||
|       - port: 2303 |       - port: 2303 | ||||||
|         protocol: UDPx |         protocol: UDP | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| namespaces: | namespaces: | ||||||
| @ -29,9 +29,9 @@ namespaces: | |||||||
|             protocol: UDP |             protocol: UDP | ||||||
|         matchLabels:  |         matchLabels:  | ||||||
|           app: nexus-server |           app: nexus-server | ||||||
|         matchExpressions: |         #matchExpressions: | ||||||
|           - key: jenkins/label |         #  - key: jenkins/label | ||||||
|             operator: Exists |         #    operator: Exists | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|   - name: exposure |   - name: exposure | ||||||
| @ -39,9 +39,9 @@ namespaces: | |||||||
|     applications: |     applications: | ||||||
|       - name: httpd-wamblee-org |       - name: httpd-wamblee-org | ||||||
|         matchLabels: |         matchLabels: | ||||||
|           app: wamblee-org |           app: httpd-wamblee-org | ||||||
|         ports: |         ports: | ||||||
|          - port: 1000 |          - port: 80 | ||||||
|          - port: 1001 |          - port: 1001 | ||||||
|            protocol: UDP |            protocol: UDP | ||||||
| 
 | 
 | ||||||
| @ -54,6 +54,7 @@ communications: | |||||||
|       - nexus-server |       - nexus-server | ||||||
|       - internet |       - internet | ||||||
|     ports: |     ports: | ||||||
|  |       - port: 8084 | ||||||
|       - port: 53 |       - port: 53 | ||||||
|         protocol: UDP |         protocol: UDP | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user