Documentation
¶
Overview ¶
Package netpolicy is the engine module for Phase 6 (CAPABILITY_SPEC domain 6): network egress analysis and least-privilege policy generation. It is a thin adapter — the flow model and detection heuristics live in internal/netmon; this package loads a recorded capture from a Target, runs netmon over it, and projects the resulting anomalies and posture observations onto engine.Finding.
It also *generates* least-privilege artifacts from an observed baseline: a Kubernetes NetworkPolicy, a companion DNS/FQDN egress allowlist, and a default-deny egress policy. Generation is advisory by design — the module and the `dsecrat net` command emit policy for review or agent-application, never enforce it. That keeps us an observation-and-recommendation layer, not a dataplane, exactly as the phase handoff requires.
Two AI-age features ride on top, both OFF by default (opt in via Target metadata or CLI flags): egress intent modelling (intended-vs-anomalous destinations with a rationale an agent can approve) and agent egress governance (watching AI-agent workloads for egress to unknown model hosts).
Index ¶
- func Command(args []string) int
- func Register(r *engine.Registry)
- func RenderFQDNAllowlist(gp GeneratedPolicy) string
- func RenderNetworkPolicy(p NetworkPolicy) string
- type Allowlist
- type DeniedFlow
- type DryRunResult
- type EgressPeer
- type FQDNEntry
- type GenOptions
- type GeneratedPolicy
- type Module
- type NetworkPolicy
- type PolicyDiff
- type Port
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Command ¶
Command runs `dsecrat net <capture.json> [flags]`. Exit codes: 0 ok, 1 error or --fail-on threshold met, 2 usage error.
func Register ¶
Register adds the netpolicy module to the registry. The master agent calls this from modules.Default() during integration, so this package never edits the shared registry file (parallel-safe wiring — see SHARED_CONTRACT §2).
func RenderFQDNAllowlist ¶
func RenderFQDNAllowlist(gp GeneratedPolicy) string
RenderFQDNAllowlist renders the DNS/FQDN egress allowlist as an advisory YAML document. Standard NetworkPolicy cannot express FQDN egress, so this is a portable artifact for an FQDN-aware CNI (Cilium toFQDNs, Calico DNS policy). Each entry carries its intent class and rationale as comments so a reviewer or agent can see why the domain is permitted.
func RenderNetworkPolicy ¶
func RenderNetworkPolicy(p NetworkPolicy) string
RenderNetworkPolicy renders a NetworkPolicy as a valid Kubernetes manifest. The default-deny form emits an empty egress list (which Kubernetes interprets as "deny all egress" for the selected pods).
Types ¶
type Allowlist ¶
type Allowlist struct {
// FQDNs are permitted destination names; a leading "." or bare domain matches
// subdomains too (".example.com" and "example.com" both match "a.example.com").
FQDNs []string `json:"fqdns,omitempty"`
// CIDRs are permitted destination IP ranges.
CIDRs []string `json:"cidrs,omitempty"`
// Ports restricts allowed destination ports; empty means any port.
Ports []int `json:"ports,omitempty"`
// AllowInternal permits all private-range (east-west) egress.
AllowInternal bool `json:"allow_internal,omitempty"`
// AllowDNS permits UDP/TCP 53 to any resolver (a pod must resolve names).
AllowDNS bool `json:"allow_dns,omitempty"`
}
Allowlist is a candidate egress policy expressed as matchable rules. It is the JSON shape the `--dry-run` flag consumes and the shape AllowlistFromPolicy produces from a generated policy.
func AllowlistFromPolicy ¶
func AllowlistFromPolicy(gp GeneratedPolicy) Allowlist
AllowlistFromPolicy derives the matchable allowlist implied by a generated policy: its FQDN entries, its ipBlock CIDRs, and DNS egress. Used to prove a generated policy against its own baseline.
type DeniedFlow ¶
type DeniedFlow struct {
Workload string `json:"workload"`
Dest string `json:"dest"`
IP string `json:"ip,omitempty"`
Port uint16 `json:"port"`
Count int `json:"count"`
Reason string `json:"reason"`
}
DeniedFlow is one destination a candidate policy would drop, aggregated over the connections to it.
type DryRunResult ¶
type DryRunResult struct {
AllowedDests int `json:"allowed_dests"`
DeniedDests int `json:"denied_dests"`
Denied []DeniedFlow `json:"denied"`
}
DryRunResult is the outcome of auditing a capture against a candidate policy.
type EgressPeer ¶
type EgressPeer struct {
CIDR string `json:"cidr,omitempty"`
NamespaceLabels map[string]string `json:"namespace_labels,omitempty"`
PodLabels map[string]string `json:"pod_labels,omitempty"`
Ports []Port `json:"ports,omitempty"`
}
EgressPeer is one allowed destination in a generated NetworkPolicy: either an ipBlock CIDR (external IP) or a namespace/pod label selector (internal peer).
type FQDNEntry ¶
type FQDNEntry struct {
FQDN string `json:"fqdn"`
Ports []Port `json:"ports,omitempty"`
Class string `json:"class,omitempty"` // intended | anomalous | observed
Rationale []string `json:"rationale,omitempty"` // human+machine reasons
}
FQDNEntry is one domain on the DNS/FQDN egress allowlist, with the class and rationale from intent modelling so a reviewer/agent sees *why* it is allowed.
type GenOptions ¶
type GenOptions struct {
Namespace string
// UseIntent, when set, restricts the allowlist to intended destinations and
// records anomalous ones under Excluded. Off by default (mirrors netmon).
UseIntent bool
// Opts carries the netmon thresholds used for intent classification.
Opts netmon.Options
}
GenOptions tunes generation.
type GeneratedPolicy ¶
type GeneratedPolicy struct {
Workload string `json:"workload"`
Namespace string `json:"namespace"`
Policy NetworkPolicy `json:"policy"`
DefaultDeny NetworkPolicy `json:"default_deny"`
FQDNAllowlist []FQDNEntry `json:"fqdn_allowlist"`
// Excluded lists destinations left OFF the allowlist because intent modelling
// classed them anomalous — surfaced so the omission is explicit, not silent.
Excluded []FQDNEntry `json:"excluded,omitempty"`
}
GeneratedPolicy bundles the artifacts produced for one workload.
func GenerateForCapture ¶
func GenerateForCapture(c *netmon.Capture, g GenOptions) []GeneratedPolicy
GenerateForCapture generates artifacts for every workload with egress in the capture, in stable workload order.
func GeneratePolicy ¶
func GeneratePolicy(fl *netmon.FlowLog, g GenOptions) GeneratedPolicy
GeneratePolicy synthesises the least-privilege artifacts for one workload's observed flow log.
type Module ¶
type Module struct{}
Module is the network egress-analysis capability.
func (*Module) Analyze ¶
Analyze loads the capture from the target, runs netmon detection, and projects anomalies plus network-posture observations onto findings. When the target is not a network capture it returns nothing so generic scans stay silent.
func (*Module) Description ¶
func (*Module) Supports ¶
func (m *Module) Supports(t engine.TargetType) bool
Supports handles filesystem targets: the carrier for a recorded network capture (JSON). There is no dedicated "flow capture" target type yet — see NOTES.md for the proposed engine change. Until then a filesystem target whose content is a capture is analysed; anything that is not a capture yields no findings, so ordinary filesystem scans are unaffected.
type NetworkPolicy ¶
type NetworkPolicy struct {
Name string `json:"name"`
Namespace string `json:"namespace"`
PodSelector map[string]string `json:"pod_selector"`
Egress []EgressPeer `json:"egress,omitempty"`
DefaultDeny bool `json:"default_deny"`
}
NetworkPolicy is a minimal, renderable Kubernetes NetworkPolicy. DefaultDeny marks the empty-egress "deny all" form.
type PolicyDiff ¶
type PolicyDiff struct {
Workload string `json:"workload,omitempty"`
AddedFQDNs []string `json:"added_fqdns,omitempty"`
RemovedFQDNs []string `json:"removed_fqdns,omitempty"`
AddedCIDRs []string `json:"added_cidrs,omitempty"`
RemovedCIDRs []string `json:"removed_cidrs,omitempty"`
}
PolicyDiff is the delta between a current and a generated allowlist. It is deliberately set-based (not a text diff) so it is stable and machine-appliable.
func DiffAllowlists ¶
func DiffAllowlists(current, generated Allowlist) PolicyDiff
DiffAllowlists computes generated-minus-current: entries the generated policy adds (present in generated, absent in current) and removes (the reverse).
func (PolicyDiff) Empty ¶
func (d PolicyDiff) Empty() bool
Empty reports whether the generated policy matches the current one exactly — the "nothing to do" signal an agent uses to skip opening a change.
func (PolicyDiff) Render ¶
func (d PolicyDiff) Render() string
Render produces a human-readable, review-ready diff (+ widens, - tightens).