first commit
This commit is contained in:
commit
4d56d8ea21
134
DESIGN.md
Normal file
134
DESIGN.md
Normal file
@ -0,0 +1,134 @@
|
||||
|
||||
Network policy and (later) linkerd policy generator.
|
||||
|
||||
Basic idea:
|
||||
1. The kubernetes clusters hosts applications
|
||||
2. Applications communicate to other applications
|
||||
|
||||
Allowed communication betwen applications is configured as follows:
|
||||
|
||||
communication:
|
||||
- from: app1
|
||||
to: app2
|
||||
ports:
|
||||
- 80
|
||||
- linkerd-admin
|
||||
|
||||
Ports are optional. When omitted all ports are intended
|
||||
|
||||
There are pre-defined applications such as api-server.
|
||||
Beyond that thera are two types of applications:
|
||||
- cidr: a cidr with possible except clauses following netpol syntax
|
||||
- app: regular app based on matchLabels, together with namespace name.
|
||||
|
||||
|
||||
Application names must be unique.
|
||||
|
||||
There are also standard capablities for an application such as:
|
||||
* linkerd: addes egress to linkerd-jaeger, egress to linkerd, ingress from
|
||||
linkerd-viz
|
||||
|
||||
capablities can also be defined at the namespace level, which means they
|
||||
apply to each pod in the namespace.
|
||||
|
||||
|
||||
|
||||
networks:
|
||||
- name: internet
|
||||
cidr: 0.0.0.0/0
|
||||
except:
|
||||
- 10.0.0.0/8
|
||||
- 172.16.0.0/12
|
||||
- 192.168.0.0/16
|
||||
|
||||
|
||||
namespaces:
|
||||
- namespace: wamblee-org
|
||||
capabilities:
|
||||
- linkerd
|
||||
applications:
|
||||
- name: nexus-server
|
||||
# ports when specified at the application level are used when
|
||||
# not explicitly mentioned when a link is made
|
||||
ports:
|
||||
- 8081
|
||||
- 8082
|
||||
matchLabels:
|
||||
app: nexus-server
|
||||
|
||||
- namespace: 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:8081,8082
|
||||
|
||||
|
||||
Handling of capabilities:
|
||||
1. capabilities at namespace level apply to each individual pod in the
|
||||
namespace
|
||||
2. a capability is a list of templates.
|
||||
|
||||
Ingress template
|
||||
|
||||
from:
|
||||
- linkerd-viz
|
||||
to:
|
||||
- {{ application }}
|
||||
|
||||
egress template
|
||||
|
||||
from:
|
||||
- {{ application }}
|
||||
to:
|
||||
- linkerd-jaeger
|
||||
- linkerd
|
||||
|
||||
The templates are evaluated for an application and then parsed, and added
|
||||
to the allowed communications.
|
||||
|
||||
|
||||
Linkerd extension:
|
||||
* for each application an optional service account is defined, when not
|
||||
defined, 'default' is assumed.
|
||||
* together with the communication links this determines the authorization
|
||||
policies.
|
||||
|
||||
Technical realization could be in the form of go templates where the input
|
||||
the template is:
|
||||
1. map of app name to definition with for each app also a list of
|
||||
capabilities.
|
||||
2. a single app config with for each app
|
||||
* all ingresses
|
||||
* all egresses
|
||||
|
||||
Similar and easier technical solution via interface with a network policy
|
||||
implementation and a linkerd implementation.
|
||||
|
||||
Then we can have:
|
||||
1. networkpolicy templates
|
||||
2. server, meshtls, networkauthentication, authorization policy
|
||||
* when linkerd capability for both pods, the tool can generated a
|
||||
meshtlsauthentication, when the target pods does not have linkerd
|
||||
capability, it can be ignored.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
29
Makefile
Normal file
29
Makefile
Normal file
@ -0,0 +1,29 @@
|
||||
.DEFAULT_GOAL := all
|
||||
|
||||
# seems superfluous
|
||||
#.PHONY: fmt vet build clean
|
||||
|
||||
tools:
|
||||
GOPROXY=direct go install git.wamblee.org/public/gotools/cmd/go2junit@main
|
||||
|
||||
fmt:
|
||||
go fmt ./...
|
||||
|
||||
vet: fmt
|
||||
go vet ./...
|
||||
|
||||
|
||||
build: vet
|
||||
mkdir -p bin
|
||||
go build -o bin ./cmd/...
|
||||
|
||||
install:
|
||||
go install ./...
|
||||
|
||||
test: build
|
||||
go test -count=1 -coverprofile=testout/coverage.out -json ${TESTFLAGS} ./... | go2junit testout
|
||||
|
||||
clean:
|
||||
rm -rf bin
|
||||
|
||||
all: build
|
45
cmd/policygen/config.go
Normal file
45
cmd/policygen/config.go
Normal file
@ -0,0 +1,45 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/goccy/go-yaml"
|
||||
"net"
|
||||
)
|
||||
|
||||
func validateCIDR(cidr string) error {
|
||||
_, _, err := net.ParseCIDR(cidr)
|
||||
return err
|
||||
}
|
||||
|
||||
type CIDR string
|
||||
|
||||
// UnmarshalYAML implements the yaml.Unmarshaler interface
|
||||
func (c *CIDR) UnmarshalYAML(value []byte) error {
|
||||
// Get the string value from the node
|
||||
var s string
|
||||
if err := yaml.Unmarshal(value, &s); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validateCIDR(s); err != nil {
|
||||
return err
|
||||
}
|
||||
*c = CIDR(s)
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalYAML implements the yaml.Marshaler interface
|
||||
func (c CIDR) MarshalYAML() ([]byte, error) {
|
||||
// Do any custom processing here before marshalling
|
||||
return []byte(string(c)), nil
|
||||
}
|
||||
|
||||
// CIDRS represents each network entry in the YAML
|
||||
type CIDRS struct {
|
||||
Name string `yaml:"name"`
|
||||
CIDR CIDR `yaml:"cidr"`
|
||||
Except []CIDR `yaml:"except,omitempty"`
|
||||
}
|
||||
|
||||
// Config represents the top-level YAML structure
|
||||
type Config struct {
|
||||
Networks []CIDRS `yaml:"networks"`
|
||||
}
|
50
cmd/policygen/main.go
Normal file
50
cmd/policygen/main.go
Normal file
@ -0,0 +1,50 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/goccy/go-yaml"
|
||||
"github.com/spf13/cobra"
|
||||
"os"
|
||||
)
|
||||
|
||||
type Options struct {
|
||||
}
|
||||
|
||||
func execute(files []string, options *Options) error {
|
||||
if len(files) != 1 {
|
||||
return fmt.Errorf("File expected")
|
||||
}
|
||||
yamlFile, err := os.ReadFile(files[0])
|
||||
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)
|
||||
}
|
||||
|
||||
fmt.Printf("PARSED %+v\n", config)
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
options := Options{}
|
||||
cmd := &cobra.Command{
|
||||
Use: "policygen",
|
||||
Short: "Generate network policies",
|
||||
Long: "Generated policies based on a more compact representation of topology",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return execute(args, &options)
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Execute()
|
||||
}
|
94
cmd/policygen/templates.go
Normal file
94
cmd/policygen/templates.go
Normal file
@ -0,0 +1,94 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io/fs"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
import "embed"
|
||||
import sprig "github.com/Masterminds/sprig/v3" // This provides most Helm functions
|
||||
import "github.com/goccy/go-yaml"
|
||||
|
||||
// This provides most Helm functions
|
||||
|
||||
//go:embed templates/*
|
||||
var templateFS embed.FS
|
||||
|
||||
func NewTemplate() *template.Template {
|
||||
tmpl := template.New("")
|
||||
|
||||
// Combine sprig functions with custom functions
|
||||
funcMap := template.FuncMap{}
|
||||
|
||||
// Add all sprig functions
|
||||
for name, fn := range sprig.FuncMap() {
|
||||
funcMap[name] = fn
|
||||
}
|
||||
|
||||
// Add any custom functions you want
|
||||
customFuncs := template.FuncMap{
|
||||
"toYaml": func(v interface{}) string {
|
||||
data, err := yaml.Marshal(v)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return string(data)
|
||||
},
|
||||
}
|
||||
|
||||
// Merge custom functions
|
||||
for name, fn := range customFuncs {
|
||||
funcMap[name] = fn
|
||||
}
|
||||
|
||||
// Add the function map to the template
|
||||
tmpl = tmpl.Funcs(funcMap)
|
||||
|
||||
return tmpl
|
||||
}
|
||||
|
||||
func showContents(files fs.FS) {
|
||||
entries, err := fs.ReadDir(files, ".")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for _, entry := range entries {
|
||||
fmt.Printf("entry %s %s\n", entry.Name(), entry.Type())
|
||||
if entry.Type().IsDir() {
|
||||
subdir, err := fs.Sub(files, entry.Name())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
showContents(subdir)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func loadTemplates() (*template.Template, error) {
|
||||
showContents(templateFS)
|
||||
|
||||
// Parse all templates at once from the embedded FS
|
||||
tmpl := NewTemplate()
|
||||
|
||||
err := loadTemplatesGlob(tmpl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tmpl, err
|
||||
}
|
||||
|
||||
func loadTemplatesGlob(tmpl *template.Template) error {
|
||||
return fs.WalkDir(templateFS, ".", func(path string, d os.DirEntry, err error) error {
|
||||
if strings.HasSuffix(path, ".yaml") {
|
||||
data, err := fs.ReadFile(templateFS, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "Loading template %s\n", path)
|
||||
tmpl.New(path).Parse(string(data))
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
15
cmd/policygen/templates/netpol/apiserver/cilium/egress.yaml
Normal file
15
cmd/policygen/templates/netpol/apiserver/cilium/egress.yaml
Normal file
@ -0,0 +1,15 @@
|
||||
kind: CiliumNetworkPolicy
|
||||
apiVersion: cilium.io/v2
|
||||
metadata:
|
||||
name: {{.name}}
|
||||
namespace: {{.namespace}}
|
||||
spec:
|
||||
endpointSelector:
|
||||
{{ .selector }}
|
||||
egress:
|
||||
- toEntities:
|
||||
- kube-apiserver
|
||||
- toPorts:
|
||||
- ports:
|
||||
- port: "6443"
|
||||
protocol: TCP
|
13
cmd/policygen/templates/netpol/apiserver/cilium/ingress.yaml
Normal file
13
cmd/policygen/templates/netpol/apiserver/cilium/ingress.yaml
Normal file
@ -0,0 +1,13 @@
|
||||
kind: CiliumNetworkPolicy
|
||||
apiVersion: cilium.io/v2
|
||||
metadata:
|
||||
name: {{.name}}
|
||||
namespace: {{.namespace}}
|
||||
spec:
|
||||
endpointSelector:
|
||||
{{ .selector }}
|
||||
ingress:
|
||||
- fromEntities:
|
||||
- kube-apiserver
|
||||
# See https://github.com/cilium/cilium/issues/35401
|
||||
- remote-node
|
14
cmd/policygen/templates/netpol/egress.yaml
Normal file
14
cmd/policygen/templates/netpol/egress.yaml
Normal file
@ -0,0 +1,14 @@
|
||||
kind: NetworkPolicy
|
||||
apiVersion: networking.k8s.io/v1
|
||||
metadata:
|
||||
name: "{{.name}}"
|
||||
namespace: "{{.namespace}}"
|
||||
spec:
|
||||
policyTypes:
|
||||
- Egress
|
||||
podSelector:
|
||||
{{.selector}}
|
||||
egress:
|
||||
{{- range $from := .from }}
|
||||
- {{ $from | nindent 4 }}
|
||||
{{- end }}
|
14
cmd/policygen/templates/netpol/ingress.yaml
Normal file
14
cmd/policygen/templates/netpol/ingress.yaml
Normal file
@ -0,0 +1,14 @@
|
||||
kind: NetworkPolicy
|
||||
apiVersion: networking.k8s.io/v1
|
||||
metadata:
|
||||
name: "{{.name}}"
|
||||
namespace: "{{.namespace}}"
|
||||
spec:
|
||||
policyTypes:
|
||||
- Ingress
|
||||
podSelector:
|
||||
{{.selector}}
|
||||
ingress:
|
||||
{{- range $from := .from }}
|
||||
- {{ $from | nindent 4 }}
|
||||
{{- end }}
|
12
example/config.yaml
Normal file
12
example/config.yaml
Normal file
@ -0,0 +1,12 @@
|
||||
|
||||
|
||||
|
||||
networks:
|
||||
- name: internet
|
||||
cidr: 0.0.0.0/0
|
||||
except:
|
||||
- 10.0.0.0/8
|
||||
- 172.16.0.0/12
|
||||
- 192.168.0.0/16
|
||||
|
||||
|
21
go.mod
Normal file
21
go.mod
Normal file
@ -0,0 +1,21 @@
|
||||
module git.wamblee.org/public/policy-generator
|
||||
|
||||
go 1.23.4
|
||||
|
||||
require (
|
||||
dario.cat/mergo v1.0.1 // indirect
|
||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.3.0 // indirect
|
||||
github.com/Masterminds/sprig/v3 v3.3.0 // indirect
|
||||
github.com/goccy/go-yaml v1.15.13 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/huandu/xstrings v1.5.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||
github.com/shopspring/decimal v1.4.0 // indirect
|
||||
github.com/spf13/cast v1.7.0 // indirect
|
||||
github.com/spf13/cobra v1.8.1 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
golang.org/x/crypto v0.26.0 // indirect
|
||||
)
|
34
go.sum
Normal file
34
go.sum
Normal file
@ -0,0 +1,34 @@
|
||||
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
|
||||
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
||||
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
|
||||
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
||||
github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0=
|
||||
github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
||||
github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs=
|
||||
github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/goccy/go-yaml v1.15.13 h1:Xd87Yddmr2rC1SLLTm2MNDcTjeO/GYo0JGiww6gSTDg=
|
||||
github.com/goccy/go-yaml v1.15.13/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
|
||||
github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
|
||||
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
|
||||
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
|
||||
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
|
||||
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
|
||||
github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
|
||||
github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
|
||||
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
|
||||
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
Loading…
Reference in New Issue
Block a user