now inferring the service accounts from the network policy config.
This commit is contained in:
parent
b3c24048d6
commit
95e7106dba
@ -31,6 +31,7 @@ func (c *CIDR) UnmarshalYAML(value []byte) error {
|
||||
return err
|
||||
}
|
||||
*c = CIDR(s)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -65,6 +66,7 @@ type Application struct {
|
||||
MatchLabels map[string]string `yaml:"matchLabels"`
|
||||
//MatchExpressions []MatchExpression `yaml:"matchExpressions" validate:"omitempty,dive"`
|
||||
MatchExpressions []metav1.LabelSelectorRequirement `yaml:"matchExpressions" validate:"omitempty,dive"`
|
||||
ServiceAccounts []string `yaml:"serviceAccounts,omitempty"`
|
||||
Namespace *Namespace `yaml:"-" validate:"-"`
|
||||
}
|
||||
|
||||
@ -171,6 +173,16 @@ func (c Config) GetApplication(name string) (*Application, *Network, string) {
|
||||
return nil, nil, ""
|
||||
}
|
||||
|
||||
func (c *Config) Infer(resolver func(application *Application) []string) {
|
||||
for _, ns := range c.Namespaces {
|
||||
for _, app := range ns.Applications {
|
||||
if len(app.ServiceAccounts) == 0 {
|
||||
app.ServiceAccounts = resolver(app)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func LoadConfig(file string) (*Config, error) {
|
||||
fmt.Fprintf(os.Stderr, "Reading config %s\n", file)
|
||||
yamlFile, err := os.ReadFile(file)
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
"log"
|
||||
"maps"
|
||||
"os"
|
||||
"slices"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
@ -38,11 +39,28 @@ func MapKeys[K comparable, V any](m map[K]V) []K {
|
||||
}
|
||||
|
||||
func validate(files []string, options *Options) error {
|
||||
clientset, _ := GetKubernetesConnection()
|
||||
config, err := readConfig(files)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
clientset, _ := GetKubernetesConnection()
|
||||
config.Infer(func(application *Application) []string {
|
||||
pods := FindPods(application, clientset)
|
||||
var res []string
|
||||
for _, pod := range pods {
|
||||
if !slices.Contains(res, pod.Spec.ServiceAccountName) {
|
||||
res = append(res, pod.Spec.ServiceAccountName)
|
||||
}
|
||||
}
|
||||
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.
|
||||
openToClosedAccess := make(map[string]string)
|
||||
|
||||
applicationPods := make(map[string][]v1.Pod)
|
||||
for _, ns := range config.Namespaces {
|
||||
namespace := ns.Name
|
||||
_, err = clientset.CoreV1().Namespaces().Get(context.Background(), namespace, metav1.GetOptions{})
|
||||
@ -50,8 +68,14 @@ func validate(files []string, options *Options) error {
|
||||
LogValidationMsg(Error, "ERROR: namespace not found: %s", namespace)
|
||||
continue
|
||||
}
|
||||
|
||||
// checking for service accounts shared by applications
|
||||
// map of namespace/sa -> []applicationname
|
||||
serviceAccountMap := make(map[string][]string)
|
||||
|
||||
for _, application := range ns.Applications {
|
||||
pods := FindPods(application, clientset)
|
||||
applicationPods[application.Name] = pods
|
||||
//log.Printf(namespace + "/" + application.Name)
|
||||
if len(pods) == 0 {
|
||||
LogValidationMsg(Error, "application %s: no running pods found", application.Name)
|
||||
@ -76,9 +100,47 @@ func validate(files []string, options *Options) error {
|
||||
}
|
||||
}
|
||||
// Check service accounts
|
||||
applicationServiceAccounts := make(map[string]bool)
|
||||
for _, sa := range application.ServiceAccounts {
|
||||
applicationServiceAccounts[sa] = true
|
||||
}
|
||||
for _, pod := range pods {
|
||||
delete(applicationServiceAccounts, pod.Spec.ServiceAccountName)
|
||||
}
|
||||
if len(applicationServiceAccounts) > 0 {
|
||||
LogValidationMsg(Error, "application %s: service accounts %v configured but not used by workloads",
|
||||
application.Name, MapKeys(applicationServiceAccounts))
|
||||
}
|
||||
for _, pod := range pods {
|
||||
sa := pod.Namespace + "/" + pod.Spec.ServiceAccountName
|
||||
serviceAccountMap[sa] = append(serviceAccountMap[sa],
|
||||
application.Name)
|
||||
|
||||
if pod.Spec.ServiceAccountName == "default" {
|
||||
LogValidationMsg(Warning, "Pod %s/%s: running with default service account",
|
||||
pod.Namespace, pod.Name)
|
||||
if application.ServiceAccounts != nil && !slices.Contains(application.ServiceAccounts, "default") {
|
||||
LogValidationMsg(Warning, "application %s: Pod %s/%s: running with 'default' service account but configured with %v",
|
||||
application.Name, pod.Namespace, pod.Name, application.ServiceAccounts)
|
||||
}
|
||||
} else {
|
||||
if !slices.Contains(application.ServiceAccounts, pod.Spec.ServiceAccountName) {
|
||||
LogValidationMsg(Warning, "application %s: Pod %s/%s: running with service account '%s' but configured with %v",
|
||||
application.Name, pod.Namespace, pod.Name, pod.Spec.ServiceAccountName, application.ServiceAccounts)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// service accounts shared by multiple applications.
|
||||
for sa, applist := range serviceAccountMap {
|
||||
if len(applist) == 1 {
|
||||
continue
|
||||
}
|
||||
LogValidationMsg(Error, "service account %s: shared by multiple applications %v", sa, applist)
|
||||
}
|
||||
}
|
||||
|
||||
for _, communication := range config.Communications {
|
||||
if len(communication.Ports) == 0 {
|
||||
continue
|
||||
@ -99,7 +161,42 @@ func validate(files []string, options *Options) error {
|
||||
|
||||
}
|
||||
}
|
||||
for _, communication := range config.Communications {
|
||||
for _, applicationName := range communication.To {
|
||||
application, _, _ := config.GetApplication(applicationName)
|
||||
if application == nil {
|
||||
continue
|
||||
}
|
||||
// capability linkerd must exist on target namespace
|
||||
if !slices.Contains(application.Namespace.Capabilities, "linkerd") {
|
||||
continue
|
||||
}
|
||||
// annotation linkerd.io/inject must not be disabled
|
||||
pods := applicationPods[applicationName]
|
||||
for _, pod := range pods {
|
||||
if pod.Annotations["linkerd.io/inject"] == "disabled" {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if !application.Namespace.Open {
|
||||
for _, applicationNameFrom := range communication.From {
|
||||
applicationFrom, _, _ := config.GetApplication(applicationNameFrom)
|
||||
if applicationFrom != nil && !applicationFrom.Namespace.Open {
|
||||
continue
|
||||
}
|
||||
openToClosedAccess[applicationNameFrom] = applicationName
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for appFrom, appTo := range openToClosedAccess {
|
||||
LogValidationMsg(Error, "Access from 'open' application '%s' to 'closed' application '%s'",
|
||||
appFrom, appTo)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -27,6 +27,8 @@ namespaces:
|
||||
- port: 8081
|
||||
- port: 8082
|
||||
protocol: UDP
|
||||
serviceAccounts:
|
||||
- jantje
|
||||
matchLabels:
|
||||
app: nexus-server
|
||||
#matchExpressions:
|
||||
|
Loading…
Reference in New Issue
Block a user