Networking¶
Addressing¶
| Network | Range | Purpose |
|---|---|---|
| Node network | 192.168.55.0/24 |
Node, API, etcd and overlay underlay traffic |
| Pod network | 10.42.0.0/16 |
Addresses assigned to Pods |
| Service network | 10.43.0.0/16 |
Virtual Service addresses |
Per-node PodCIDRs:
The current Cilium configuration uses Kubernetes IPAM:
version=1.20.2
ipam=kubernetes
k8s-require-ipv4-pod-cidr=true
routing=VXLAN
kubeProxyReplacement=false
k3s assigns each node a PodCIDR. Cilium consumes that CIDR instead of allocating addresses from a separate cluster pool.
Verify:
kubectl get nodes \
-o custom-columns='NAME:.metadata.name,PODCIDR:.spec.podCIDR'
kubectl get configmap cilium-config \
--namespace kube-system \
-o jsonpath='ipam={.data.ipam}{"\n"}require-pod-cidr={.data.k8s-require-ipv4-pod-cidr}{"\n"}'
Cilium and kube-proxy¶
Cilium and kube-proxy coexist.
| Component | Current responsibility |
|---|---|
| Cilium | Pod interfaces, routing, NetworkPolicy, Service eBPF for tested Pod traffic and VXLAN |
| kube-proxy | iptables Service rules for traffic that reaches the host netfilter dataplane |
kubeProxyReplacement=false means kube-proxy remains part of the cluster. It does not mean Cilium performs no Service translation.
The tested kube-dns paths were:
| Source | ClusterIP translation | Remote Pod transport |
|---|---|---|
Process on master |
kube-proxy / iptables | Cilium VXLAN |
Pod on master |
Cilium eBPF | Cilium VXLAN |
These results apply to the tested UDP/53 ClusterIP traffic. NodePort and LoadBalancer use separate paths.
Service and EndpointSlice¶
A Service provides a stable virtual frontend. EndpointSlice stores the current backend addresses and readiness conditions.
EndpointSlice belongs to the control path:
- Kubernetes records ready backends.
- kube-proxy and Cilium observe Service and EndpointSlice objects.
- They update local iptables rules or eBPF maps.
- Packets use the programmed dataplane; they do not query EndpointSlice directly.
Inspect the current state:
kubectl get service kube-dns \
--namespace kube-system \
--output wide
kubectl get endpointslices \
--namespace kube-system \
--selector kubernetes.io/service-name=kube-dns \
--output wide
kube-proxy iptables path¶
For tested traffic originating from a node process, kube-proxy used:
The ClusterIP is not assigned to a network interface and does not require a route in the regular routing table. Netfilter intercepts the destination and performs DNAT.
Inspect counters:
Cilium VXLAN path¶
After Service translation selects a Pod on another node, Cilium routes the packet through cilium_host and encapsulates it in VXLAN.
Example observed during a DNS query:
The inner destination was already the CoreDNS Pod address. Service translation therefore happened before VXLAN encapsulation.
cilium_host is the host-side entry into the Cilium datapath. cilium_vxlan implements the overlay between nodes.
Useful checks:
Hubble network path¶
Each Cilium agent exposes the Hubble observer API on its node address at TCP port 4244.
Users reach Hubble UI through its normal Ingress at https://hubble.cluster.kcn333.com. Traefik routes this request to Service/hubble-ui; no manual port-forward is required.
The hubble-peer Service uses internalTrafficPolicy: Local. Hubble Relay first connects through the Service to the local Cilium agent and obtains the peer list. It then opens a direct TLS connection to every advertised node endpoint:
Hubble Relay Pod
-> hubble-peer ClusterIP:443
-> local Cilium agent:4244
-> peer list
-> 192.168.55.10:4244
-> 192.168.55.11:4244
-> 192.168.55.12:4244
The internal Relay-to-agent path requires Pod-to-NodeIP connectivity on TCP 4244.
This is cluster-internal traffic, separate from browser access through Traefik.
The configuration uses the Pod network without hostNetwork or an additional
UFW route rule. Hubble UI provides live flows and the service map.
Useful checks:
kubectl get service hubble-peer \
--namespace kube-system \
--output wide
kubectl get endpointslices \
--namespace kube-system \
--selector kubernetes.io/service-name=hubble-peer \
--output wide
HAProxy and node access¶
HAProxy is the intended external entry point:
Direct node addresses are still valid technical endpoints for NodePort and ServiceLB. Router and UFW rules decide which source networks may use them. HAProxy does not make those paths disappear.
Application traffic through ServiceLB is described in Ingress and TLS.
NetworkPolicy¶
Without a policy, Pods can communicate across namespaces. A NetworkPolicy selects Pods and restricts ingress, egress or both.
The application-access policy allows PostgreSQL traffic from clients-api Pods in the same namespace:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: clients-db-allow-only-api
namespace: clients
spec:
podSelector:
matchLabels:
cnpg.io/cluster: clients-db
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: clients-api
ports:
- protocol: TCP
port: 5432
Cilium drops traffic denied by policy. A timeout can therefore indicate a policy drop; it does not prove a routing failure.
CloudNativePG replication¶
Both clients/clients-db and clients-staging/clients-db-staging have two
instances. Their policies also allow the CNPG operator in cnpg-system to reach
TCP 8000, and Pods bearing the same cnpg.io/cluster label in the same namespace
to reach database instances on TCP 5432.
The replication rule selects destination Pods by both cnpg.io/cluster and
cnpg.io/podRole: instance; the source selector uses only the cluster label so
that replica join Jobs are included. It does not allow other namespaces or
unrelated workloads.
The current policies do not allow Prometheus to scrape CNPG metrics on TCP 9187.
API server proxy traffic to Sealed Secrets¶
kubeseal --fetch-cert and kubeseal --validate reach the controller through the
Kubernetes API server proxy. HAProxy may send the client request to an API server on a
different node from the controller Pod. Cilium classifies the final hop as
remote-node, not as a Pod.
The Flux-managed CiliumNetworkPolicy permits only remote-node to reach the selected
Sealed Secrets controller on 8080/TCP. It does not expose the endpoint to the LAN or
Internet and does not select other workloads. The existing Flux allow-scraping
policy separately permits Pod-originated traffic to port 8080.
Inspect policies and drops:
kubectl get networkpolicy -A
kubectl describe networkpolicy <name> -n <namespace>
kubectl -n kube-system exec ds/cilium -- cilium-dbg monitor --type drop
Required node ports¶
| Port | Protocol | Purpose |
|---|---|---|
| 6443 | TCP | Kubernetes API |
| 8472 | UDP | Cilium VXLAN |
| 10250 | TCP | kubelet API |
| 2379 | TCP | etcd client traffic |
| 2380 | TCP | etcd peer traffic |
| 9100 | TCP | node-exporter |
| 4443 | TCP | metrics-server webhook |
| 4244 | TCP | Hubble Relay to Cilium agents |
| 4240 | TCP | Cilium health |
| 123 | UDP | NTP inside the node network |
The firewall source ranges and active rules are documented in Security and implemented by the cluster Ansible playbooks.