强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Podman 完全指南 / 11 - 安全加固

第 11 章 — 安全加固

11.1 容器安全全景

容器安全是一个多层次的防御体系:

┌──────────────────────────────────────────────┐
│              容器安全分层模型                    │
│                                              │
│  第 6 层: 供应链安全                           │
│  ├── 镜像签名与验证                           │
│  ├── 镜像扫描(CVE 检测)                      │
│  └── 可信基础镜像                              │
│                                              │
│  第 5 层: 运行时安全                           │
│  ├── seccomp 系统调用过滤                      │
│  ├── AppArmor / SELinux                      │
│  └── 运行时异常检测                            │
│                                              │
│  第 4 层: 内核隔离                             │
│  ├── 用户命名空间 (User Namespace)             │
│  ├── 网络命名空间 (Network Namespace)           │
│  ├── PID 命名空间                             │
│  └── cgroup 资源限制                          │
│                                              │
│  第 3 层: 容器权限                             │
│  ├── Linux Capability 管理                    │
│  ├── 只读根文件系统                            │
│  └── 无特权模式                               │
│                                              │
│  第 2 层: 镜像构建安全                         │
│  ├── 最小化镜像 (distroless/alpine)            │
│  ├── 多阶段构建                               │
│  ├── 非 root 用户运行                         │
│  └── 固定版本标签                              │
│                                              │
│  第 1 层: 宿主机安全                           │
│  ├── 内核更新                                 │
│  ├── 文件系统隔离                              │
│  └── 网络防火墙                               │
└──────────────────────────────────────────────┘

11.2 SELinux

SELinux(Security-Enhanced Linux)是 Linux 内核强制访问控制(MAC)机制,Podman 原生支持 SELinux。

11.2.1 SELinux 容器标签

# 查看容器进程的 SELinux 标签
podman run --rm alpine cat /proc/1/attr/current
# system_u:system_r:container_t:s0:c123,c456

# 查看宿主机上的容器文件标签
ls -Z /var/lib/containers/storage/
# system_u:object_r:container_file_t:s0:c123,c456 ...

11.2.2 SELinux 模式管理

# 查看 SELinux 状态
getenforce
# Enforcing / Permissive / Disabled

# 临时切换为 Permissive(调试用)
sudo setenforce 0

# 恢复 Enforcing
sudo setenforce 1

# 永久配置
sudo vi /etc/selinux/config
# SELINUX=enforcing

11.2.3 SELinux 与容器卷

# SELinux 阻止容器访问未标记的目录
# 容器内报错: Permission denied

# 解决方案:使用 :Z 或 :z 标签
podman run -v /data:/data:Z myapp        # :Z 私有标签
podman run -v /shared:/shared:z app1     # :z 共享标签
podman run -v /shared:/shared:z app2     # 多容器共享

# 手动标记目录
sudo semanage fcontext -a -t container_file_t "/data(/.*)?"
sudo restorecon -R /data

11.2.4 SELinux 布尔值

# 查看容器相关的 SELinux 布尔值
getsebool -a | grep container

# 常用布尔值
# container_connect_any — 允许容器连接任何端口
# container_manage_cgroup — 允许容器管理 cgroup
# container_use_cephfs — 允许容器使用 CephFS

# 设置布尔值(允许容器连接任何端口)
sudo setsebool -P container_connect_any on

11.3 seccomp

seccomp(Secure Computing Mode)用于限制容器可以使用的系统调用(syscall)。

11.3.1 默认 seccomp Profile

Podman 默认启用 seccomp,阻止约 50 个危险的系统调用:

# 查看默认阻止的系统调用
podman info | grep -A5 seccomp

# 被阻止的系统调用包括:
# - mount / umount2 — 防止挂载文件系统
# - reboot — 防止重启系统
# - kexec_load — 防止加载新内核
# - open_by_handle_at — 绕过文件权限
# - init_module / finit_module — 加载内核模块
# - ptrace — 调试其他进程

11.3.2 自定义 seccomp Profile

