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
|
return err
|
||||||
}
|
}
|
||||||
*c = CIDR(s)
|
*c = CIDR(s)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,6 +66,7 @@ type Application struct {
|
|||||||
MatchLabels map[string]string `yaml:"matchLabels"`
|
MatchLabels map[string]string `yaml:"matchLabels"`
|
||||||
//MatchExpressions []MatchExpression `yaml:"matchExpressions" validate:"omitempty,dive"`
|
//MatchExpressions []MatchExpression `yaml:"matchExpressions" validate:"omitempty,dive"`
|
||||||
MatchExpressions []metav1.LabelSelectorRequirement `yaml:"matchExpressions" validate:"omitempty,dive"`
|
MatchExpressions []metav1.LabelSelectorRequirement `yaml:"matchExpressions" validate:"omitempty,dive"`
|
||||||
|
ServiceAccounts []string `yaml:"serviceAccounts,omitempty"`
|
||||||
Namespace *Namespace `yaml:"-" validate:"-"`
|
Namespace *Namespace `yaml:"-" validate:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -171,6 +173,16 @@ func (c Config) GetApplication(name string) (*Application, *Network, string) {
|
|||||||
return nil, nil, ""
|
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) {
|
func LoadConfig(file string) (*Config, error) {
|
||||||
fmt.Fprintf(os.Stderr, "Reading config %s\n", file)
|
fmt.Fprintf(os.Stderr, "Reading config %s\n", file)
|
||||||
yamlFile, err := os.ReadFile(file)
|
yamlFile, err := os.ReadFile(file)
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"maps"
|
"maps"
|
||||||
"os"
|
"os"
|
||||||
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -38,11 +39,28 @@ func MapKeys[K comparable, V any](m map[K]V) []K {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func validate(files []string, options *Options) error {
|
func validate(files []string, options *Options) error {
|
||||||
|
clientset, _ := GetKubernetesConnection()
|
||||||
config, err := readConfig(files)
|
config, err := readConfig(files)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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 {
|
for _, ns := range config.Namespaces {
|
||||||
namespace := ns.Name
|
namespace := ns.Name
|
||||||
_, err = clientset.CoreV1().Namespaces().Get(context.Background(), namespace, metav1.GetOptions{})
|
_, 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)
|
LogValidationMsg(Error, "ERROR: namespace not found: %s", namespace)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// checking for service accounts shared by applications
|
||||||
|
// map of namespace/sa -> []applicationname
|
||||||
|
serviceAccountMap := make(map[string][]string)
|
||||||
|
|
||||||
for _, application := range ns.Applications {
|
for _, application := range ns.Applications {
|
||||||
pods := FindPods(application, clientset)
|
pods := FindPods(application, clientset)
|
||||||
|
applicationPods[application.Name] = pods
|
||||||
//log.Printf(namespace + "/" + application.Name)
|
//log.Printf(namespace + "/" + application.Name)
|
||||||
if len(pods) == 0 {
|
if len(pods) == 0 {
|
||||||
LogValidationMsg(Error, "application %s: no running pods found", application.Name)
|
LogValidationMsg(Error, "application %s: no running pods found", application.Name)
|
||||||
@ -76,9 +100,47 @@ func validate(files []string, options *Options) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Check service accounts
|
// 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 {
|
for _, communication := range config.Communications {
|
||||||
if len(communication.Ports) == 0 {
|
if len(communication.Ports) == 0 {
|
||||||
continue
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,6 +27,8 @@ namespaces:
|
|||||||
- port: 8081
|
- port: 8081
|
||||||
- port: 8082
|
- port: 8082
|
||||||
protocol: UDP
|
protocol: UDP
|
||||||
|
serviceAccounts:
|
||||||
|
- jantje
|
||||||
matchLabels:
|
matchLabels:
|
||||||
app: nexus-server
|
app: nexus-server
|
||||||
#matchExpressions:
|
#matchExpressions:
|
||||||
|
Loading…
Reference in New Issue
Block a user