generating policies first version.
Still includes linkerd ports.
This commit is contained in:
parent
496e58347c
commit
108f21ea58
@ -240,6 +240,11 @@ func LoadConfig(file string) (*Config, error) {
|
|||||||
for _, ns := range config.Namespaces {
|
for _, ns := range config.Namespaces {
|
||||||
for _, app := range ns.Applications {
|
for _, app := range ns.Applications {
|
||||||
app.Namespace = ns
|
app.Namespace = ns
|
||||||
|
for i, _ := range app.Ports {
|
||||||
|
if app.Ports[i].Protocol == "" {
|
||||||
|
app.Ports[i].Protocol = "TCP"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"iter"
|
"iter"
|
||||||
"k8s.io/api/core/v1"
|
"k8s.io/api/core/v1"
|
||||||
"maps"
|
|
||||||
"os"
|
"os"
|
||||||
"slices"
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -35,13 +34,6 @@ func IterToSlice[K any](i iter.Seq[K]) []K {
|
|||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
func MapKeys[K comparable, V any](m map[K]V) []K {
|
|
||||||
return IterToSlice(maps.Keys(m))
|
|
||||||
}
|
|
||||||
func MapValues[K comparable, V any](m map[K]V) []V {
|
|
||||||
return IterToSlice(maps.Values(m))
|
|
||||||
}
|
|
||||||
|
|
||||||
func validate(files []string, options *Options) error {
|
func validate(files []string, options *Options) error {
|
||||||
clientset, _ := GetKubernetesConnection()
|
clientset, _ := GetKubernetesConnection()
|
||||||
config, err := readConfig(files)
|
config, err := readConfig(files)
|
||||||
|
@ -6,9 +6,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Generator interface {
|
type Generator interface {
|
||||||
Init(write io.Writer) error
|
Init(writer io.Writer) error
|
||||||
GenerateNamespace(writer io.Writer, namespace *Namespace) error
|
GenerateNamespace(writer io.Writer, namespace *Namespace) error
|
||||||
GenerateCommunicationRule(writer io.Writer, app *Application, ingress *Ingress, egress *Egress) error
|
GenerateCommunicationRule(writer io.Writer, app *Application, ingress *Ingress, egress *Egress) error
|
||||||
|
Finalize(writer io.Writer) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type ApplicationPeer struct {
|
type ApplicationPeer struct {
|
||||||
@ -172,5 +173,5 @@ func Generate(writer io.Writer, generator Generator, config *Config) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return generator.Finalize(writer)
|
||||||
}
|
}
|
||||||
|
@ -4,71 +4,154 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"slices"
|
||||||
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
type LinkerdPolicyGenerator struct {
|
type LinkerdPolicyGenerator struct {
|
||||||
config *Config
|
config *Config
|
||||||
policyTemplates *PolicyTemplates
|
policyTemplates *PolicyTemplates
|
||||||
|
|
||||||
|
networks map[string]*Network
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g LinkerdPolicyGenerator) Init(writer io.Writer) error {
|
func NewLinkerdPolicyGenerator(config *Config, templates *PolicyTemplates) *LinkerdPolicyGenerator {
|
||||||
// start by generating network authentications
|
return &LinkerdPolicyGenerator{
|
||||||
for _, network := range g.config.Networks {
|
config: config,
|
||||||
fmt.Fprintf(os.Stderr, "NetworkAuthentication default/%s\n", network.Name)
|
policyTemplates: templates,
|
||||||
template := g.policyTemplates.PredefineApplicationPolicyTemplate("linkerd", "networkauthentication")
|
networks: make(map[string]*Network),
|
||||||
if template == nil {
|
|
||||||
return fmt.Errorf("Linkerd template for network authentication not found")
|
|
||||||
}
|
|
||||||
err := template.Execute(writer, network)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Error executing network authentication template for %s", network.Name)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *LinkerdPolicyGenerator) Init(writer io.Writer) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g LinkerdPolicyGenerator) GenerateNamespace(writer io.Writer, namespace *Namespace) error {
|
func (g *LinkerdPolicyGenerator) GenerateNamespace(writer io.Writer, namespace *Namespace) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g LinkerdPolicyGenerator) GenerateCommunicationRule(
|
func (g *LinkerdPolicyGenerator) GenerateCommunicationRule(
|
||||||
writer io.Writer,
|
writer io.Writer,
|
||||||
app *Application,
|
app *Application,
|
||||||
ingress *Ingress,
|
ingress *Ingress,
|
||||||
egress *Egress) error {
|
egress *Egress) error {
|
||||||
|
|
||||||
// and then the meshTLSAuthentications
|
if app.Namespace.Unauthorized {
|
||||||
fmt.Fprintf(os.Stderr, "MeshTLSAuthentication %s/%s %v\n",
|
fmt.Fprintf(os.Stderr, "UNAUTHORIZED %s\n", app.Name)
|
||||||
app.Namespace.Name, app.Name, app.ServiceAccounts)
|
return nil
|
||||||
template := g.policyTemplates.PredefineApplicationPolicyTemplate("linkerd", "meshtlsauthentication")
|
|
||||||
if template == nil {
|
|
||||||
return fmt.Errorf("Could not find meshtlsauthentication template")
|
|
||||||
}
|
|
||||||
err := template.Execute(writer, app)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// and the server resources
|
// and the server resources
|
||||||
fmt.Fprintf(os.Stderr, "Server %s/%s\n",
|
fmt.Fprintf(os.Stderr, "Server %s/%s\n",
|
||||||
app.Namespace.Name, app.Name)
|
app.Namespace.Name, app.Name)
|
||||||
template = g.policyTemplates.PredefineApplicationPolicyTemplate("linkerd", "server")
|
err := g.policyTemplates.Execute("linkerd", "server", writer, app)
|
||||||
if template == nil {
|
|
||||||
return fmt.Errorf("Could not find meshtlsauthentication template")
|
|
||||||
}
|
|
||||||
err = template.Execute(writer, app)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(ingress.Applications)+
|
if len(ingress.Applications)+
|
||||||
len(ingress.Networks)+
|
len(ingress.Networks) > 0 {
|
||||||
len(egress.Applications)+
|
ports := make(map[int]bool)
|
||||||
len(egress.Networks) > 0 {
|
//
|
||||||
// non-trivial regular network policy
|
clientApplications := make(map[int][]*ApplicationPeer)
|
||||||
|
for _, ingress := range ingress.Applications {
|
||||||
|
for _, port := range ingress.Ports {
|
||||||
|
if port.Protocol == "TCP" {
|
||||||
|
portno, err := strconv.Atoi(port.Port)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error convert port '%s' of ingress '%s' of app '%s': %w",
|
||||||
|
port.Port, ingress.Application.Name, app.Name, err)
|
||||||
|
}
|
||||||
|
ports[portno] = true
|
||||||
|
clientApplications[portno] = append(clientApplications[portno], ingress)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO
|
clientNetworks := make(map[int][]*NetworkPeer)
|
||||||
|
for _, ingress := range ingress.Networks {
|
||||||
|
for _, port := range ingress.Ports {
|
||||||
|
if port.Protocol == "TCP" {
|
||||||
|
portno, err := strconv.Atoi(port.Port)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error convert port '%s' of ingress '%s' of app '%s': %w",
|
||||||
|
port.Port, ingress.Network.Name, app.Name, err)
|
||||||
|
}
|
||||||
|
ports[portno] = true
|
||||||
|
clientNetworks[portno] = append(clientNetworks[portno], ingress)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for port, _ := range ports {
|
||||||
|
fmt.Fprintf(os.Stderr, "Generating authorization policy: %v %v -> %v : %v\n",
|
||||||
|
Map(clientApplications[port], func(peer *ApplicationPeer) string {
|
||||||
|
return peer.Application.Name
|
||||||
|
}),
|
||||||
|
Map(clientNetworks[port], func(peer *NetworkPeer) string {
|
||||||
|
return peer.Network.Name
|
||||||
|
}),
|
||||||
|
app.Name,
|
||||||
|
port)
|
||||||
|
// Optimization: keep track of the references clientApps and
|
||||||
|
// client Networks and only generate authentications for those instead of for
|
||||||
|
// all apps and networks.
|
||||||
|
for _, clientNetwork := range clientNetworks[port] {
|
||||||
|
g.networks[clientNetwork.Network.Name] = clientNetwork.Network
|
||||||
|
}
|
||||||
|
|
||||||
|
// linkerd rules
|
||||||
|
// 1. an authpolicy may contain only one meshtlsauthentication rule
|
||||||
|
// 2. an authpolicy may contain only one service account .
|
||||||
|
// 3. an authpolicy may contain more than one networkauthentication
|
||||||
|
//
|
||||||
|
// Should generate here a methtlsautheorization for every port
|
||||||
|
// and pass in a list of service accounts instead of a list of apps.
|
||||||
|
serviceAccounts := g.serviceAccounts(clientApplications[port])
|
||||||
|
if len(serviceAccounts) > 0 {
|
||||||
|
err = g.policyTemplates.Execute("linkerd", "meshtlsauthentication",
|
||||||
|
writer,
|
||||||
|
map[string]any{
|
||||||
|
"app": app,
|
||||||
|
"port": port,
|
||||||
|
"serviceAccounts": serviceAccounts,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = g.policyTemplates.Execute("linkerd", "authorizationpolicy",
|
||||||
|
writer,
|
||||||
|
map[string]any{
|
||||||
|
"app": app,
|
||||||
|
"port": port,
|
||||||
|
"clientNetworks": clientNetworks[port],
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *LinkerdPolicyGenerator) serviceAccounts(peers []*ApplicationPeer) []string {
|
||||||
|
serviceAccounts := []string{}
|
||||||
|
for _, peer := range peers {
|
||||||
|
serviceAccounts = append(serviceAccounts, peer.Application.ServiceAccounts...)
|
||||||
|
}
|
||||||
|
slices.Sort(serviceAccounts)
|
||||||
|
return slices.Compact(serviceAccounts)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *LinkerdPolicyGenerator) Finalize(writer io.Writer) error {
|
||||||
|
for _, network := range g.networks {
|
||||||
|
fmt.Fprintf(os.Stderr, "NetworkAuthentication default/%s\n", network.Name)
|
||||||
|
err := g.policyTemplates.Execute("linkerd", "networkauthentication", writer, network)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -42,10 +42,7 @@ func generateNetworkPolicy(files []string, options *Options) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
var generator Generator
|
var generator Generator
|
||||||
generator = NetworkPolicyGenerator{
|
generator = NewNetworkPolicyGenerator(config, policyTemplates)
|
||||||
config: config,
|
|
||||||
policyTemplates: policyTemplates,
|
|
||||||
}
|
|
||||||
err = Generate(os.Stdout, generator, config)
|
err = Generate(os.Stdout, generator, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -74,10 +71,7 @@ func generateLinkerdPolicies(files []string, options *Options) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
var generator Generator
|
var generator Generator
|
||||||
generator = LinkerdPolicyGenerator{
|
generator = NewLinkerdPolicyGenerator(config, policyTemplates)
|
||||||
config: config,
|
|
||||||
policyTemplates: policyTemplates,
|
|
||||||
}
|
|
||||||
err = Generate(os.Stdout, generator, config)
|
err = Generate(os.Stdout, generator, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -12,11 +12,18 @@ type NetworkPolicyGenerator struct {
|
|||||||
policyTemplates *PolicyTemplates
|
policyTemplates *PolicyTemplates
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g NetworkPolicyGenerator) Init(writer io.Writer) error {
|
func NewNetworkPolicyGenerator(config *Config, templates *PolicyTemplates) *NetworkPolicyGenerator {
|
||||||
|
return &NetworkPolicyGenerator{
|
||||||
|
config: config,
|
||||||
|
policyTemplates: templates,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *NetworkPolicyGenerator) Init(writer io.Writer) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g NetworkPolicyGenerator) GenerateNamespace(writer io.Writer, namespace *Namespace) error {
|
func (g *NetworkPolicyGenerator) GenerateNamespace(writer io.Writer, namespace *Namespace) error {
|
||||||
fmt.Fprintf(os.Stderr, "Namespace %s\n", namespace.Name)
|
fmt.Fprintf(os.Stderr, "Namespace %s\n", namespace.Name)
|
||||||
|
|
||||||
templates := g.policyTemplates.NamespaceTemplates("netpol", namespace.Capabilities)
|
templates := g.policyTemplates.NamespaceTemplates("netpol", namespace.Capabilities)
|
||||||
@ -30,7 +37,7 @@ func (g NetworkPolicyGenerator) GenerateNamespace(writer io.Writer, namespace *N
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g NetworkPolicyGenerator) GenerateCommunicationRule(
|
func (g *NetworkPolicyGenerator) GenerateCommunicationRule(
|
||||||
writer io.Writer,
|
writer io.Writer,
|
||||||
app *Application,
|
app *Application,
|
||||||
ingress *Ingress,
|
ingress *Ingress,
|
||||||
@ -42,11 +49,7 @@ func (g NetworkPolicyGenerator) GenerateCommunicationRule(
|
|||||||
len(egress.Networks) > 0 {
|
len(egress.Networks) > 0 {
|
||||||
// non-trivial regular network policy
|
// non-trivial regular network policy
|
||||||
|
|
||||||
tmpl := g.policyTemplates.ApplicationTemplate("netpol")
|
err := g.policyTemplates.Execute("netpol", "pod", writer, map[string]any{
|
||||||
if tmpl == nil {
|
|
||||||
return fmt.Errorf("Could not find policy template for 'netpol'")
|
|
||||||
}
|
|
||||||
err := tmpl.Execute(writer, map[string]any{
|
|
||||||
"app": app,
|
"app": app,
|
||||||
"ingress": ingress,
|
"ingress": ingress,
|
||||||
"egress": egress,
|
"egress": egress,
|
||||||
@ -68,18 +71,15 @@ func (g NetworkPolicyGenerator) GenerateCommunicationRule(
|
|||||||
}
|
}
|
||||||
|
|
||||||
for predefined, _ := range allPredefined {
|
for predefined, _ := range allPredefined {
|
||||||
tmpl := g.policyTemplates.PredefineApplicationPolicyTemplate("netpol", predefined)
|
err := g.policyTemplates.Execute("netpol", predefined,
|
||||||
if tmpl == nil {
|
writer, map[string]any{
|
||||||
return fmt.Errorf("Could not find predefined template for netpol/%s", predefined)
|
"app": app,
|
||||||
}
|
"ingress": slices.Contains(ingress.Predefined, predefined),
|
||||||
err := tmpl.Execute(writer, map[string]any{
|
"egress": slices.Contains(egress.Predefined, predefined),
|
||||||
"app": app,
|
"labels": map[string]string{
|
||||||
"ingress": slices.Contains(ingress.Predefined, predefined),
|
"policy-generator": "1",
|
||||||
"egress": slices.Contains(egress.Predefined, predefined),
|
},
|
||||||
"labels": map[string]string{
|
})
|
||||||
"policy-generator": "1",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -87,3 +87,7 @@ func (g NetworkPolicyGenerator) GenerateCommunicationRule(
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (g *NetworkPolicyGenerator) Finalize(writer io.Writer) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -2,6 +2,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
@ -119,12 +120,21 @@ func (t *PolicyTemplates) NamespaceTemplates(policyType string, capabilities []s
|
|||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *PolicyTemplates) ApplicationTemplate(policyType string) *template.Template {
|
|
||||||
tmpl := t.templates.Lookup(fmt.Sprintf("templates/%s/application/pod.yaml", policyType))
|
|
||||||
return tmpl
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *PolicyTemplates) PredefineApplicationPolicyTemplate(policyType string, predefined string) *template.Template {
|
func (t *PolicyTemplates) PredefineApplicationPolicyTemplate(policyType string, predefined string) *template.Template {
|
||||||
tmpl := t.templates.Lookup(fmt.Sprintf("templates/%s/application/%s.yaml", policyType, predefined))
|
tmpl := t.templates.Lookup(fmt.Sprintf("templates/%s/application/%s.yaml", policyType, predefined))
|
||||||
return tmpl
|
return tmpl
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *PolicyTemplates) Execute(policyType string, predefined string, writer io.Writer, data any) error {
|
||||||
|
tmpl := t.PredefineApplicationPolicyTemplate(policyType, predefined)
|
||||||
|
if tmpl == nil {
|
||||||
|
return fmt.Errorf("Could not find template %s for policy type %s", predefined, policyType)
|
||||||
|
}
|
||||||
|
err := tmpl.Execute(writer, data)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error rendering template %s for policy type %s: %w", predefined, policyType, err)
|
||||||
|
}
|
||||||
|
writer.Write([]byte("\n"))
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
apiVersion: policy.linkerd.io/v1alpha1
|
||||||
|
kind: AuthorizationPolicy
|
||||||
|
metadata:
|
||||||
|
name: {{ .app.Name }}-p{{ .port }}
|
||||||
|
namespace: {{ .app.Namespace.Name}}
|
||||||
|
spec:
|
||||||
|
targetRef:
|
||||||
|
group: policy.linkerd.io
|
||||||
|
kind: Server
|
||||||
|
name: {{ .app.Name }}-p{{ .port }}
|
||||||
|
requiredAuthenticationRefs:
|
||||||
|
- name: {{ .app.Name }}-p{{ .port }}
|
||||||
|
namespace: {{ .app.Namespace.Name }}
|
||||||
|
kind: MeshTLSAuthentication
|
||||||
|
group: policy.linkerd.io
|
||||||
|
{{- range $net := .clientNetworks }}
|
||||||
|
- name: {{ $net.Network.Name }}
|
||||||
|
namespace: default
|
||||||
|
kind: NetworkAuthentication
|
||||||
|
group: policy.linkerd.io
|
||||||
|
{{- end }}
|
@ -2,16 +2,12 @@
|
|||||||
apiVersion: policy.linkerd.io/v1alpha1
|
apiVersion: policy.linkerd.io/v1alpha1
|
||||||
kind: MeshTLSAuthentication
|
kind: MeshTLSAuthentication
|
||||||
metadata:
|
metadata:
|
||||||
name: {{ .Name }}
|
name: {{ .app.Name }}-{{.port}}
|
||||||
namespace: {{ .Namespace.Name }}
|
namespace: {{ .app.Namespace.Name }}
|
||||||
spec:
|
spec:
|
||||||
{{- if .ServiceAccounts }}
|
|
||||||
identityRefs:
|
identityRefs:
|
||||||
{{- range $sa := .ServiceAccounts }}
|
{{- range $sa := .serviceAccounts }}
|
||||||
- kind: ServiceAccount
|
- kind: ServiceAccount
|
||||||
name: {{ $sa }}
|
name: {{ $sa }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- else }}
|
|
||||||
fail (printf "no service accounts defined for app %s" .Name )
|
|
||||||
{{- end}}
|
|
||||||
|
|
||||||
|
17
cmd/policygen/util.go
Normal file
17
cmd/policygen/util.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "maps"
|
||||||
|
|
||||||
|
func MapKeys[K comparable, V any](m map[K]V) []K {
|
||||||
|
return IterToSlice(maps.Keys(m))
|
||||||
|
}
|
||||||
|
func MapValues[K comparable, V any](m map[K]V) []V {
|
||||||
|
return IterToSlice(maps.Values(m))
|
||||||
|
}
|
||||||
|
func Map[K any, V any](s []K, mapper func(K) V) []V {
|
||||||
|
res := make([]V, len(s))
|
||||||
|
for i, _ := range s {
|
||||||
|
res[i] = mapper(s[i])
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user