// custom-seccomp.json
{
    "defaultAction": "SCMP_ACT_ERRNO",
    "architectures": ["SCMP_ARCH_X86_64"],
    "syscalls": [
        {
            "names": [
                "accept", "access", "arch_prctl", "bind", "brk",
                "chdir", "chmod", "chown", "clock_gettime",
                "close", "connect", "dup", "dup2", "dup3",
                "epoll_create1", "epoll_ctl", "epoll_wait",
                "execve", "exit", "exit_group",
                "fchmod", "fchown", "fcntl", "fstat", "futex",
                "getcwd", "getdents64", "getpid", "getppid",
                "getuid", "ioctl", "listen", "lseek",
                "madvise", "mmap", "mprotect", "munmap",
                "nanosleep", "newfstatat", "openat",
                "pipe2", "poll", "prctl", "pread64",
                "read", "readlink", "recvfrom", "rename",
                "rt_sigaction", "rt_sigprocmask",
                "sendto", "set_robust_list", "set_tid_address",
                "setsockopt", "socket", "stat", "statfs",
                "tgkill", "umask", "unlink", "wait4", "write",
                "writev"
            ],
            "action": "SCMP_ACT_ALLOW"
        }
    ]
}
# 使用自定义 seccomp Profile
podman run --security-opt seccomp=custom-seccomp.json myapp

# 禁用 seccomp(仅调试,不推荐生产)
podman run --security-opt seccomp=unconfined myapp

11.4 Linux Capability

Linux Capability 将传统的 root 权限细分为多个独立的权限单元。

11.4.1 容器默认 Capability

# 查看容器的默认 Capability
podman run --rm alpine cat /proc/1/status | grep Cap
# CapPrm: 00000000a80425fb
# CapEff: 00000000a80425fb

# 解码
capsh --decode=00000000a80425fb
# 0x00000000a80425fb=cap_chown,cap_dac_override,cap_fowner,
# cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,
# cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_mknod,
# cap_audit_write,cap_setfcap

11.4.2 Capability 管理

# 移除所有 Capability,只添加需要的
podman run --cap-drop=ALL --cap-add=NET_BIND_SERVICE \
    --name web nginx:1.27-alpine

# 添加特定 Capability
podman run --cap-add=SYS_PTRACE mydebugger:latest

# 常用 Capability 说明
Capability作用常见用途
NET_BIND_SERVICE绑定 < 1024 端口Web 服务器
NET_RAW使用 raw socketping、网络诊断
SYS_PTRACE调试进程调试器
SYS_ADMIN大量系统管理操作特殊场景(慎用)
SYS_TIME修改系统时钟NTP 容器
SYS_RESOURCE绕过资源限制特殊性能需求
DAC_OVERRIDE绕过文件权限文件操作
CHOWN修改文件所有者初始化脚本
SETUID/SETGID切换用户/组启动脚本降权
# 查看容器 Capability
podman exec web capsh --print

# 生产推荐:先 drop ALL,再按需 add
podman run --cap-drop=ALL \
    --cap-add=CHOWN \
    --cap-add=SETUID \
    --cap-add=SETGID \
    --cap-add=NET_BIND_SERVICE \
    myapp:latest

11.5 只读根文件系统

# 只读根文件系统 + tmpfs
podman run -d --name app \
    --read-only \
    --tmpfs /tmp:rw,size=100m,mode=1777 \
    --tmpfs /run:rw,size=50m \
    -v app-data:/app/data:Z \
    myapp:latest

# 优势:
# 1. 防止恶意写入文件系统
# 2. 容器内的恶意进程无法持久化
# 3. 强制应用设计为无状态

# 在 Quadlet 中使用
# app.container
[Container]
ReadOnly=true
Tmpfs=/tmp:size=100m
Tmpfs=/run:size=50m

11.6 用户命名空间深度

11.6.1 –userns 配置

# 默认:Rootless 使用用户命名空间映射
podman run --rm alpine id
# uid=0(root) gid=0(root)

# keep-id:容器内 UID/GID 与宿主机用户一致
podman run --rm --userns=keep-id alpine id
# uid=1000 gid=1000
# 适用于开发环境,避免文件权限问题

# auto:自动分配映射
podman run --rm --userns=auto alpine id
# uid=0(root) gid=0(root)(但在独立的映射范围内)

# 指定 UID/GID 映射
podman run --rm --userns=keep-id:uid=1000,gid=1000 alpine id

# host:使用宿主机命名空间(放弃隔离)
podman run --rm --userns=host alpine id
# uid=0(root) gid=0(root)(宿主机 root,危险!)

11.6.2 多租户隔离

