diff --git a/DESIGN.md b/DESIGN.md index e6eb07a..c1c64c0 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -74,7 +74,10 @@ communications: - from: - httpd-wamblee-org to: - - nexus-server:8081,8082 + - nexus-server + porst: + - 8081 + - 8082 Handling of capabilities: diff --git a/cmd/policygen/config.go b/cmd/policygen/config.go index b454eca..af9c18f 100644 --- a/cmd/policygen/config.go +++ b/cmd/policygen/config.go @@ -1,6 +1,8 @@ package main import ( + "errors" + "fmt" "github.com/goccy/go-yaml" "net" ) @@ -32,8 +34,8 @@ func (c CIDR) MarshalYAML() ([]byte, error) { return []byte(string(c)), nil } -// CIDRS represents each network entry in the YAML -type CIDRS struct { +// Network represents each network entry in the YAML +type Network struct { Name string `yaml:"name"` CIDR CIDR `yaml:"cidr"` Except []CIDR `yaml:"except,omitempty"` @@ -46,13 +48,70 @@ type Application struct { } type Namespace struct { - Namespace string `yaml:"namespace"` + Name string `yaml:"name"` Capabilities []string `yaml:"capabilities"` Applications []Application `yaml:"applications"` } +type Communication struct { + From []string `yaml:"from"` + To []string `yaml:"to"` + Ports []string `yaml:"ports"` +} + // Config represents the top-level YAML structure type Config struct { - Networks []CIDRS `yaml:"networks"` - Namespaces []Namespace `yaml:"namespaces"` + Networks []Network `yaml:"networks,omitempty"` + Namespaces []Namespace `yaml:"namespaces,omitempty"` + Communications []Communication `yaml:"communications,omitempty"` +} + +func (c Config) Validate() error { + + errs := make([]error, 0) + + // namesapaces must be unique + ns := make(map[string]bool) + for _, namespace := range c.Namespaces { + if ns[namespace.Name] { + errs = append(errs, fmt.Errorf("Duplicate namespace name %s", namespace.Name)) + } + ns[namespace.Name] = true + } + + // network names mus tbe unique + networks := make(map[string]bool) + for _, network := range c.Networks { + if networks[network.Name] { + errs = append(errs, fmt.Errorf("Duplicate network name %s", network.Name)) + } + networks[network.Name] = true + } + + // application names must be unique + apps := make(map[string]bool) + for _, namespace := range c.Namespaces { + for _, app := range namespace.Applications { + if apps[app.Name] { + errs = append(errs, fmt.Errorf("Duplicate application %s (%s)", app.Name, namespace.Name)) + } + apps[app.Name] = true + } + } + + // applications mentioned in a communication must exist + for _, communication := range c.Communications { + for _, from := range communication.From { + if !apps[from] { + errs = append(errs, fmt.Errorf("Application does not exist: %s referenced in a communication (%+v)", from, communication)) + } + } + for _, to := range communication.To { + if !apps[to] { + errs = append(errs, fmt.Errorf("Application does not exist: %s referenced in a communication (%+v)", to, communication)) + } + } + } + + return errors.Join(errs...) } diff --git a/cmd/policygen/main.go b/cmd/policygen/main.go index 42b690d..c1e5d8a 100644 --- a/cmd/policygen/main.go +++ b/cmd/policygen/main.go @@ -9,34 +9,45 @@ import ( ) type Options struct { + cni string + policyType string } func execute(files []string, options *Options) error { - if len(files) != 1 { + if len(files) == 0 { return fmt.Errorf("File expected") } - yamlFile, err := os.ReadFile(files[0]) - if err != nil { - return fmt.Errorf("Error reading YAML file: %v", err) - } + for _, file := range files { + fmt.Fprintf(os.Stderr, "Reading config %s\n", file) + yamlFile, err := os.ReadFile(file) + if err != nil { + return fmt.Errorf("Error reading YAML file: %v", err) + } - // Parse the YAML content - dec := yaml.NewDecoder(bytes.NewReader(yamlFile), - yaml.UseJSONUnmarshaler(), - yaml.DisallowUnknownField(), - ) - var config Config - err = dec.Decode(&config) - if err != nil { - return fmt.Errorf("Error parsing YAML: %v", err) + // Parse the YAML content + dec := yaml.NewDecoder(bytes.NewReader(yamlFile), + yaml.UseJSONUnmarshaler(), + yaml.DisallowUnknownField(), + ) + var config Config + err = dec.Decode(&config) + if err != nil { + return fmt.Errorf("Error parsing YAML: %v", err) + } + err = config.Validate() + if err != nil { + return err + } + fmt.Printf("PARSED %+v\n", config) } - - fmt.Printf("PARSED %+v\n", config) return nil } func main() { - options := Options{} + options := Options{ + cni: "cilium", + policyType: "netpol", + } cmd := &cobra.Command{ Use: "policygen", Short: "Generate network policies", diff --git a/example/config.yaml b/example/config.yaml index cc51c18..246e0eb 100644 --- a/example/config.yaml +++ b/example/config.yaml @@ -11,7 +11,7 @@ networks: namespaces: - - namespace: wamblee-org + - name: wamblee-org capabilities: - linkerd applications: @@ -24,8 +24,27 @@ namespaces: matchLabels: app: nexus-server - - namespace: exposure + - name: exposure applications: - name: httpd-wamblee-org matchLabels: app: wamblee-org + +communications: + - from: # can we support both string and list of strings? + - httpd-wamblee-org + to: + - nexus-server + - wamblee-static + - wamblee-safe + + # or limiting ports further + - from: + - httpd-wamblee-org + to: + - nexus-server + ports: + - 8081 + - 8082 + +