-
Notifications
You must be signed in to change notification settings - Fork 0
feat(networking): add tenant agent isolation NetworkPolicy #11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
f0899f7
ded3a33
300d680
39bb184
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,2 +1,3 @@ | ||
| resources: | ||
| - allow-metrics-traffic.yaml | ||
| - tenant-agent-isolation.yaml |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| --- | ||
| # Tenant Agent Isolation NetworkPolicy | ||
| # | ||
| # Purpose: Restricts agent pods across tenant namespaces, enforcing zero-trust | ||
| # isolation between tenants and preventing unauthorized outbound traffic. | ||
| # | ||
| # Selector: Matches all pods labelled `agentrax.io/agent: "true"`. | ||
| # The AgentDeployment reconciler sets this label on every pod template | ||
| # it manages, so this policy applies to all managed agent pods. | ||
| # | ||
| # Ingress rules: | ||
| # - Allow Prometheus to scrape metrics on port 8080 from namespaces | ||
| # labelled `monitoring: enabled` (kube-prometheus-stack namespace). | ||
| # | ||
| # Egress rules: | ||
| # - Allow egress to kube-apiserver on port 6443 (required for agent-to-API | ||
| # communication and tool-calling via the Kubernetes API). | ||
| # - Allow CoreDNS lookups on port 53 (UDP and TCP) for service discovery | ||
| # within the cluster. | ||
| # - All other egress (internet, cross-tenant) is denied by default. | ||
| # | ||
| # Usage: Apply this manifest to every tenant namespace: | ||
| # kubectl apply -n tenant-<name> -f tenant-agent-isolation.yaml | ||
| # | ||
| # Note: This policy applies only to tenant-* namespaces (managed agent pods). | ||
| # The operator namespace (agentrax-system) is protected by allow-metrics-traffic.yaml (TCP 8443). | ||
| apiVersion: networking.k8s.io/v1 | ||
| kind: NetworkPolicy | ||
| metadata: | ||
| name: tenant-agent-isolation | ||
| labels: | ||
| app.kubernetes.io/name: agentrax | ||
| app.kubernetes.io/managed-by: kustomize | ||
| spec: | ||
| # Select all pods carrying the agentrax.io/agent=true label. | ||
| # This label is set by agentLabels() in the AgentDeployment reconciler. | ||
| podSelector: | ||
| matchLabels: | ||
| agentrax.io/agent: "true" | ||
| policyTypes: | ||
| - Ingress | ||
| - Egress | ||
| ingress: | ||
| # Allow Prometheus to scrape agent /metrics on port 8080. | ||
| # Prometheus Operator runs in a namespace labelled `monitoring: enabled`. | ||
| - from: | ||
| - namespaceSelector: | ||
| matchLabels: | ||
| monitoring: enabled | ||
| ports: | ||
| - port: 8080 | ||
| protocol: TCP | ||
| egress: | ||
| # Allow outbound to kube-apiserver via ClusterIP (port 443) and direct endpoint (port 6443). | ||
| # Agents may call the Kubernetes API to discover services or use cluster tools. | ||
| - ports: | ||
| - port: 443 | ||
| protocol: TCP | ||
| - port: 6443 | ||
| protocol: TCP | ||
| # Allow CoreDNS resolution on UDP and TCP port 53. | ||
| # Matches cluster DNS pods in the kube-system namespace. | ||
| - to: | ||
| - namespaceSelector: | ||
| matchLabels: | ||
| kubernetes.io/metadata.name: kube-system | ||
| podSelector: | ||
| matchExpressions: | ||
| - key: k8s-app | ||
| operator: In | ||
| values: ["kube-dns", "coredns"] | ||
| ports: | ||
| - port: 53 | ||
| protocol: UDP | ||
| - port: 53 | ||
| protocol: TCP | ||
|
Comment on lines
+53
to
+76
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- target policy ---'
cat -n config/network-policy/tenant-agent-isolation.yaml
printf '%s\n' '--- network-policy files ---'
git ls-files '*network-policy*' '*networkpolicy*' | sed -n '1,160p'
printf '%s\n' '--- relevant selectors and endpoint references ---'
rg -n -i --glob '*.yaml' --glob '*.yml' \
'tenant-agent-isolation|NetworkPolicy|kube-apiserver|kubernetes\.default|kube-system|coredns|dns|egress:|ingress:' . \
| sed -n '1,260p'Repository: gitcommitankit/agentrax Length of output: 6318 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- API and DNS references across tracked files ---'
rg -n -i \
'kubernetes\.default\.svc|kube-apiserver|api[-_ ]server|coredns|kube-dns|:443|port:[[:space:]]*443|port:[[:space:]]*6443|port:[[:space:]]*53|networkplugin|cni' \
$(git ls-files) 2>/dev/null | sed -n '1,320p'
printf '%s\n' '--- related policy ---'
cat -n config/network-policy/allow-metrics-traffic.yamlRepository: gitcommitankit/agentrax Length of output: 4475 🌐 Web query:
💡 Result: In Kubernetes NetworkPolicy, the ports field is optional [1][2]. If you omit the ports field in an egress rule (or an ingress rule), it signifies that the rule matches all ports, meaning the traffic is not restricted by port [1][2][3]. When no ports are specified, the policy allows traffic on any port to the destination(s) defined in the to block [4]. Conversely, if you include the ports field and provide at least one item, the policy restricts traffic to only those specific ports [1][2]. It is important to distinguish between omitting the field entirely and providing an empty object (e.g., ports: - {}). Omitting the field allows all ports [1][4], while providing an empty object in the list can result in the rule matching no ports at all, effectively blocking traffic [5]. Key points regarding the egress rule configuration: - The rule combines the to and ports sections: traffic must match both the defined destination(s) and the allowed port(s) [6][2]. - If ports is missing, all ports are allowed to the defined destination(s) [1][4]. - If to is also missing or empty within a rule, the rule matches all destinations, effectively allowing all traffic on all ports if ports is also missing [2]. Official documentation and API references explicitly state that when the ports field is empty or missing, the rule matches all ports [1][2]. Citations:
🌐 Web query:
💡 Result: In Kubernetes, the kube-apiserver typically listens on port 6443 [1][2]. The default Citations:
🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- networking documentation ---'
cat -n docs/networking/README.md | sed -n '35,145p'
printf '%s\n' '--- focused manifest verifier ---'
python3 - <<'PY'
from pathlib import Path
import re
policy = Path("config/network-policy/tenant-agent-isolation.yaml").read_text()
egress = policy.split("\n egress:\n", 1)[1].split("\n", 1)[0] if "\n egress:\n" in policy else ""
if not egress:
raise SystemExit("egress section not found")
# Inspect each list item in the egress section up to the next top-level spec field.
section = policy.split("\n egress:\n", 1)[1]
section = section.split("\n ", 1)[0] if False else section
items = re.split(r"(?m)^\s{4}- ", section)
items = [item for item in items[1:] if item.strip()]
for index, item in enumerate(items, 1):
ports = [int(value) for value in re.findall(r"(?m)^\s{10}- port: (\d+)", item)]
has_to = bool(re.search(r"(?m)^\s{6}to:", item))
print(f"egress rule {index}: to={has_to}, ports={ports}")
if has_to or not ports:
raise SystemExit("unexpected egress rule shape")
doc = Path("docs/networking/README.md").read_text()
api_urls = re.findall(r"https://kubernetes\.default\.svc:(\d+)", doc)
policy_api_ports = [int(value) for value in re.findall(r"(?m)^\s{10}- port: (\d+)", section)]
print(f"documented API Service ports={api_urls}")
print(f"policy egress ports={policy_api_ports}")
if "443" not in api_urls:
raise SystemExit("documented API Service port 443 not found")
if 6443 not in policy_api_ports:
raise SystemExit("policy port 6443 not found")
PYRepository: gitcommitankit/agentrax Length of output: 4657 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- networking documentation ---'
cat -n docs/networking/README.md | sed -n '35,145p'
printf '%s\n' '--- focused manifest verifier ---'
python3 - <<'PY'
from pathlib import Path
import re
policy = Path("config/network-policy/tenant-agent-isolation.yaml").read_text()
section = policy.split("\n egress:\n", 1)[1]
items = re.split(r"(?m)^\s{4}- ", section)
items = [item for item in items[1:] if item.strip()]
for index, item in enumerate(items, 1):
ports = [int(value) for value in re.findall(r"(?m)^\s{10}- port: (\d+)", item)]
has_to = bool(re.search(r"(?m)^\s{6}to:", item))
print(f"egress rule {index}: to={has_to}, ports={ports}")
if has_to or not ports:
raise SystemExit("unexpected egress rule shape")
doc = Path("docs/networking/README.md").read_text()
api_urls = re.findall(r"https://kubernetes\.default\.svc:(\d+)", doc)
policy_api_ports = [int(value) for value in re.findall(r"(?m)^\s{10}- port: (\d+)", section)]
print(f"documented API Service ports={api_urls}")
print(f"policy egress ports={policy_api_ports}")
if "443" not in api_urls:
raise SystemExit("documented API Service port 443 not found")
if 6443 not in policy_api_ports:
raise SystemExit("policy port 6443 not found")
PYRepository: gitcommitankit/agentrax Length of output: 4657 🏁 Script executed: #!/bin/bash
set -eu
python3 - <<'PY'
from pathlib import Path
import re
policy_lines = Path("config/network-policy/tenant-agent-isolation.yaml").read_text().splitlines()
start = next(i for i, line in enumerate(policy_lines) if line == " egress:")
end = next(
(i for i in range(start + 1, len(policy_lines))
if re.match(r"^ [A-Za-z][A-Za-z0-9_-]*:", policy_lines[i])),
len(policy_lines),
)
egress_lines = policy_lines[start + 1:end]
rules = []
current = None
for line in egress_lines:
if re.match(r"^ - ", line):
current = {"to": False, "ports": []}
rules.append(current)
elif current is not None:
if re.match(r"^ to:", line):
current["to"] = True
match = re.match(r"^ - port: (\d+)$", line)
if match:
current["ports"].append(int(match.group(1)))
for index, rule in enumerate(rules, 1):
print(f"egress rule {index}: to={rule['to']}, ports={rule['ports']}")
assert not rule["to"]
assert rule["ports"]
doc = Path("docs/networking/README.md").read_text()
api_ports = re.findall(r"kubernetes\.default\.svc:(\d+)", doc)
print(f"documented API Service ports={api_ports}")
assert "443" in api_ports
assert any(6443 in rule["ports"] for rule in rules)
PYRepository: gitcommitankit/agentrax Length of output: 284 🏁 Script executed: #!/bin/bash
set -eu
python3 - <<'PY'
from pathlib import Path
import re
lines = Path("config/network-policy/tenant-agent-isolation.yaml").read_text().splitlines()
start = lines.index(" egress:") + 1
rules = []
current = None
for line in lines[start:]:
if line and len(line) - len(line.lstrip()) == 2 and re.match(r"[A-Za-z][A-Za-z0-9_-]*:", line.strip()):
break
indent = len(line) - len(line.lstrip())
if indent == 4 and line.lstrip().startswith("- "):
current = {"to": False, "ports": []}
rules.append(current)
elif current is not None:
if indent == 6 and line.strip() == "to:":
current["to"] = True
match = re.fullmatch(r"\s*- port: (\d+)", line)
if match:
current["ports"].append(int(match.group(1)))
for index, rule in enumerate(rules, 1):
print(f"egress rule {index}: to={rule['to']}, ports={rule['ports']}")
assert rule["to"] is False
assert rule["ports"]
doc = Path("docs/networking/README.md").read_text()
api_ports = re.findall(r"kubernetes\.default\.svc:(\d+)", doc)
print(f"documented API Service ports={api_ports}")
assert "443" in api_ports
assert any(6443 in rule["ports"] for rule in rules)
PYRepository: gitcommitankit/agentrax Length of output: 276 Constrain egress destinations and align the API port. Both egress rules omit 🤖 Prompt for AI Agents |
||
Uh oh!
There was an error while loading. Please reload this page.