# 不同用户运行容器,天然隔离
alice$ podman run -d --name app1 myapp:v1
bob$ podman run -d --name app2 myapp:v2

# 容器存储完全隔离
# alice: ~/.local/share/containers/storage/
# bob:   ~/.local/share/containers/storage/

11.7 镜像安全

11.7.1 镜像扫描

# 使用 Trivy 扫描镜像漏洞
podman images
trivy image nginx:1.27-alpine

# 使用 Grype 扫描
grype nginx:1.27-alpine

# 扫描本地构建的镜像
trivy image myapp:v1.0

11.7.2 安全基础镜像选择

# 镜像安全等级对比
镜像大小包管理器ShellCVE 风险
ubuntu:24.04~78MB✅ apt
debian:bookworm-slim~75MB✅ apt
alpine:3.20~8MB✅ apk
distroless/static~2MB极低
scratch0MB
# 使用 distroless 作为最终镜像
FROM golang:1.23 AS builder
WORKDIR /src
COPY . .
RUN CGO_ENABLED=0 go build -o /app

# 最终镜像:无 Shell、无包管理器
FROM gcr.io/distroless/static
COPY --from=builder /app /app
USER nonroot:nonroot
ENTRYPOINT ["/app"]

11.7.3 镜像签名验证

# 使用 cosign 验证镜像签名
# 安装 cosign
# brew install cosign (macOS)
# sudo dnf install cosign (Fedora)

# 验证签名
cosign verify \
    --certificate-identity-regexp='.*' \
    --certificate-oidc-issuer-regexp='.*' \
    ghcr.io/sigstore/cosign/cosign

# Podman 配置镜像签名策略
# /etc/containers/policy.json
{
    "default": [{"type": "insecureAcceptAnything"}],
    "transports": {
        "docker": {
            "registry.example.com": [
                {
                    "type": "signedBy",
                    "keyType": "GPGKeys",
                    "keyPath": "/etc/pki/rpm-gpg/RPM-GPG-KEY-example"
                }
            ]
        }
    }
}

11.8 安全检查清单

生产环境安全检查表

#!/bin/bash
# security-audit.sh — 容器安全审计脚本

echo "=== 容器安全审计 ==="

# 1. 检查是否有特权容器
echo "--- 特权容器 ---"
podman ps --format '{{.Names}}' | while read name; do
    privileged=$(podman inspect "$name" --format '{{.HostConfig.Privileged}}')
    if [ "$privileged" = "true" ]; then
        echo "⚠️  $name 运行在特权模式!"
    fi
done

# 2. 检查是否有容器以 root 运行
echo "--- Root 容器 ---"
podman ps --format '{{.Names}}' | while read name; do
    user=$(podman inspect "$name" --format '{{.Config.User}}')
    if [ -z "$user" ] || [ "$user" = "root" ] || [ "$user" = "0" ]; then
        echo "⚠️  $name 以 root 用户运行"
    fi
done

# 3. 检查端口暴露
echo "--- 端口暴露 ---"
podman ps --format 'table {{.Names}}\t{{.Ports}}'

# 4. 检查卷挂载
echo "--- 卷挂载 ---"
podman ps --format '{{.Names}}' | while read name; do
    mounts=$(podman inspect "$name" --format '{{range .Mounts}}{{.Source}}:{{.Destination}} {{end}}')
    if echo "$mounts" | grep -qE '^/(etc|var/run|proc|sys|dev)'; then
        echo "⚠️  $name 挂载了敏感宿主机目录: $mounts"
    fi
done

# 5. 检查 Capability
echo "--- 额外 Capability ---"
podman ps --format '{{.Names}}' | while read name; do
    caps=$(podman inspect "$name" --format '{{.HostConfig.CapAdd}}')
    if [ "$caps" != "[]" ] && [ -n "$caps" ]; then
        echo "⚠️  $name 添加了额外 Capability: $caps"
    fi
done

echo "=== 审计完成 ==="

11.9 本章小结

知识点要点
SELinux强制访问控制,容器标签隔离
seccomp系统调用过滤,限制危险操作
Capability细粒度权限,--cap-drop=ALL --cap-add=...
只读文件系统--read-only + --tmpfs
用户命名空间容器内 root ≠ 宿主机 root
镜像扫描Trivy / Grype 检测 CVE
最佳实践非 root 运行、最小权限、最小镜像

下一步


扩展阅读