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"
|
||||||
@ -60,11 +60,12 @@ type MatchExpression struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Application struct {
|
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"`
|
||||||
Namespace *Namespace `yaml:"-" validate:"-"`
|
MatchExpressions []metav1.LabelSelectorRequirement `yaml:"matchExpressions" validate:"omitempty,dive"`
|
||||||
|
Namespace *Namespace `yaml:"-" validate:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Namespace struct {
|
type Namespace struct {
|
||||||
@ -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:
|
||||||
@ -27,11 +27,11 @@ namespaces:
|
|||||||
- port: 8081
|
- port: 8081
|
||||||
- port: 8082
|
- port: 8082
|
||||||
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