netpol: NetworkPolicy v1 enforcement via nftables
Build flock Image / build (push) Has been cancelled
Build flock Image / build (push) Has been cancelled
New pkg/agent/netpol implementing standard networking.k8s.io/v1 NetworkPolicy. Pipeline: pods + policies + namespaces → Translate → Render → Apply Supports ingress + egress, all three peer types (podSelector, namespaceSelector, ipBlock with except), numeric ports + port ranges, default-deny semantics derived from PolicyTypes (or inferred from non-empty Spec.Egress when unset). Apply path is `nft -f -` shell-out — single transaction, atomic, kernel guarantees partial-failure rollback. Idempotent dedup via last-applied script. Reconcile triggers: informer events, 30s self-heal tick, every CNI ADD/DEL. Verified against the three live cluster NetPols (calico-apiserver, remote-proxies/lodge-home-assistant, storage/garage-admin-restrict). Fuzz target stitches Translate + Render with random selector and peer inputs; 21 unit tests cover the policy semantics. Named ports skip with a warn — deferred until kubelet exposes them in a form that doesn't require shadowing pod state. Dockerfile: + nftables. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"code.fritzlab.net/fritzlab/flock/pkg/agent/netpol"
|
||||
)
|
||||
|
||||
// collectLocalPods bridges the agent's allocation store + pod informer
|
||||
// cache into the netpol-package input shape. It returns one Pod per
|
||||
// committed allocation that has a matching pod in the informer cache;
|
||||
// allocations whose pod was just deleted (DEL race) are skipped.
|
||||
//
|
||||
// Called on every netpol reconcile pass, so it must be cheap. The work
|
||||
// here is O(allocations) and reads from in-memory maps only.
|
||||
func collectLocalPods(store *Store, pods *PodCache) []netpol.Pod {
|
||||
allocs := store.Snapshot()
|
||||
out := make([]netpol.Pod, 0, len(allocs))
|
||||
for _, a := range allocs {
|
||||
if a.State != StateCommitted {
|
||||
continue
|
||||
}
|
||||
pod, ok := pods.Get(a.Namespace, a.PodName)
|
||||
if !ok {
|
||||
// Pod evicted but DEL hasn't fired yet; nothing to enforce.
|
||||
continue
|
||||
}
|
||||
ips := allocationIPs(a)
|
||||
if len(ips) == 0 {
|
||||
continue
|
||||
}
|
||||
out = append(out, netpol.Pod{
|
||||
Namespace: a.Namespace,
|
||||
Name: a.PodName,
|
||||
Labels: pod.Labels,
|
||||
HostIface: HostIfaceName(a.ContainerID),
|
||||
IPs: ips,
|
||||
})
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func allocationIPs(a Allocation) []net.IP {
|
||||
var out []net.IP
|
||||
if a.IP6 != "" {
|
||||
if ip := net.ParseIP(a.IP6); ip != nil {
|
||||
out = append(out, ip)
|
||||
}
|
||||
}
|
||||
if a.IP4 != "" {
|
||||
if ip := net.ParseIP(a.IP4); ip != nil {
|
||||
out = append(out, ip)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
Reference in New Issue
Block a user