Network policies
Audience
PLANA staff working on cluster security or troubleshooting pod-to-pod connectivity.
PLANA uses Kubernetes NetworkPolicies to enforce that pods can only reach the network destinations they actually need. The default posture is deny everything; explicit allow rules open exactly the flows the application requires.
The default-deny baseline
Every product namespace has a default-deny NetworkPolicy applied at namespace creation:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: <product-namespace>
spec:
podSelector: {}
policyTypes: [Ingress, Egress]This blocks all ingress and egress traffic to/from every pod in the namespace. Allow rules added on top open specific destinations.
What each namespace can reach
The allow-list is intentional and minimal. The pattern is to list the specific service-port destinations needed.
Tenant Odoo namespaces (plana-odoo*)
| Egress destination | Reason |
|---|---|
pg01.planapulse.com:5432 / :6432 | PostgreSQL (and pgbouncer) |
redis.redis:6379 | Sessions, bus, queues |
nfs01.planapulse.com:2049 | NFS filestore |
auth.planapulse.com:443 | OIDC discovery + token exchange |
kube-dns:53 | DNS resolution |
| Egress to internet on 443 | For Anthropic API calls (AI agents), Stripe webhooks |
| Ingress source | Reason |
|---|---|
envoy-gateway-system | All public traffic comes via Envoy |
crossplane-system | Composition Jobs need to run against workers |
pulse-account (account portal + BOS frontend + API)
| Egress | Reason |
|---|---|
pg01:5432 | plana_pulse_account DB |
redis.redis:6379 | Session blocklist, cache |
pulse-banking.pulse-banking:3200 | PSD2 proxy |
ai-agents.ai-bos-agent:8000 | Agent chat proxy |
pulse-events.pulse-events:3001 | Event bus |
Tenant Odoo *.planapulse.app:443 | XML-RPC to tenant ERPs |
ai-bos-agent
| Egress | Reason |
|---|---|
Tenant Odoo *.planapulse.app:443 | JSON-RPC tool calls |
pulse-data.pulse-data:8000 | Neural Business Network |
api.anthropic.com:443 | LLM inference |
openrouter.ai:443 | Fallback LLM |
Tools namespaces
Each tool (Forgejo, Matrix, Matomo, etc.) has its own narrow allow list, typically:
- PostgreSQL for its own DB
- Redis where applicable
- Object storage where applicable (Matomo to SOS for archives, etc.)
- Authentik on 443 for OIDC
Cross-namespace traffic
To allow pod A in namespace X to reach service B in namespace Y, two things must align:
- Egress allowed from X to Y/B in X's NetworkPolicy
- Ingress allowed from X to Y/B in Y's NetworkPolicy
Both must agree. Missing either side blocks the connection silently (it just times out).
Default-allow for the cluster control plane
Some flows are allowed implicitly by Calico:
- Pod → kube-apiserver (required for in-cluster API calls)
- Pod → metadata service (locked-down by Exoscale, no IMDS leak)
- DNS resolution to kube-dns
These don't require explicit NetworkPolicy entries.
Source of truth
All NetworkPolicies live in infra/k8s/network-policies/ and are reconciled by the network-policies Flux Kustomization.
Two patterns in the directory:
infra/k8s/network-policies/cluster-wide.yaml— policies applied vianamespaceSelectorto many namespaces at onceinfra/k8s/<namespace>/network-policy.yaml— namespace-specific policies
Adding a new flow
When a service needs a new destination:
- Identify the source pod label and destination service:port
- Add an egress entry to the source namespace's NetworkPolicy
- Add an ingress entry to the destination namespace's NetworkPolicy
- PR to
infrarepo, merge, Flux reconciles within ~60 seconds - Verify with
kubectl execfrom the source pod:nc -zv <dest-host> <port>
Common diagnostic: connection times out
Symptom: a service-to-service call hangs and eventually times out.
Diagnosis:
# From inside the source pod
nc -zv <dest-host> <dest-port>
# Check Calico's pod policy log
kubectl logs -n calico-system -l k8s-app=calico-node --tail=200 \
| grep "DROP" | grep -i <source-or-dest-name>If Calico reports DROP, the NetworkPolicy is missing a rule. Add the flow per "Adding a new flow" above.
Where to read more
- Threat model — what NetworkPolicies defend against
- Architecture → Tenant isolation
- Operations → Flux GitOps — how the policies reach the cluster
- Source:
infra/k8s/network-policies/