susu10-10/3tier-k8s-Hardening
GitHub: susu10-10/3tier-k8s-Hardening
该项目是一个自动化 Kubernetes 加固实验室,演示如何将三层微服务应用从默认的不安全配置加固为零信任网络与 Restricted Pod 安全标准。
Stars: 0 | Forks: 0
# 🔒 三层架构 K8s 加固:阶段 2 (Zer0-Trust)
```
graph TB
subgraph "Kubernetes Cluster (Hardened)"
subgraph "Namespace: frontend-secure"
User((User)) --> FrontendSVC[Frontend Service
port: 80] FrontendSVC --> FrontendPod[Nginx Pod
runAsUser: 101
readOnlyRootFilesystem: true
capabilities: DROP ALL] end subgraph "Namespace: backend-secure" BackendSVC[Backend Service
port: 5000] BackendSVC --> BackendPod[Flask API Pod
runAsUser: 1000
automountServiceAccountToken: false
allowPrivilegeEscalation: false] DBSVC[PostgreSQL Service
port: 5432] DBSVC --> DBPod[PostgreSQL Pod
runAsUser: 70
readOnlyRootFilesystem: true] end subgraph "Network Policies" DenyAllIngress[Default Deny All Ingress
applied in both namespaces] AllowFrontendToBackend[Allow: frontend-secure → backend-secure
port 5000 only] AllowBackendToDB[Allow: backend-secure → postgres
port 5432 only] end subgraph "Security Controls" PSA[Pod Security Admission
label: restricted] Secrets[Randomly generated secrets
32-char base64, idempotent] NoServiceAccount[ServiceAccount tokens
disabled everywhere] end end FrontendPod -.-> DenyAllIngress FrontendPod -.-> AllowFrontendToBackend BackendPod -.-> AllowBackendToDB PSA -.-> FrontendPod PSA -.-> BackendPod PSA -.-> DBPod NoServiceAccount -.-> BackendPod style User fill:#e1f5fe,stroke:#01579b style FrontendPod fill:#fff3e0,stroke:#e65100 style BackendPod fill:#fff3e0,stroke:#e65100 style DBPod fill:#e8f5e9,stroke:#1b5e20 style DenyAllIngress fill:#ffebee,stroke:#c62828 style AllowFrontendToBackend fill:#e8f5e9,stroke:#2e7d32 style AllowBackendToDB fill:#e8f5e9,stroke:#2e7d32 style PSA fill:#f3e5f5,stroke:#4a148c ``` **目标**:将处于运行状态的三层架构应用从 `Privileged`(特权) Pod 安全标准加固为 `Restricted`(受限)标准。 ## 安全转型:从阶段 1 到阶段 2 阶段 1 主要侧重于功能性。 ## 阶段 1 - 不安全的默认配置 (Before) ### 1. Pod 以 root 身份运行  Container 并不是一个轻量级的虚拟机 (VM)。它只是运行在宿主机操作系统 (host-OS) 上的一个普通 Linux 进程。 它利用了两项 Linux 内核特性来制造隔离的假象(命名空间 namespaces 和控制组 C-groups)。Container 只是一个共享宿主机内核的进程,因此运行该进程的用户权限至关重要。默认情况下,许多公开的容器镜像(如 nginx)会以 root 身份运行其入口进程。容器内的 uid 与宿主机上的 uid 完全相同。 ### 2. 可写的根文件系统  ### 3. 默认挂载 Service account token  (如果攻击者实现了远程代码执行,他们就可以检查这个 secrets 目录,并提取此 token 供自己使用。) ### 4. 过多的 Linux Capabilities  默认配置过于宽松,即使是特权用户,依然会被赋予所有这些 Linux capabilities。 默认的 capabilities 包括 `CAP_CHOWN`、`CAP_DAC_OVERRIDE`、`CAP_NET_RAW`,这些对于 Web 应用来说都是不必要的。 大多数微服务并不需要它们。 ### 5. 允许提权  容器内的进程可以获得更多权限(例如,通过 setuid 二进制文件)。 子进程可以获取比其父进程更高的权限。当设置 `NoNewPrivs = 0` 时便会如此。 ### 6. Pod 安全准入 = Privileged  默认级别是 `privileged`,这意味着允许以最高权限级别执行任何操作。 ### 7. 缺乏网络策略 - 扁平且开放的通信  任何 Pod 都可以与其他任何 Pod 通信。  Kubernetes 拥有一个扁平且未路由的网络空间,其网络实现由 CNI 插件(calico、cillium 等)处理,并且该模型严格规定: 因此,位于 `namespace-1` 中的 Pod,即使与另一个 namespace(例如 `namespace-2`)中受高度安全保护的数据库没有任何业务或应用通信需求,在默认情况下它也能自动与其通信。 ### 8. 环境变量中的 Secrets  ## 🔒 阶段 2:加固后的状态 (After) [](https://asciinema.org/a/PKSP6nBTuEDtnFCF) ## 安全转型:从阶段 1 到阶段 2 **阶段 1** 关注功能性。**阶段 2** 关注纵深防御 (defense-in-depth)。 ### 1. Pod 安全准入 = restricted - **默认状态 (阶段 1)**:`privileged`(允许一切,包括宿主机级别的访问)。 - **加固后的状态**:`restricted`。 现在,每个 Pod 都必须通过严格校验,API server 才会接收它。这可以防止“影子 IT”或不安全的清单被部署。 ![alt text]()
### 2. 多 Namespace 隔离与网络分段设计
- **Frontend namespace** (`frontend-secure`):仅包含 Nginx,接收公共流量。
- **Backend namespace** (`backend-secure`):包含 Flask API 和 PostgreSQL,无外部 ingress。
- **Network Policies**:
- 默认全部拒绝 (default deny All):默认情况下,所有的 ingress/egress 流量都会被阻止。
- 显式允许规则 (explicit allow rules):仅允许 `frontend` -> `backend` (端口 5000) 以及 `backend` -> `database` (端口 5432) 的通信。
![alt text]()
### 3. 安全上下文 (非 root 用户、只读 root 文件系统、丢弃 capabilities)
将**最小权限原则**应用到了容器运行时。
- 不可变根文件系统:设置 `readOnlyRootFilesystem: true` 可防止攻击者植入恶意软件或篡改应用程序代码。
- 非 root 身份执行:容器以特定的 UID 运行(`Postgres` 为 `70`,`Nginx` 为 `101`,`Backend` 为 `1000`)。这可以防止发生容器逃逸时,攻击者借此获取宿主机的 root 访问权限。
![alt text]()
- 丢弃 Capabilities:丢弃所有的 Linux capabilities(如 `CAP_SYS_ADMIN` 等),从而减小内核的攻击面。
![alt text]()
- 提权:明确设置 `allowPrivilegeEscalation: false` 以阻断 setuid 二进制文件漏洞利用。
![alt text]()
此截图展示了执行 `touch /testing` 失败,而 `touch /tmp/testing` 成功的过程。
### 4. 禁用 Service account token
- 在所有的 deployments 中设置了 `automountServiceAccountToken: false`。
![alt text]()
这将防止攻击者窃取 Pod 的身份去查询 K8s API。
### 5. 资源配额与限制范围
![alt text]()
### 6. 随机生成的 Secrets (幂等)
- **基于熵的凭证**:我使用 `openssl rand` 生成 32 位的 base64 字符串,并结合本地文件检查替换了硬编码的密码,从而避免在重新运行时发生密码更改。
## 对比总结 (阶段 1 vs 阶段 2)
| 控制措施 | 阶段 1 (不安全) | 阶段 2 (已加固) |
|---------|-------------------|-------------------|
| Pod 用户 | root | 非 root (UID Nginx:`101`, Flask:`1000`, PostgreSQL:`70`) |
| 根文件系统 | 可写 | 只读 (包含 `/tmp` emptyDir) |
| Service account token | 已挂载 | 已禁用: `automountServiceAccountToken: false` |
| Linux capabilities | 默认 (包含过多) | 全部丢弃 (`DROP ALL`) |
| 提权 | 允许 | 已禁用: `allowPrivilegeEscalation: false` |
| PSA 标签 | `privileged` | `restricted` |
| Network policies | 无 | 默认拒绝 + 显式允许 |
| Secrets | 硬编码 | 随机 32 字符 base64 字符串,幂等 |
| 资源限制 | 无 | 配额 + 限制范围 |
### 经验教训
**权衡:特权端口 vs. 非 root 用户**
挑战:转为以 runAsUser: 101 (非 root) 运行后,导致 Nginx 无法绑定到 80 端口。
我们将容器应用修改为在内部使用 8080 端口,同时由 Kubernetes Service 处理来自 80 端口的转换。这在满足 `restricted` PSA 要求的同时,也没有牺牲可访问性。
## 如何复现
## 🛠️ 前置条件
- **Kubernetes 集群** (Minikube、Kind、KillerCoda 或任何兼容的集群)
- **kubectl** (v1.24+)
- **kustomize** (内置于 `kubectl 1.14+` 中)
- **Docker** (仅在您需要自行构建后端镜像时需要——否则请直接使用预构建的 `succesc/fact-app-s:v3`)
1. **克隆仓库**
`git clone https://github.com/susu10-10/3tier-k8s-Hardening.git`
`cd 3tier-k8s-Hardening.git`
2. 将所需的文件放置在与 deploy-secure.sh 相同的目录下:
3. 使脚本可执行
`chmod +x deploy-secure.sh`
4. 运行脚本
`./deploy-secure.sh`
5. 验证部署
`kubectl get all -n backend-secure`
`kubectl get all -n frontend-secure`
6. 访问前端
`kubectl port-forward -n frontend-secure svc/frontend-svc 8080:80`
```
http://localhost:8080 in your browser.
```
添加一条 fact -> 它就会出现。点击 “Random Fact” -> 会随机显示一条 fact。
port: 80] FrontendSVC --> FrontendPod[Nginx Pod
runAsUser: 101
readOnlyRootFilesystem: true
capabilities: DROP ALL] end subgraph "Namespace: backend-secure" BackendSVC[Backend Service
port: 5000] BackendSVC --> BackendPod[Flask API Pod
runAsUser: 1000
automountServiceAccountToken: false
allowPrivilegeEscalation: false] DBSVC[PostgreSQL Service
port: 5432] DBSVC --> DBPod[PostgreSQL Pod
runAsUser: 70
readOnlyRootFilesystem: true] end subgraph "Network Policies" DenyAllIngress[Default Deny All Ingress
applied in both namespaces] AllowFrontendToBackend[Allow: frontend-secure → backend-secure
port 5000 only] AllowBackendToDB[Allow: backend-secure → postgres
port 5432 only] end subgraph "Security Controls" PSA[Pod Security Admission
label: restricted] Secrets[Randomly generated secrets
32-char base64, idempotent] NoServiceAccount[ServiceAccount tokens
disabled everywhere] end end FrontendPod -.-> DenyAllIngress FrontendPod -.-> AllowFrontendToBackend BackendPod -.-> AllowBackendToDB PSA -.-> FrontendPod PSA -.-> BackendPod PSA -.-> DBPod NoServiceAccount -.-> BackendPod style User fill:#e1f5fe,stroke:#01579b style FrontendPod fill:#fff3e0,stroke:#e65100 style BackendPod fill:#fff3e0,stroke:#e65100 style DBPod fill:#e8f5e9,stroke:#1b5e20 style DenyAllIngress fill:#ffebee,stroke:#c62828 style AllowFrontendToBackend fill:#e8f5e9,stroke:#2e7d32 style AllowBackendToDB fill:#e8f5e9,stroke:#2e7d32 style PSA fill:#f3e5f5,stroke:#4a148c ``` **目标**:将处于运行状态的三层架构应用从 `Privileged`(特权) Pod 安全标准加固为 `Restricted`(受限)标准。 ## 安全转型:从阶段 1 到阶段 2 阶段 1 主要侧重于功能性。 ## 阶段 1 - 不安全的默认配置 (Before) ### 1. Pod 以 root 身份运行  Container 并不是一个轻量级的虚拟机 (VM)。它只是运行在宿主机操作系统 (host-OS) 上的一个普通 Linux 进程。 它利用了两项 Linux 内核特性来制造隔离的假象(命名空间 namespaces 和控制组 C-groups)。Container 只是一个共享宿主机内核的进程,因此运行该进程的用户权限至关重要。默认情况下,许多公开的容器镜像(如 nginx)会以 root 身份运行其入口进程。容器内的 uid 与宿主机上的 uid 完全相同。 ### 2. 可写的根文件系统  ### 3. 默认挂载 Service account token  (如果攻击者实现了远程代码执行,他们就可以检查这个 secrets 目录,并提取此 token 供自己使用。) ### 4. 过多的 Linux Capabilities  默认配置过于宽松,即使是特权用户,依然会被赋予所有这些 Linux capabilities。 默认的 capabilities 包括 `CAP_CHOWN`、`CAP_DAC_OVERRIDE`、`CAP_NET_RAW`,这些对于 Web 应用来说都是不必要的。 大多数微服务并不需要它们。 ### 5. 允许提权  容器内的进程可以获得更多权限(例如,通过 setuid 二进制文件)。 子进程可以获取比其父进程更高的权限。当设置 `NoNewPrivs = 0` 时便会如此。 ### 6. Pod 安全准入 = Privileged  默认级别是 `privileged`,这意味着允许以最高权限级别执行任何操作。 ### 7. 缺乏网络策略 - 扁平且开放的通信  任何 Pod 都可以与其他任何 Pod 通信。  Kubernetes 拥有一个扁平且未路由的网络空间,其网络实现由 CNI 插件(calico、cillium 等)处理,并且该模型严格规定: 因此,位于 `namespace-1` 中的 Pod,即使与另一个 namespace(例如 `namespace-2`)中受高度安全保护的数据库没有任何业务或应用通信需求,在默认情况下它也能自动与其通信。 ### 8. 环境变量中的 Secrets  ## 🔒 阶段 2:加固后的状态 (After) [](https://asciinema.org/a/PKSP6nBTuEDtnFCF) ## 安全转型:从阶段 1 到阶段 2 **阶段 1** 关注功能性。**阶段 2** 关注纵深防御 (defense-in-depth)。 ### 1. Pod 安全准入 = restricted - **默认状态 (阶段 1)**:`privileged`(允许一切,包括宿主机级别的访问)。 - **加固后的状态**:`restricted`。 现在,每个 Pod 都必须通过严格校验,API server 才会接收它。这可以防止“影子 IT”或不安全的清单被部署。 ![alt text](
标签:Web截图, 子域名突变, 容器安全, 应用安全, 测试用例, 系统加固, 请求拦截, 零信任网络