eliminating duplicate communications. this caused problems with linkerd

and helm
This commit is contained in:
Erik Brakkee 2025-01-25 14:49:33 +01:00
parent cd4023f5ce
commit 2bea96cc57
5 changed files with 84 additions and 94 deletions

View File

@ -3,6 +3,7 @@ package main
import ( import (
"io" "io"
"os" "os"
"slices"
) )
type Generator interface { type Generator interface {
@ -15,13 +16,15 @@ type Generator interface {
type ApplicationPeer struct { type ApplicationPeer struct {
Application *Application Application *Application
Ports []Port Ports []Port
Rule string // used as convenience for template rendering.
Rule string
} }
type NetworkPeer struct { type NetworkPeer struct {
Network *Network Network *Network
Ports []Port Ports []Port
Rule string // Used as convenience for template rendering.
Rule string
} }
type Peer struct { type Peer struct {
@ -32,13 +35,23 @@ type Peer struct {
func (p *Peer) append(app *ApplicationPeer, network *NetworkPeer, predefined string) { func (p *Peer) append(app *ApplicationPeer, network *NetworkPeer, predefined string) {
if app != nil { if app != nil {
p.Applications = append(p.Applications, app) if !slices.ContainsFunc(p.Applications, func(a *ApplicationPeer) bool {
return a.Application.Name == app.Application.Name && slices.Equal(a.Ports, app.Ports)
}) {
p.Applications = append(p.Applications, app)
}
} }
if network != nil { if network != nil {
p.Networks = append(p.Networks, network) if !slices.ContainsFunc(p.Networks, func(n *NetworkPeer) bool {
return n.Network.Name == network.Network.Name && slices.Equal(n.Network.Ports, network.Network.Ports)
}) {
p.Networks = append(p.Networks, network)
}
} }
if predefined != "" { if predefined != "" {
p.Predefined = append(p.Predefined, predefined) if !slices.Contains(p.Predefined, predefined) {
p.Predefined = append(p.Predefined, predefined)
}
} }
} }

View File

@ -7,7 +7,6 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"os" "os"
"slices" "slices"
"strconv"
"strings" "strings"
) )
@ -15,6 +14,7 @@ type LinkerdPolicyGenerator struct {
config *Config config *Config
policyTemplates *PolicyTemplates policyTemplates *PolicyTemplates
apps map[string]*Application
networks map[string]*Network networks map[string]*Network
} }
@ -22,6 +22,7 @@ func NewLinkerdPolicyGenerator(config *Config, templates *PolicyTemplates) *Link
return &LinkerdPolicyGenerator{ return &LinkerdPolicyGenerator{
config: config, config: config,
policyTemplates: templates, policyTemplates: templates,
apps: make(map[string]*Application),
networks: make(map[string]*Network), networks: make(map[string]*Network),
} }
} }
@ -53,83 +54,37 @@ func (g *LinkerdPolicyGenerator) GenerateCommunicationRule(
return err return err
} }
if len(ingress.Applications)+ for _, ingress := range ingress.Applications {
len(ingress.Networks) > 0 { for _, port := range ingress.Ports {
ports := make(map[int]bool) if port.Protocol != "TCP" {
// continue
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)
}
} }
} g.apps[ingress.Application.Name] = ingress.Application
err = g.policyTemplates.Execute("linkerd", "authorizationpolicy-app",
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 only 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, writer,
map[string]any{ map[string]any{
"app": app, "app": app,
"port": port, "port": port.Port,
"clientNetworks": clientNetworks[port], "client": ingress.Application,
})
if err != nil {
return err
}
}
}
for _, ingress := range ingress.Networks {
for _, port := range ingress.Ports {
if port.Protocol != "TCP" {
continue
}
g.networks[ingress.Network.Name] = ingress.Network
err = g.policyTemplates.Execute("linkerd", "authorizationpolicy-net",
writer,
map[string]any{
"app": app,
"port": port.Port,
"client": ingress.Network,
}) })
if err != nil { if err != nil {
return err return err
@ -160,6 +115,15 @@ func (g *LinkerdPolicyGenerator) serviceAccounts(peers []*ApplicationPeer) []v1.
} }
func (g *LinkerdPolicyGenerator) Finalize(writer io.Writer) error { func (g *LinkerdPolicyGenerator) Finalize(writer io.Writer) error {
for _, app := range g.apps {
fmt.Fprintf(os.Stderr, "MeshTLSAuthentication %s/%s %v\n",
app.Namespace.Name, app.Name, app.ServiceAccounts)
err := g.policyTemplates.Execute("linkerd", "meshtlsauthentication",
writer, app)
if err != nil {
return err
}
}
for _, network := range g.networks { for _, network := range g.networks {
fmt.Fprintf(os.Stderr, "NetworkAuthentication default/%s\n", network.Name) fmt.Fprintf(os.Stderr, "NetworkAuthentication default/%s\n", network.Name)
err := g.policyTemplates.Execute("linkerd", "networkauthentication", writer, network) err := g.policyTemplates.Execute("linkerd", "networkauthentication", writer, network)

View File

@ -0,0 +1,16 @@
---
apiVersion: policy.linkerd.io/v1alpha1
kind: AuthorizationPolicy
metadata:
name: app-{{ .client.Name }}-to-{{ .app.Name }}-p{{ .port }}
namespace: {{ .app.Namespace.Name}}
spec:
targetRef:
group: policy.linkerd.io
kind: Server
name: {{ .app.Name }}-p{{ .port }}
requiredAuthenticationRefs:
- name: {{ .client.Name }}
namespace: {{ .client.Namespace.Name }}
kind: MeshTLSAuthentication
group: policy.linkerd.io

View File

@ -2,7 +2,7 @@
apiVersion: policy.linkerd.io/v1alpha1 apiVersion: policy.linkerd.io/v1alpha1
kind: AuthorizationPolicy kind: AuthorizationPolicy
metadata: metadata:
name: {{ .app.Name }}-p{{ .port }} name: net-{{ .client.Name }}-to-{{ .app.Name }}-p{{ .port }}
namespace: {{ .app.Namespace.Name}} namespace: {{ .app.Namespace.Name}}
spec: spec:
targetRef: targetRef:
@ -10,13 +10,7 @@ spec:
kind: Server kind: Server
name: {{ .app.Name }}-p{{ .port }} name: {{ .app.Name }}-p{{ .port }}
requiredAuthenticationRefs: requiredAuthenticationRefs:
- name: {{ .app.Name }}-p{{ .port }} - name: {{ .client.Name }}
namespace: {{ .app.Namespace.Name }}
kind: MeshTLSAuthentication
group: policy.linkerd.io
{{- range $net := .clientNetworks }}
- name: {{ $net.Network.Name }}
namespace: default namespace: default
kind: NetworkAuthentication kind: NetworkAuthentication
group: policy.linkerd.io group: policy.linkerd.io
{{- end }}

View File

@ -2,13 +2,16 @@
apiVersion: policy.linkerd.io/v1alpha1 apiVersion: policy.linkerd.io/v1alpha1
kind: MeshTLSAuthentication kind: MeshTLSAuthentication
metadata: metadata:
name: {{ .app.Name }}-p{{.port}} name: {{ .Name }}
namespace: {{ .app.Namespace.Name }} namespace: {{ .Namespace.Name }}
spec: spec:
{{- if .ServiceAccounts }}
identityRefs: identityRefs:
{{- range $sa := .serviceAccounts }} {{- range $sa := .ServiceAccounts }}
- kind: ServiceAccount - kind: ServiceAccount
name: {{ $sa.Name }} name: {{ $sa }}
namespace: {{ $sa.Namespace }}
{{- end }} {{- end }}
{{- else }}
fail (printf "no service accounts defined for app %s" .Name )
{{- end}}