Skip to content

feat(rules): symmetric R0011 egress / R0012 ingress, port-aware, no IP-class gate - #94

Open
entlein wants to merge 5 commits into
mainfrom
feat/rules-internal-egress
Open

feat(rules): symmetric R0011 egress / R0012 ingress, port-aware, no IP-class gate#94
entlein wants to merge 5 commits into
mainfrom
feat/rules-internal-egress

Conversation

@entlein

@entlein entlein commented Aug 24, 2026

Copy link
Copy Markdown

Rules-only PR: the single place the R0011/R0012 semantics land, consuming the engines from #85 (ports), #90 (peer selectors), #93 (serviceRef resolution).

Expected CT fallout: tests whose profiles omit kube-dns/apiserver internal egress will alert — the allowlist pressure is the feature; fixes belong in profiles/fixtures, not rule scope.

🤖 Generated with Claude Code

https://claude.ai/code/session_01A8UV2B7b6dpDJQci31aC6c

entlein and others added 5 commits August 24, 2026 18:58
R0011 keeps its external-only scope (!is_private_ip); internal traffic gets its
own rule instead of widening R0011 — rewriting R0011's scope broke Test_21/28
(kube-dns FPs) when tried in the fork CT.

R0012 alerts on OUTGOING to private addresses (loopback excluded — is_private_ip
counts 127.0.0.1/::1 as private) not allowlisted by the profile's egress
addresses, which includes serviceRef/serviceSelector-resolved entries. Uses the
port-aware matcher; behaves address-only until the port projection lands, then
becomes port-sensitive with no rules change. A selector clause
(was_selector_in_egress) is added one-line when the peer-selector fields merge.
Same defaults as R0011; uniqueId keyed on addr_port_proto; bound in the default
binding (new rule names are inert until bound).

Signed-off-by: tanzee <einentlein@gmail.com>
Per design review: R0011 (egress) and R0012 (ingress, new) are symmetric twins.
Neither uses is_private_ip — internal and external peers are treated alike, so
lateral movement to unlisted internal peers alerts; only loopback is excluded.
Allowlisting internal traffic is the profile's job (addresses, resolved
serviceRef/serviceSelector entries), not the rule's.

Both use the port-aware matcher (address-only until port projection lands).
On HOST (incoming) events the gadget's dstAddr/dstPort carry the remote peer
and local port. R0011's scope widens to internal egress: component tests whose
profiles do not list kube-dns et al. will alert until their profiles do —
that pressure is the feature.

Signed-off-by: tanzee <einentlein@gmail.com>
…eset

Deployable over any kubescape install to replace the stock rules; namespace
templated. A drift test pins the chart copy to the CI-validated test-chart
copy so the shipped semantics are always the tested ones.

Signed-off-by: tanzee <einentlein@gmail.com>
…ntegration branch)

Signed-off-by: tanzee <einentlein@gmail.com>
charts/kubescape-rules was a third copy of the ruleset: a verbatim duplicate of
tests/chart/templates/node-agent/default-rules.yaml, kept in step by a drift
test that existed only to guard the copy. No workflow published it, so nothing
consumed it as a chart.

The rules belong in kubescape/rulelibrary, which already ships R0011 and R0012
with per-rule tests and is the direction this repo already syncs FROM (see
"rules: sync severity + MITRE mapping from rulelibrary main"). node-agent keeps
its test-chart copy because CI has to pin the semantics it validates; it does
not need a distributable one as well.

The drift test goes with it — it asserted nothing once the file it compared
against is gone.

Neither path exists on main.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant