Gateway¶
One shared Cilium Gateway API Gateway resource in the gateway namespace handles all
inbound traffic. Cilium provisions a single DigitalOcean Load Balancer for it.
Architecture¶
Cloudflare Edge (orange-cloud DNS, TLS terminated with edge cert)
↓ (CF re-encrypts with Origin CA cert)
DO Load Balancer (proxy-protocol, CF IP allowlist)
↓
Cilium Gateway shared (gateway ns)
├── :80 http-redirect → 301 https
├── :443 api-oep-stg → HTTPRoute in oep-stg ns
├── :443 api-mansety-prd → HTTPRoute in mansety-prd ns
├── :443 api-us-prd → HTTPRoute in us-prd ns
├── :6001 ws-oep-stg → HTTPRoute in oep-stg ns
├── :6001 ws-mansety-prd → HTTPRoute in mansety-prd ns
├── :6001 ws-us-prd → HTTPRoute in us-prd ns
├── :443 argocd-unipuka → HTTPRoute in gateway ns (BD-3.14)
└── :443 grafana-unipuka → HTTPRoute in gateway ns (BD-3.14)
Tenant isolation via allowedRoutes¶
Each tenant API/WS listener has allowedRoutes.namespaces.from: Selector with:
This means an HTTPRoute in mansety-prd ns cannot attach to the api-oep-stg listener.
Namespace labels are set by TF (k8s_namespaces.tf).
Hostnames¶
| Listener | Hostname | TLS Secret | Tenant ns |
|---|---|---|---|
| api-oep-stg | api-staging.unipuka.app |
tls-unipuka-app | oep-stg |
| ws-oep-stg | ws-staging.unipuka.app |
tls-unipuka-app | oep-stg |
| api-mansety-prd | api.mansety.com |
tls-mansety-com | mansety-prd |
| ws-mansety-prd | ws.mansety.com |
tls-mansety-com | mansety-prd |
| api-us-prd | api.us-academy.net |
tls-us-academy-net | us-prd |
| ws-us-prd | ws.us-academy.net |
tls-us-academy-net | us-prd |
| argocd-unipuka | argocd.unipuka.app |
tls-unipuka-app | gateway (same) |
| grafana-unipuka | grafana.unipuka.app |
tls-unipuka-app | gateway (same) |
Adding a new hostname¶
See add-hostname.md for the step-by-step recipe.
DO Load Balancer annotations¶
The Gateway's infrastructure.annotations set:
do-loadbalancer-enable-proxy-protocol: "true"- preserves real client IP.do-loadbalancer-allow-cidrs- Cloudflare IP ranges only; non-CF traffic dropped at LB.
Debugging¶
Listener Accepted=False¶
kubectl -n gateway describe gateway shared
# Listeners[].Conditions[type=Accepted].Message
# Common: invalid hostname, certificateRef Secret not found/wrong type
Listener ResolvedRefs=False¶
# TLS Secret missing or wrong type
kubectl -n gateway get secret tls-unipuka-app -o jsonpath='{.type}'
# expect: kubernetes.io/tls
# ExternalSecret Ready?
kubectl -n gateway get externalsecret tls-unipuka-app
HTTPRoute NotAllowedByListeners¶
kubectl -n oep-stg describe httproute <name>
# Means tenant ns label missing or wrong listener sectionName
kubectl get ns oep-stg --show-labels | grep gateway.unipuka.app
DO LB not provisioned¶
doctl compute load-balancer list | grep -i cilium
kubectl -n gateway get gateway shared -o jsonpath='{.status.addresses}'
# If empty after 2 min, check cilium-operator logs:
kubectl -n kube-system logs -l app.kubernetes.io/name=cilium-operator | grep -i gateway