inferring application ports in case not configure so that they

can be used for linkerd authorization.
This commit is contained in:
Erik Brakkee 2025-01-17 21:16:41 +01:00
parent 2066aad656
commit ee8c0a2204
3 changed files with 53 additions and 16 deletions

View File

@ -2,12 +2,14 @@ package main
import ( import (
"context" "context"
"fmt"
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes"
"log" "log"
"slices" "slices"
"strconv"
) )
type Cluster struct { type Cluster struct {
@ -93,11 +95,35 @@ func (c *Cluster) IsLinkerdEnabled(application *Application) bool {
return ns.Annotations["linkerd.io/inject"] == "enabled" return ns.Annotations["linkerd.io/inject"] == "enabled"
} }
func (c *Cluster) Ports(application *Application, nameBased bool) []Port { func (c *Cluster) PortNumbers(application *Application) []Port {
// gather unique ports based on name if !c.IsLinkerdEnabled(application) {
// or based on number. return nil
// Warning: same name different ports }
// same port different names. tcpPorts := make(map[int]Port)
// Can occur if the selector matches multiple replicasets. udpPorts := make(map[int]Port)
return nil for _, pod := range c.Pods(application) {
for _, container := range pod.Spec.Containers {
for _, port := range container.Ports {
switch port.Protocol {
case "TCP":
tcpPorts[int(port.ContainerPort)] = Port{
Port: strconv.Itoa(int(port.ContainerPort)),
Protocol: string(port.Protocol),
}
case "UDP":
udpPorts[int(port.ContainerPort)] = Port{
Port: strconv.Itoa(int(port.ContainerPort)),
Protocol: string(port.Protocol),
}
default:
panic(fmt.Sprintf("Unknown port type for pod %s/%s: %s",
pod.Namespace, pod.Name, port.Protocol))
}
}
}
}
res := MapValues(tcpPorts)
res = append(res, MapValues(udpPorts)...)
return res
} }

View File

@ -6,9 +6,11 @@ import (
"fmt" "fmt"
"github.com/goccy/go-yaml" "github.com/goccy/go-yaml"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"log"
"net" "net"
"os" "os"
"slices" "slices"
"strings"
) )
var PREDEFINED_APPS = []string{"apiserver"} var PREDEFINED_APPS = []string{"apiserver"}
@ -180,11 +182,18 @@ func (c Config) GetApplication(name string) (*Application, *Network, string) {
return nil, nil, "" return nil, nil, ""
} }
func (c *Config) Infer(resolver func(application *Application) []string) { func (c *Config) Infer(resolver Resolver) {
for _, ns := range c.Namespaces { for _, ns := range c.Namespaces {
for _, app := range ns.Applications { for _, app := range ns.Applications {
if len(app.ServiceAccounts) == 0 { if len(app.ServiceAccounts) == 0 {
app.ServiceAccounts = resolver(app) app.ServiceAccounts = resolver.ServiceAccounts(app)
log.Printf("Inferred service accounts: %s/%s: %v", app.Namespace.Name, app.Name,
app.ServiceAccounts)
}
if len(app.Ports) == 0 && !strings.HasPrefix(ns.Name, "linkerd") {
app.Ports = resolver.PortNumbers(app)
log.Printf("Inferred ports: %s/%s: %v", app.Namespace.Name, app.Name,
app.Ports)
} }
} }
} }

View File

@ -6,7 +6,6 @@ import (
"iter" "iter"
"k8s.io/api/core/v1" "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"log"
"maps" "maps"
"os" "os"
"slices" "slices"
@ -21,6 +20,11 @@ const (
Error Error
) )
type Resolver interface {
ServiceAccounts(application *Application) []string
PortNumbers(application *Application) []Port
}
func LogValidationMsg(level ValidationLevel, msg string, v ...any) { func LogValidationMsg(level ValidationLevel, msg string, v ...any) {
fmt.Fprintf(os.Stderr, "NOTICE: "+msg+"\n", v...) fmt.Fprintf(os.Stderr, "NOTICE: "+msg+"\n", v...)
} }
@ -36,6 +40,9 @@ func IterToSlice[K any](i iter.Seq[K]) []K {
func MapKeys[K comparable, V any](m map[K]V) []K { func MapKeys[K comparable, V any](m map[K]V) []K {
return IterToSlice(maps.Keys(m)) 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()
@ -49,12 +56,7 @@ func validate(files []string, options *Options) error {
return err return err
} }
config.Infer(func(application *Application) []string { config.Infer(cluster)
res := cluster.ServiceAccounts(application)
log.Printf("Inferred service accounts: %s/%s: %v", application.Namespace.Name, application.Name,
res)
return res
})
// map applname1 -> appname2 where appname1 is in an open namespace and app2 is in a closed namespace. // map applname1 -> appname2 where appname1 is in an open namespace and app2 is in a closed namespace.
// Exclusing when 'from' side is a CIDR. // Exclusing when 'from' side is a CIDR.