net-kingdom/tools/contract-tests/tests/test_ingress.py
tegwick 80d280a340
All checks were successful
Bounded-execution contracts / contracts (push) Successful in 1m43s
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s
NK-WP-0058-T02 done: per-row contract tests (E7, E9, C4, C5) and the coverage table
Ingress: each owner's NetworkPolicies admit the executor pod (small evaluator, synthetic cases).
Custody names: KeyCape client secret (C4) and audit sender (C5) agree across net-kingdom,
railiance-platform and audit-core. E7: one LLDAP digest for the executor tool and the server.
95 tests pass in a CI-like layout.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 943911@bnt-lap001
Assistant-Session: fc322940-1c8e-4bed-ab39-86cec99f23a0
2026-11-06 00:23:52 +01:00

103 lines
5.6 KiB
Python

"""Row E9: every owner's ingress admits the executor pod (NK-WP-0058-T02), read from the owners' source.
Kubernetes semantics, for the subset these policies use: a pod that no Ingress policy selects is open; once any
policy selects it, traffic must match an ingress rule of one of them (peer selectors and port). matchExpressions and
ipBlock peers are not used by these policies; a policy that uses them fails the test instead of being guessed.
Not covered here: OpenBao (rapp-openbao is private) and the executor's egress (the rapp's own CI, row E9 there).
"""
from pathlib import Path
import pytest
import yaml
REPO = Path(__file__).resolve().parents[3]
SIBLINGS = REPO.parent
EXECUTOR = {'namespace': 'sso', 'labels': {'app.kubernetes.io/name': 'bounded-executor'}}
SSO = 'sso-mfa/k8s/network-policies/netpol-sso.yaml'
# (owner, policy files relative to the owner, namespace, target pod labels, port), from the executor's config
TARGETS = [
('approval-engine', ['deploy/networkpolicies.yaml'], 'approval-engine',
{'app.kubernetes.io/name': 'approval-engine'}, 8080),
('audit-core', ['deploy/networkpolicies.yaml'], 'audit-core', {'app.kubernetes.io/name': 'audit-core'}, 8080),
('informed-decision', ['deploy/lifecycle/*.yaml'], 'informed-decision',
{'app.kubernetes.io/name': 'informed-decision', 'app.kubernetes.io/component': 'review'}, 8081),
] + [('net-kingdom', [SSO], 'sso', {'app.kubernetes.io/name': name}, port)
for name, port in (('lldap', 17170), ('keycape', 8080), ('authelia', 9091))]
def selects(selector, labels):
if set(selector) - {'matchLabels'}:
raise AssertionError(f'unsupported selector {sorted(selector)}: extend the evaluator, do not guess')
return all(labels.get(k) == v for k, v in (selector.get('matchLabels') or {}).items())
def peer_admits(peer, policy_ns, source):
if set(peer) - {'namespaceSelector', 'podSelector'}:
raise AssertionError(f'unsupported peer {sorted(peer)}')
if 'namespaceSelector' in peer:
ns_ok = selects(peer['namespaceSelector'], {'kubernetes.io/metadata.name': source['namespace']})
else:
ns_ok = source['namespace'] == policy_ns
return ns_ok and selects(peer.get('podSelector') or {}, source['labels'])
def port_admits(ports, port):
return not ports or any(p.get('port') == port and p.get('protocol', 'TCP') == 'TCP' for p in ports)
def admitted(policies, namespace, labels, port, source=EXECUTOR):
applying = [p for p in policies if (p['metadata'].get('namespace') or namespace) == namespace
and 'Ingress' in p['spec'].get('policyTypes', ['Ingress'])
and selects(p['spec'].get('podSelector') or {}, labels)]
if not applying:
return 'open'
for policy in applying:
for rule in policy['spec'].get('ingress') or []:
peers = rule.get('from')
if (not peers or any(peer_admits(peer, namespace, source) for peer in peers)) \
and port_admits(rule.get('ports'), port):
return 'admitted:' + policy['metadata']['name']
return 'denied'
def policies_of(owner, patterns):
root = REPO if owner == 'net-kingdom' else SIBLINGS / owner
if not root.is_dir():
pytest.skip(f'owner checkout missing: {owner}')
out = []
for pattern in patterns:
for path in sorted(root.glob(pattern)):
out += [d for d in yaml.safe_load_all(path.read_text()) if d and d.get('kind') == 'NetworkPolicy']
return out
@pytest.mark.parametrize('owner, files, namespace, labels, port', TARGETS,
ids=[f"{t[0]}:{t[3]['app.kubernetes.io/name']}:{t[4]}" for t in TARGETS])
def test_owner_ingress_admits_the_executor(owner, files, namespace, labels, port):
result = admitted(policies_of(owner, files), namespace, labels, port)
assert result != 'denied', f'{owner} ingress does not admit sso/bounded-executor on {port}'
def policy(name, namespace, target, peers=None, ports=None):
rule = {**({'from': peers} if peers is not None else {}), **({'ports': ports} if ports else {})}
return {'kind': 'NetworkPolicy', 'metadata': {'name': name, 'namespace': namespace},
'spec': {'podSelector': {'matchLabels': target}, 'policyTypes': ['Ingress'], 'ingress': [rule]}}
def test_the_evaluator_on_synthetic_policies():
target, ns = {'app.kubernetes.io/name': 'svc'}, 'svc'
executor_peer = {'namespaceSelector': {'matchLabels': {'kubernetes.io/metadata.name': 'sso'}},
'podSelector': {'matchLabels': {'app.kubernetes.io/name': 'bounded-executor'}}}
other_peer = {'namespaceSelector': {'matchLabels': {'kubernetes.io/metadata.name': 'other'}}}
assert admitted([], ns, target, 8080) == 'open'
assert admitted([policy('a', ns, target, [executor_peer], [{'port': 8080}])], ns, target, 8080) == 'admitted:a'
assert admitted([policy('a', ns, target, [executor_peer], [{'port': 9090}])], ns, target, 8080) == 'denied'
assert admitted([policy('a', ns, target, [other_peer])], ns, target, 8080) == 'denied'
assert admitted([policy('a', ns, {'app.kubernetes.io/name': 'else'}, [other_peer])], ns, target, 8080) == 'open'
deny_all = {'kind': 'NetworkPolicy', 'metadata': {'name': 'deny', 'namespace': ns},
'spec': {'podSelector': {}, 'policyTypes': ['Ingress']}}
assert admitted([deny_all], ns, target, 8080) == 'denied'
same_ns_peer = {'podSelector': {'matchLabels': {'app.kubernetes.io/name': 'bounded-executor'}}}
assert admitted([deny_all, policy('b', 'sso', target, [same_ns_peer])], 'sso', target, 8080) == 'admitted:b'
assert admitted([deny_all, policy('b', ns, target, [same_ns_peer])], ns, target, 8080) == 'denied'