Skip to content

Commit 5d3a094

Browse files
committed
Add Cilium LAN isolation policy and DVWA pentest app
Introduces a CiliumClusterwideNetworkPolicy to block pod egress to LAN while allowing internet, cluster, and specific storage access. Adds documentation on network policy and LAN isolation. Deploys DVWA (Damn Vulnerable Web Application) for penetration testing, including deployment, service, HTTPRoute, and namespace manifests. Updates Cilium kustomization to include the new policy. Also clears WHITELISTED_MODELS in Ollama WebUI config.
1 parent 6293bfb commit 5d3a094

9 files changed

Lines changed: 447 additions & 2 deletions

File tree

docs/network-policy.md

Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
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

infrastructure/networking/cilium/kustomization.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@ kind: Kustomization
77
# NOTE: Apply ip-pool.yaml and l2-policy.yaml AFTER Cilium is running
88
# These require Cilium CRDs to be installed first
99

10-
resources:
10+
resources:
1111
- ip-pool.yaml
1212
- l2-policy.yaml
13+
- policies/block-lan-access.yaml
1314

1415
helmCharts:
1516
- name: cilium
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
apiVersion: cilium.io/v2
2+
kind: CiliumClusterwideNetworkPolicy
3+
metadata:
4+
name: default-deny-lan-egress
5+
spec:
6+
description: "Block pods from accessing LAN - allow internet, cluster, and whitelisted storage"
7+
endpointSelector: {} # Applies to ALL pods
8+
9+
# ============================================
10+
# INGRESS: Allow all legitimate traffic
11+
# (cluster, host, and external via gateway)
12+
# ============================================
13+
ingress:
14+
- fromEntities:
15+
- cluster
16+
- host
17+
- world
18+
19+
egress:
20+
# ============================================
21+
# ALLOW: Internet (everything EXCEPT RFC1918)
22+
# ============================================
23+
- toCIDRSet:
24+
- cidr: 0.0.0.0/0
25+
except:
26+
- 10.0.0.0/8
27+
- 172.16.0.0/12
28+
- 192.168.0.0/16
29+
30+
# ============================================
31+
# ALLOW: All cluster-internal traffic
32+
# (pods, services, nodes, kube-apiserver)
33+
# ============================================
34+
- toEntities:
35+
- cluster
36+
- host
37+
- kube-apiserver
38+
39+
# ============================================
40+
# ALLOW: LoadBalancer IP Pool (Cilium L2)
41+
# Needed for pod-to-service via external IPs
42+
# ============================================
43+
- toCIDR:
44+
- 192.168.10.32/27
45+
46+
# NOTE: Gateway (192.168.10.1) NOT whitelisted
47+
# Internet works via toEntities:host, no direct gateway access needed
48+
# This prevents pods from accessing router admin/SSH
49+
50+
# ============================================
51+
# ALLOW: TrueNAS - NFS + SMB + MinIO Storage
52+
# (NFS for Plex, SMB for Jellyfin, MinIO for Longhorn backups)
53+
# ============================================
54+
- toCIDR:
55+
- 192.168.10.133/32
56+
toPorts:
57+
- ports:
58+
- port: "2049"
59+
protocol: TCP
60+
- port: "111"
61+
protocol: TCP
62+
- port: "111"
63+
protocol: UDP
64+
- port: "445"
65+
protocol: TCP
66+
- port: "9000"
67+
protocol: TCP
68+
69+
# ============================================
70+
# ALLOW: TrueNAS - SMB Storage
71+
# (Used by ComfyUI, Ollama, Frigate, etc.)
72+
# ============================================
73+
- toCIDR:
74+
- 172.31.250.1/32
75+
toPorts:
76+
- ports:
77+
- port: "445"
78+
protocol: TCP
79+
80+
# ============================================
81+
# ALLOW: Wyze Bridge RTSP (for Frigate)
82+
# ============================================
83+
- toCIDR:
84+
- 192.168.10.46/32
85+
toPorts:
86+
- ports:
87+
- port: "8554"
88+
protocol: TCP
89+
90+
# ============================================
91+
# ALLOW: Proxmox API (for Omni provider)
92+
# Remove if not using Omni/Terraform in-cluster
93+
# ============================================
94+
- toCIDR:
95+
- 192.168.10.14/32
96+
toPorts:
97+
- ports:
98+
- port: "8006"
99+
protocol: TCP

my-apps/ai/ollama-webui/configmap.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ data:
1313

1414
# Semantic Routing for Unsloth 2.0
1515
DEFAULT_MODELS: "manager"
16-
WHITELISTED_MODELS: "reasoning,dev,manager,expert,vision"
16+
WHITELISTED_MODELS: ""
1717

1818
# Hardware-Optimized Parameters
1919
CONTEXT_WINDOW: "131072"
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
apiVersion: apps/v1
2+
kind: Deployment
3+
metadata:
4+
name: dvwa
5+
namespace: dvwa
6+
labels:
7+
app: dvwa
8+
annotations:
9+
description: "DVWA - Damn Vulnerable Web Application for penetration testing"
10+
spec:
11+
replicas: 1
12+
selector:
13+
matchLabels:
14+
app: dvwa
15+
template:
16+
metadata:
17+
labels:
18+
app: dvwa
19+
spec:
20+
containers:
21+
- name: dvwa
22+
image: vulnerables/web-dvwa:latest
23+
ports:
24+
- containerPort: 80
25+
name: http
26+
env:
27+
- name: RECAPTCHA_PRIV_KEY
28+
value: ""
29+
- name: RECAPTCHA_PUB_KEY
30+
value: ""
31+
# Security level: low = easy to exploit for testing
32+
- name: SECURITY_LEVEL
33+
value: "low"
34+
# Disable PHPIDS protection
35+
- name: PHPIDS_ENABLED
36+
value: "0"
37+
resources:
38+
requests:
39+
memory: "128Mi"
40+
cpu: "100m"
41+
limits:
42+
memory: "512Mi"
43+
cpu: "500m"

0 commit comments

Comments
 (0)