|
| 1 | +# Network Security & LAN Isolation |
| 2 | + |
| 3 | +This document details the Cilium network policy that isolates Kubernetes pods from the local network, preventing lateral movement attacks while allowing legitimate traffic. |
| 4 | + |
| 5 | +## Overview |
| 6 | + |
| 7 | +The cluster uses a **CiliumClusterwideNetworkPolicy** to implement a "default deny" stance for LAN access. This provides an 80/20 security solution - one policy that protects all pods without requiring per-app network policies. |
| 8 | + |
| 9 | +## The Threat Model |
| 10 | + |
| 11 | +When hosting public-facing applications (via Cloudflare Tunnel), an attacker who exploits a vulnerability could: |
| 12 | + |
| 13 | +1. Gain shell access inside a pod |
| 14 | +2. Scan the internal network |
| 15 | +3. Pivot to attack other LAN devices (router, NAS, other servers) |
| 16 | + |
| 17 | +```mermaid |
| 18 | +graph LR |
| 19 | + subgraph "Internet" |
| 20 | + Attacker[Attacker] |
| 21 | + end |
| 22 | +
|
| 23 | + subgraph "Cloudflare" |
| 24 | + CF[Cloudflare Tunnel] |
| 25 | + end |
| 26 | +
|
| 27 | + subgraph "Kubernetes Cluster" |
| 28 | + VulnPod[Vulnerable Pod] |
| 29 | + end |
| 30 | +
|
| 31 | + subgraph "LAN (192.168.10.0/24)" |
| 32 | + Router[Router .1] |
| 33 | + NAS[TrueNAS .133] |
| 34 | + Other[Other Devices] |
| 35 | + end |
| 36 | +
|
| 37 | + Attacker -->|1. Exploit| CF |
| 38 | + CF -->|2. RCE| VulnPod |
| 39 | + VulnPod -.->|3. Pivot BLOCKED| Router |
| 40 | + VulnPod -.->|3. Pivot BLOCKED| Other |
| 41 | +
|
| 42 | + style VulnPod fill:#f96,stroke:#333,stroke-width:2px |
| 43 | + style Router fill:#f66,stroke:#333,stroke-width:2px |
| 44 | +``` |
| 45 | + |
| 46 | +## The Solution: CiliumClusterwideNetworkPolicy |
| 47 | + |
| 48 | +Located at: `infrastructure/networking/cilium/policies/block-lan-access.yaml` |
| 49 | + |
| 50 | +### What Gets Blocked |
| 51 | + |
| 52 | +| Traffic | Status | Reason | |
| 53 | +|---------|--------|--------| |
| 54 | +| RFC1918 ranges (10.x, 172.16.x, 192.168.x) | **BLOCKED** | Prevents LAN scanning | |
| 55 | +| Router (192.168.10.1) | **BLOCKED** | Prevents admin/SSH access | |
| 56 | +| Random LAN devices | **BLOCKED** | No lateral movement | |
| 57 | + |
| 58 | +### What Gets Allowed |
| 59 | + |
| 60 | +| Traffic | Status | Reason | |
| 61 | +|---------|--------|--------| |
| 62 | +| Internet (public IPs) | **ALLOWED** | Apps need external APIs | |
| 63 | +| Pod-to-Pod (cluster) | **ALLOWED** | Inter-service communication | |
| 64 | +| Kube-apiserver | **ALLOWED** | Kubernetes operations | |
| 65 | +| DNS (CoreDNS) | **ALLOWED** | Name resolution | |
| 66 | +| TrueNAS (specific ports) | **ALLOWED** | NFS/SMB/MinIO storage | |
| 67 | +| LoadBalancer IPs | **ALLOWED** | Cilium L2 announcements | |
| 68 | + |
| 69 | +## Policy Architecture |
| 70 | + |
| 71 | +```mermaid |
| 72 | +graph TD |
| 73 | + subgraph "Egress Rules" |
| 74 | + Internet[Internet<br/>0.0.0.0/0 EXCEPT RFC1918] |
| 75 | + Cluster[Cluster Entities<br/>pods, nodes, apiserver] |
| 76 | + Storage[Whitelisted Storage<br/>TrueNAS: ports 2049,445,9000] |
| 77 | + LB[LoadBalancer Pool<br/>192.168.10.32/27] |
| 78 | + end |
| 79 | +
|
| 80 | + subgraph "All Pods" |
| 81 | + Pod[Any Pod] |
| 82 | + end |
| 83 | +
|
| 84 | + subgraph "Blocked" |
| 85 | + LAN[LAN Devices<br/>192.168.10.x] |
| 86 | + Router[Router<br/>192.168.10.1] |
| 87 | + end |
| 88 | +
|
| 89 | + Pod -->|ALLOWED| Internet |
| 90 | + Pod -->|ALLOWED| Cluster |
| 91 | + Pod -->|ALLOWED| Storage |
| 92 | + Pod -->|ALLOWED| LB |
| 93 | + Pod -.->|BLOCKED| LAN |
| 94 | + Pod -.->|BLOCKED| Router |
| 95 | +
|
| 96 | + style LAN fill:#f66,stroke:#333 |
| 97 | + style Router fill:#f66,stroke:#333 |
| 98 | + style Internet fill:#6f6,stroke:#333 |
| 99 | + style Cluster fill:#6f6,stroke:#333 |
| 100 | +``` |
| 101 | + |
| 102 | +## Whitelisted LAN Resources |
| 103 | + |
| 104 | +These specific IPs are allowed on specific ports only: |
| 105 | + |
| 106 | +| IP | Hostname | Allowed Ports | Purpose | |
| 107 | +|----|----------|---------------|---------| |
| 108 | +| 192.168.10.133 | TrueNAS | 2049 (NFS), 111 (RPC), 445 (SMB), 9000 (MinIO) | Storage backend | |
| 109 | +| 172.31.250.1 | TrueNAS SMB | 445 (SMB) | SMB shares for apps | |
| 110 | +| 192.168.10.46 | Wyze Bridge | 8554 (RTSP) | Camera streams for Frigate | |
| 111 | +| 192.168.10.14 | Proxmox | 8006 (API) | Omni/Terraform integration | |
| 112 | +| 192.168.10.32/27 | LB Pool | All | Cilium L2 LoadBalancer IPs | |
| 113 | + |
| 114 | +## Why Lateral Movement Fails |
| 115 | + |
| 116 | +The policy uses `endpointSelector: {}` which matches **ALL pods** in the cluster: |
| 117 | + |
| 118 | +```yaml |
| 119 | +spec: |
| 120 | + endpointSelector: {} # <-- Applies to EVERY pod |
| 121 | +``` |
| 122 | +
|
| 123 | +This means: |
| 124 | +- DVWA pod cannot reach LAN |
| 125 | +- n8n pod cannot reach LAN |
| 126 | +- If attacker pivots from DVWA → n8n, n8n STILL cannot reach LAN |
| 127 | +
|
| 128 | +```mermaid |
| 129 | +sequenceDiagram |
| 130 | + participant Attacker |
| 131 | + participant DVWA as DVWA Pod |
| 132 | + participant N8N as n8n Pod |
| 133 | + participant Router as Router (192.168.10.1) |
| 134 | + |
| 135 | + Attacker->>DVWA: Exploit vulnerability |
| 136 | + Note over DVWA: Shell access gained |
| 137 | + |
| 138 | + DVWA->>Router: ping 192.168.10.1 |
| 139 | + Router--xDVWA: BLOCKED (100% packet loss) |
| 140 | + |
| 141 | + DVWA->>N8N: Pivot to n8n pod |
| 142 | + Note over N8N: Lateral movement works |
| 143 | + |
| 144 | + N8N->>Router: ping 192.168.10.1 |
| 145 | + Router--xN8N: STILL BLOCKED |
| 146 | + |
| 147 | + Note over Attacker,Router: No matter which pod,<br/>LAN is unreachable |
| 148 | +``` |
| 149 | +
|
| 150 | +## Testing the Policy |
| 151 | +
|
| 152 | +### Quick Test (from any pod) |
| 153 | +
|
| 154 | +```bash |
| 155 | +# Test LAN access (should fail) |
| 156 | +kubectl exec -n <namespace> <pod> -- ping -c 1 -W 2 192.168.10.1 |
| 157 | + |
| 158 | +# Test internet access (should work) |
| 159 | +kubectl exec -n <namespace> <pod> -- ping -c 2 8.8.8.8 |
| 160 | +``` |
| 161 | + |
| 162 | +### Full Pentest Simulation |
| 163 | + |
| 164 | +Deploy DVWA (Damn Vulnerable Web Application) for realistic testing: |
| 165 | + |
| 166 | +1. Access `https://dvwa.vanillax.me` |
| 167 | +2. Login: `admin` / `password` |
| 168 | +3. Set security to "Low" |
| 169 | +4. Navigate to Command Injection |
| 170 | +5. Try: `; ping -c 1 -W 2 192.168.10.1` |
| 171 | + |
| 172 | +**Expected Result**: 100% packet loss (LAN blocked) |
| 173 | + |
| 174 | +### Verify from Multiple Pods |
| 175 | + |
| 176 | +```bash |
| 177 | +# Test from different namespaces |
| 178 | +for ns in dvwa n8n immich; do |
| 179 | + echo "=== Testing from $ns ===" |
| 180 | + kubectl exec -n $ns deploy/${ns} -- ping -c 1 -W 2 192.168.10.1 2>&1 | grep -E "packet loss|PING" |
| 181 | +done |
| 182 | +``` |
| 183 | + |
| 184 | +## Hubble Observability |
| 185 | + |
| 186 | +Use Hubble to see policy enforcement in real-time: |
| 187 | + |
| 188 | +```bash |
| 189 | +# Watch for dropped traffic |
| 190 | +hubble observe --verdict DROPPED --to-ip 192.168.10.0/24 |
| 191 | + |
| 192 | +# Watch specific pod |
| 193 | +hubble observe --pod dvwa/dvwa --verdict DROPPED |
| 194 | +``` |
| 195 | + |
| 196 | +## Troubleshooting |
| 197 | + |
| 198 | +### App Can't Reach Required LAN Resource |
| 199 | + |
| 200 | +Add a specific whitelist rule: |
| 201 | + |
| 202 | +```yaml |
| 203 | +- toCIDR: |
| 204 | + - 192.168.10.X/32 # The IP you need |
| 205 | + toPorts: |
| 206 | + - ports: |
| 207 | + - port: "XXXX" # Only the required port |
| 208 | + protocol: TCP |
| 209 | +``` |
| 210 | +
|
| 211 | +### Policy Not Taking Effect |
| 212 | +
|
| 213 | +1. Check Cilium agent is running: `kubectl get pods -n kube-system -l k8s-app=cilium` |
| 214 | +2. Verify policy is applied: `kubectl get ciliumclusterwidenetworkpolicies` |
| 215 | +3. Check Hubble for verdicts: `hubble observe --pod <your-pod>` |
| 216 | + |
| 217 | +### Internet Stopped Working |
| 218 | + |
| 219 | +Ensure `toEntities: host` is present - this allows traffic to reach the node which then NATs to the internet. |
| 220 | + |
| 221 | +## Security Considerations |
| 222 | + |
| 223 | +1. **Minimize Whitelists**: Only add LAN IPs that are absolutely necessary |
| 224 | +2. **Port Restrict**: Always specify ports, never allow all ports to a LAN IP |
| 225 | +3. **No Router Access**: Never whitelist 192.168.10.1 (your gateway) |
| 226 | +4. **Regular Audits**: Review whitelisted IPs periodically |
0 commit comments