The packet capture operator now supports pod-based packet capture using Kubernetes ephemeral containers. This approach attaches debug containers directly to running pods for more accurate pod-to-pod traffic capture.
-
Pod-Based Capture (NEW)
- Triggered when
source.podSelectorordestination.podSelectoris specified - Creates ephemeral debug containers attached to matching pods
- Captures traffic directly from pod network namespace
- Capture files include pod names:
{capture-name}-{pod-name}-{direction}.pcap
- Triggered when
-
Node-Based Capture (Original)
- Triggered when no pod selectors are specified
- Creates Jobs on nodes with hostNetwork
- Captures traffic at node level
- Capture files include node names
When you create a PacketCapture with pod selectors:
- Pod Discovery: Controller finds all pods matching source/destination selectors
- Ephemeral Container Creation: For each pod, creates a debug container with:
- Name:
capture-{capture-name}-{source|destination} - Image:
nicolaka/netshoot:latest - Capabilities:
NET_ADMIN,NET_RAW - Shared network namespace with target pod
- Name:
- Packet Capture: tcpdump runs inside ephemeral container
- Storage: Captures saved to PersistentVolume with pod name in filename
apiVersion: capture.k8s.io/v1alpha1
kind: PacketCapture
metadata:
name: pod-to-pod-capture
namespace: default
spec:
# Capture traffic FROM pods with class=netshoot label
source:
podSelector:
matchLabels:
class: netshoot
# Capture traffic TO pods with app=proxy label
destination:
podSelector:
matchLabels:
app: proxy
ports:
- port: 80
protocol: TCP
duration: "2m"
interface: "any"
# Save to persistent storage
storage:
type: "PersistentVolume"
persistentVolumeClaim: "packet-capture-pvc"If you have:
- 2 pods with
class=netshoot(netshoot-1, netshoot-2) - 3 pods with
app=proxy(proxy-a, proxy-b, proxy-c)
The operator creates 5 ephemeral containers:
capture-pod-to-pod-capture-sourceon netshoot-1capture-pod-to-pod-capture-sourceon netshoot-2capture-pod-to-pod-capture-destinationon proxy-acapture-pod-to-pod-capture-destinationon proxy-bcapture-pod-to-pod-capture-destinationon proxy-c
Files are saved with pod names:
pod-to-pod-capture-netshoot-1-source.pcap
pod-to-pod-capture-netshoot-2-source.pcap
pod-to-pod-capture-proxy-a-destination.pcap
pod-to-pod-capture-proxy-b-destination.pcap
pod-to-pod-capture-proxy-c-destination.pcap
-
controllers/pod_capture.go: Pod discovery and ephemeral container managementgetTargetPods(): Finds pods matching selectorscreateEphemeralContainer(): Attaches debug container to podbuildPodCaptureCommand(): Builds tcpdump command for podbuildCaptureJobName(): Creates unique name with pod name
-
controllers/packetcapture_controller.go: Main reconciliation logichandlePending(): Routes to pod-based or node-based capturehandlePodBasedCapture(): Orchestrates ephemeral container creationhandleNodeBasedCapture(): Original node-level capture
Based on cilium-cli implementation (k8s/client.go):
// Create ephemeral container spec
ec := corev1.EphemeralContainer{
EphemeralContainerCommon: corev1.EphemeralContainerCommon{
Name: "capture-{name}-{direction}",
Image: "nicolaka/netshoot:latest",
Command: []string{"/bin/sh", "-c", tcpdumpCmd},
SecurityContext: &corev1.SecurityContext{
Privileged: true,
Capabilities: &corev1.Capabilities{
Add: []corev1.Capability{"NET_ADMIN", "NET_RAW"},
},
},
VolumeMounts: volumeMounts,
},
}
// Patch pod to add ephemeral container
patch := strategicpatch.CreateTwoWayMergePatch(oldPod, newPod, pod)
r.Patch(ctx, pod, client.RawPatch(types.StrategicMergePatchType, patch))export KUBECONFIG=/Users/pijablon/Downloads/projects/kind/kubeconfig
# Apply the pod-to-pod capture example
kubectl apply -f examples/pod-to-pod-capture.yaml
# Watch the capture
kubectl get packetcaptures.capture.k8s.io pod-to-pod-capture -w# Check pods for ephemeral containers
kubectl get pods -l class=netshoot -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{range .spec.ephemeralContainers[*]} - {.name}{"\n"}{end}{end}'
# View ephemeral container logs
kubectl logs <pod-name> -c capture-pod-to-pod-capture-source# Download all captures
./scripts/access-persistent-captures.sh
# Files will include pod names
ls -lh captures/
# pod-to-pod-capture-netshoot-xxx-source.pcap
# pod-to-pod-capture-proxy-xxx-destination.pcap# View specific pod's traffic
tcpdump -tttt -n -r captures/pod-to-pod-capture-netshoot-1-source.pcap
# Filter for specific destination
tcpdump -r captures/pod-to-pod-capture-netshoot-1-source.pcap 'dst host 10.244.x.x'
# Open in Wireshark
wireshark captures/pod-to-pod-capture-netshoot-1-source.pcap- Accurate Pod Traffic: Captures from pod's network namespace, not node
- Selective Capture: Only target specific pods, not all traffic on node
- Pod Identification: Filenames include pod names for easy correlation
- Matrix Coverage: N source pods × M destination pods = complete capture matrix
- No Node Access: Works without node-level permissions
- Persistent: Ephemeral containers remain until pod deletion
- Requires Running Pods: Can only attach to pods in Running state
- Kubernetes 1.23+: Ephemeral containers require recent Kubernetes
- Storage: Requires PersistentVolume with ReadWriteMany for multi-pod access
- Cleanup: Ephemeral containers persist until pod is deleted
# Check operator logs
kubectl logs -n packet-capture-system deployment/packet-capture-controller-manager --tail=50
# Verify pod is running
kubectl get pod <pod-name> -o jsonpath='{.status.phase}'
# Check RBAC permissions
kubectl auth can-i patch pods --as=system:serviceaccount:packet-capture-system:packet-capture-controller-manager# Check ephemeral container status
kubectl get pod <pod-name> -o jsonpath='{.status.ephemeralContainerStatuses[*].state}'
# View container logs
kubectl logs <pod-name> -c capture-{name}-{direction}
# Verify PVC is mounted
kubectl exec <pod-name> -c capture-{name}-{direction} -- ls -la /captures- Auto-cleanup ephemeral containers after capture completes
- Support for sidecar pattern (long-running captures)
- Real-time streaming of capture data
- Integration with service mesh (Istio, Linkerd)
- Automatic traffic correlation between source and destination