pkg/audit/traefik_test.go
Rama main · réplica de lectura del repositorio original.
package audit
import (
"testing"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
func traefikRoute(group, kind, ns, name string, svcRefs, mwRefs []map[string]any) *unstructured.Unstructured {
route := map[string]any{}
if svcRefs != nil {
route["services"] = toIfaceSlice(svcRefs)
}
if mwRefs != nil {
route["middlewares"] = toIfaceSlice(mwRefs)
}
return &unstructured.Unstructured{Object: map[string]any{
"apiVersion": group + "/v1alpha1",
"kind": kind,
"metadata": map[string]any{"name": name, "namespace": ns, "uid": group + "/" + ns + "/" + name},
"spec": map[string]any{"routes": []any{route}},
}}
}
func traefikObj(group, kind, ns, name string) *unstructured.Unstructured {
return &unstructured.Unstructured{Object: map[string]any{
"apiVersion": group + "/v1alpha1",
"kind": kind,
"metadata": map[string]any{"name": name, "namespace": ns},
}}
}
func toIfaceSlice(maps []map[string]any) []any {
out := make([]any, len(maps))
for i, m := range maps {
out[i] = m
}
return out
}
func svc(ns, name string) *corev1.Service {
return &corev1.Service{ObjectMeta: metav1.ObjectMeta{Namespace: ns, Name: name}}
}
// authoritative builds the group\x00Kind set the runner would produce when those
// target kinds are served by a synced cluster-wide informer.
func authoritative(keys ...string) map[string]bool {
m := map[string]bool{}
for _, k := range keys {
m[k] = true
}
return m
}
func findingIDs(findings []Finding) map[string]int {
m := map[string]int{}
for _, f := range findings {
m[f.CheckID]++
}
return m
}
func TestCheckTraefikDanglingRefs(t *testing.T) {
const g = "traefik.io"
t.Run("flags missing service and middleware, accepts present ones", func(t *testing.T) {
input := &CheckInput{
AllServices: []*corev1.Service{svc("app", "present-svc")},
Middlewares: []*unstructured.Unstructured{traefikObj(g, "Middleware", "app", "present-mw")},
TraefikAuthoritativeKinds: authoritative(g + "\x00Middleware"),
IngressRoutes: []*unstructured.Unstructured{
traefikRoute(g, "IngressRoute", "app", "r1",
[]map[string]any{{"name": "present-svc"}, {"name": "missing-svc"}},
[]map[string]any{{"name": "present-mw"}, {"name": "missing-mw"}},
),
},
}
ids := findingIDs(checkTraefikDanglingRefs(newEvalTracker(), input))
if ids["traefikRouteMissingService"] != 1 {
t.Errorf("want 1 missing-service, got %d", ids["traefikRouteMissingService"])
}
if ids["traefikRouteMissingMiddleware"] != 1 {
t.Errorf("want 1 missing-middleware, got %d", ids["traefikRouteMissingMiddleware"])
}
})
t.Run("all refs present → no findings", func(t *testing.T) {
input := &CheckInput{
AllServices: []*corev1.Service{svc("app", "s")},
Middlewares: []*unstructured.Unstructured{traefikObj(g, "Middleware", "app", "mw")},
TraefikAuthoritativeKinds: authoritative(g + "\x00Middleware"),
IngressRoutes: []*unstructured.Unstructured{
traefikRoute(g, "IngressRoute", "app", "r",
[]map[string]any{{"name": "s"}}, []map[string]any{{"name": "mw"}}),
},
}
if got := checkTraefikDanglingRefs(newEvalTracker(), input); len(got) != 0 {
t.Errorf("want no findings, got %v", got)
}
})
t.Run("cross-namespace middleware ref RESOLVES against cluster-wide inventory", func(t *testing.T) {
// Route in ns "app" references a Middleware explicitly in ns "shared".
// The middleware exists there; since the inventory is cluster-wide and
// authoritative, this must NOT be flagged.
input := &CheckInput{
AllServices: []*corev1.Service{},
Middlewares: []*unstructured.Unstructured{traefikObj(g, "Middleware", "shared", "auth")},
TraefikAuthoritativeKinds: authoritative(g + "\x00Middleware"),
IngressRoutes: []*unstructured.Unstructured{
traefikRoute(g, "IngressRoute", "app", "r", nil,
[]map[string]any{{"name": "auth", "namespace": "shared"}}),
},
}
if n := findingIDs(checkTraefikDanglingRefs(newEvalTracker(), input))["traefikRouteMissingMiddleware"]; n != 0 {
t.Errorf("cross-namespace ref to an existing middleware must not be flagged, got %d", n)
}
})
t.Run("not authoritative → skip (no false positive)", func(t *testing.T) {
// Middleware kind NOT in the authoritative set (e.g. namespace-scoped
// fallback). Even though the referenced mw is absent, do not assert.
input := &CheckInput{
AllServices: []*corev1.Service{},
Middlewares: []*unstructured.Unstructured{},
TraefikAuthoritativeKinds: authoritative(), // empty → nothing authoritative
IngressRoutes: []*unstructured.Unstructured{
traefikRoute(g, "IngressRoute", "app", "r", nil, []map[string]any{{"name": "ghost"}}),
},
}
if n := findingIDs(checkTraefikDanglingRefs(newEvalTracker(), input))["traefikRouteMissingMiddleware"]; n != 0 {
t.Errorf("non-authoritative middleware kind must not be asserted, got %d", n)
}
})
t.Run("cross-group middleware does not satisfy the reference", func(t *testing.T) {
input := &CheckInput{
AllServices: []*corev1.Service{},
Middlewares: []*unstructured.Unstructured{traefikObj("traefik.containo.us", "Middleware", "app", "mw")},
TraefikAuthoritativeKinds: authoritative(g + "\x00Middleware"),
IngressRoutes: []*unstructured.Unstructured{
traefikRoute(g, "IngressRoute", "app", "r", nil, []map[string]any{{"name": "mw"}}),
},
}
if n := findingIDs(checkTraefikDanglingRefs(newEvalTracker(), input))["traefikRouteMissingMiddleware"]; n != 1 {
t.Errorf("traefik.io router must not be satisfied by a traefik.containo.us Middleware, got %d", n)
}
})
t.Run("IngressRouteTCP resolves against MiddlewareTCP, per-kind independent", func(t *testing.T) {
// Only Middleware (not MiddlewareTCP) is authoritative + present. A TCP
// router's MiddlewareTCP ref must be skipped (its kind isn't authoritative),
// NOT fabricated as missing.
input := &CheckInput{
AllServices: []*corev1.Service{},
Middlewares: []*unstructured.Unstructured{traefikObj(g, "Middleware", "app", "mw")},
TraefikAuthoritativeKinds: authoritative(g + "\x00Middleware"), // NOT MiddlewareTCP
IngressRoutes: []*unstructured.Unstructured{
traefikRoute(g, "IngressRouteTCP", "app", "r", nil, []map[string]any{{"name": "mw"}}),
},
}
if n := findingIDs(checkTraefikDanglingRefs(newEvalTracker(), input))["traefikRouteMissingMiddleware"]; n != 0 {
t.Errorf("MiddlewareTCP kind not authoritative → must skip, got %d (regression: CR-3)", n)
}
// Now MiddlewareTCP IS authoritative and the ref is absent → flag.
input.TraefikAuthoritativeKinds = authoritative(g + "\x00MiddlewareTCP")
if n := findingIDs(checkTraefikDanglingRefs(newEvalTracker(), input))["traefikRouteMissingMiddleware"]; n != 1 {
t.Errorf("authoritative MiddlewareTCP with absent ref → want 1, got %d", n)
}
})
t.Run("service check independent of middleware authority", func(t *testing.T) {
// Middlewares not authoritative, but services are listed → service ref
// still checked (per-kind independence; CR-5).
input := &CheckInput{
AllServices: []*corev1.Service{}, // non-nil, empty → listed
TraefikAuthoritativeKinds: authoritative(),
IngressRoutes: []*unstructured.Unstructured{
traefikRoute(g, "IngressRoute", "app", "r",
[]map[string]any{{"name": "missing"}}, []map[string]any{{"name": "ghost"}}),
},
}
ids := findingIDs(checkTraefikDanglingRefs(newEvalTracker(), input))
if ids["traefikRouteMissingService"] != 1 {
t.Errorf("service ref should be checked independently, got %d", ids["traefikRouteMissingService"])
}
if ids["traefikRouteMissingMiddleware"] != 0 {
t.Errorf("middleware not authoritative → skip, got %d", ids["traefikRouteMissingMiddleware"])
}
})
t.Run("findings carry their exact group", func(t *testing.T) {
input := &CheckInput{
AllServices: []*corev1.Service{},
TraefikAuthoritativeKinds: authoritative(),
IngressRoutes: []*unstructured.Unstructured{
traefikRoute(g, "IngressRoute", "app", "r", []map[string]any{{"name": "missing"}}, nil),
},
}
got := checkTraefikDanglingRefs(newEvalTracker(), input)
if len(got) != 1 {
t.Fatalf("want 1 finding, got %d", len(got))
}
if got[0].Group != g {
t.Errorf("Traefik finding lost its group, got %q", got[0].Group)
}
})
t.Run("no Traefik installed → no-op", func(t *testing.T) {
if got := checkTraefikDanglingRefs(newEvalTracker(), &CheckInput{}); got != nil {
t.Errorf("want nil, got %v", got)
}
})
}
func traefikChainMw(group, ns, name string, chainRefs []map[string]any) *unstructured.Unstructured {
return &unstructured.Unstructured{Object: map[string]any{
"apiVersion": group + "/v1alpha1",
"kind": "Middleware",
"metadata": map[string]any{"name": name, "namespace": ns, "uid": group + "/chain/" + ns + "/" + name},
"spec": map[string]any{"chain": map[string]any{"middlewares": toIfaceSlice(chainRefs)}},
}}
}
func traefikErrorsMw(group, ns, name string, svcRef map[string]any) *unstructured.Unstructured {
return &unstructured.Unstructured{Object: map[string]any{
"apiVersion": group + "/v1alpha1",
"kind": "Middleware",
"metadata": map[string]any{"name": name, "namespace": ns, "uid": group + "/errors/" + ns + "/" + name},
"spec": map[string]any{"errors": map[string]any{"service": svcRef}},
}}
}
func TestCheckTraefikMiddlewareSubjectRefs(t *testing.T) {
const g = "traefik.io"
t.Run("chain flags missing middleware, accepts present", func(t *testing.T) {
input := &CheckInput{
Middlewares: []*unstructured.Unstructured{traefikObj(g, "Middleware", "app", "present")},
TraefikAuthoritativeKinds: authoritative(g + "\x00Middleware"),
MiddlewareSubjects: []*unstructured.Unstructured{
traefikChainMw(g, "app", "c", []map[string]any{{"name": "present"}, {"name": "missing"}}),
},
}
if n := findingIDs(checkTraefikDanglingRefs(newEvalTracker(), input))["traefikChainMissingMiddleware"]; n != 1 {
t.Errorf("want 1 missing chain middleware, got %d", n)
}
})
t.Run("chain cross-namespace ref resolves against cluster-wide inventory", func(t *testing.T) {
input := &CheckInput{
Middlewares: []*unstructured.Unstructured{traefikObj(g, "Middleware", "shared", "auth")},
TraefikAuthoritativeKinds: authoritative(g + "\x00Middleware"),
MiddlewareSubjects: []*unstructured.Unstructured{
traefikChainMw(g, "app", "c", []map[string]any{{"name": "auth", "namespace": "shared"}}),
},
}
if n := findingIDs(checkTraefikDanglingRefs(newEvalTracker(), input))["traefikChainMissingMiddleware"]; n != 0 {
t.Errorf("cross-namespace ref to an existing middleware must not be flagged, got %d", n)
}
})
t.Run("chain not authoritative → skip (no false positive)", func(t *testing.T) {
input := &CheckInput{
TraefikAuthoritativeKinds: authoritative(), // nothing authoritative
MiddlewareSubjects: []*unstructured.Unstructured{
traefikChainMw(g, "app", "c", []map[string]any{{"name": "ghost"}}),
},
}
if n := findingIDs(checkTraefikDanglingRefs(newEvalTracker(), input))["traefikChainMissingMiddleware"]; n != 0 {
t.Errorf("non-authoritative middleware kind must not be asserted, got %d", n)
}
})
t.Run("errors flags missing service, accepts present", func(t *testing.T) {
input := &CheckInput{
AllServices: []*corev1.Service{svc("app", "present")},
MiddlewareSubjects: []*unstructured.Unstructured{
traefikErrorsMw(g, "app", "e-missing", map[string]any{"name": "missing"}),
traefikErrorsMw(g, "app", "e-present", map[string]any{"name": "present"}),
},
}
if n := findingIDs(checkTraefikDanglingRefs(newEvalTracker(), input))["traefikErrorsMissingService"]; n != 1 {
t.Errorf("want 1 missing errors service, got %d", n)
}
})
t.Run("errors.service kind=TraefikService resolves against TraefikService inventory", func(t *testing.T) {
input := &CheckInput{
AllServices: []*corev1.Service{}, // listed, empty
TraefikServices: []*unstructured.Unstructured{traefikObj(g, "TraefikService", "app", "weighted")},
TraefikAuthoritativeKinds: authoritative(g + "\x00TraefikService"),
MiddlewareSubjects: []*unstructured.Unstructured{
traefikErrorsMw(g, "app", "e", map[string]any{"name": "weighted", "kind": "TraefikService"}),
},
}
if n := findingIDs(checkTraefikDanglingRefs(newEvalTracker(), input))["traefikErrorsMissingService"]; n != 0 {
t.Errorf("present TraefikService ref must not be flagged, got %d", n)
}
})
t.Run("middleware-only namespace (no routes) still runs the checks", func(t *testing.T) {
// Regression guard: the early-return must not bail when there are only
// middleware subjects and zero IngressRoutes.
input := &CheckInput{
AllServices: []*corev1.Service{},
MiddlewareSubjects: []*unstructured.Unstructured{
traefikErrorsMw(g, "app", "e", map[string]any{"name": "missing"}),
},
}
got := checkTraefikDanglingRefs(newEvalTracker(), input)
if len(got) != 1 {
t.Fatalf("want 1 finding from a middleware-only namespace, got %d", len(got))
}
if got[0].Kind != "Middleware" || got[0].Group != g {
t.Errorf("finding must preserve the Middleware group, got kind=%q group=%q", got[0].Kind, got[0].Group)
}
})
}