Skip to content

Network model

Every namespace inside a Kestrel tenant starts under a default-deny NetworkPolicy distributed by Capsule’s GlobalTenantResource. New namespaces inherit it the moment they are created — there is no window in which a fresh namespace is wide open.

Default-deny means exactly what it says: a Pod you create cannot receive traffic and cannot send traffic until an explicit allow rule permits it. This is the opposite of a default Kubernetes cluster, where Pods can reach each other freely until a NetworkPolicy narrows the traffic down.

On top of default-deny, the tenant chart distributes four pre-approved allow rules that are always in effect in every namespace your tenant owns. They cover the traffic everyone needs, so the floor is “nothing works” but the practical starting state is “the four things every workload needs work, and everything else is blocked until you write your own rule”.

  • DNS — Pods can egress to the cluster DNS service (kube-system/kube-dns) on UDP and TCP port 53. This is what makes in-cluster service discovery work, so a name like my-service.my-namespace resolves inside your pods without any extra rule.
  • Intra-tenant — Pods can talk to other Pods in the same tenant: any namespace carrying the capsule.clastix.io/tenant: <your-tenant> label is reachable from any other namespace in the same tenant. Cross-tenant pod-to-pod traffic is blocked.
  • Traefik ingress — The Traefik ingress controller pods (running in the traefik namespace) can reach your pods on whatever port your Ingress resource points at. This is what makes external HTTP and HTTPS routing work — Traefik sits at the edge, terminates TLS, and forwards to the backend Service you named.
  • Monitoring scrape — The cluster Prometheus (running in the monitoring namespace) can reach your pods on their metrics ports. This is what makes Grafana tenant dashboards work; you do not have to write an allow rule for the scrape path.

For traffic that does not match one of the four rules — for example, a Pod in your tenant that wants to talk to a Pod in a different tenant, a Pod that should accept traffic from a specific external CIDR, or a Pod that needs to reach an external endpoint such as an S3 bucket or a public API — you create your own NetworkPolicy in the target namespace. There is no general egress allow on Kestrel: outbound traffic to anything other than cluster DNS is blocked until you write an explicit egress rule. The rule is additive: it does not remove the default-deny posture, it just opens a lane on top of it.

The exact shape of a tenant-scoped NetworkPolicy (pod selectors, ingress/egress blocks, the namespaceSelector tricks for cross-namespace-same-tenant traffic) is covered in NetworkPolicy in practice. The one-line rule is: write a NetworkPolicy in the destination namespace, select the destination Pod, and allow the source you need. The Ingress on Kestrel page covers the external HTTP/HTTPS path through Traefik in more detail.

Cross-tenant pod-to-pod traffic is blocked by default because a tenant is the boundary of trust on Kestrel — RCS treats two tenants as two different teams with no implicit sharing. If two tenants need to cooperate on a workflow, open a ticket and RCS will add an explicit allow.

A handful of service and ingress shapes are blocked at a different layer — the Capsule admission webhook — not at the NetworkPolicy layer. The distinction matters because the error you see in kubectl output tells you which layer rejected you:

  • LoadBalancer and NodePort services are rejected at admission time, not at packet-routing time. See Known limitations.
  • Wildcard Ingress hostnames are rejected at admission time. See Known limitations.

If a packet gets through to your Pod and is then dropped, the problem is NetworkPolicy. If the manifest itself is rejected before the Pod even exists, the problem is Capsule admission. The two failure modes look similar in the dashboard but the fix paths are different, so the layer that rejected you is worth noting.