强曰为道

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

第 12 章:系统加固

第 12 章:系统加固

高级系统加固技术:最小化安装、只读根、SecComp 和命名空间隔离。

12.1 最小化安装原则

最小化系统裁剪

# 1. 从最小 rootfs 开始
wget https://dl-cdn.alpinelinux.org/alpine/v3.20/releases/x86_64/alpine-minirootfs-3.20.3-x86_64.tar.gz

# 2. 解压到目标目录
mkdir -p /target/rootfs
tar -xzf alpine-minirootfs-3.20.3-x86_64.tar.gz -C /target/rootfs

# 3. chroot 进入
chroot /target/rootfs /bin/sh

# 4. 最小化安装
apk update
apk add --no-cache \
    busybox \
    musl \
    alpine-baselayout \
    alpine-keys \
    apk-tools \
    libc-utils

# 5. 清理不需要的文件
rm -rf /var/cache/apk/*
rm -rf /tmp/*
rm -rf /root/.ash_history

# 6. 查看最终大小
du -sh /target/rootfs
# 通常可压缩到 ~3MB

服务最小化

# 查看当前运行的服务
rc-status -a

# 禁用所有不需要的服务
for svc in $(rc-status default 2>/dev/null | awk '{print $1}'); do
    case "$svc" in
        sshd|networking|cron) ;;  # 保留
        *) rc-update del "$svc" default 2>/dev/null ;;
    esac
done

# 最小化开机服务列表
# 只保留: networking, sshd, crond

包审计

# 列出所有已安装包
apk info | sort

# 查找不需要的包
# 移除开发工具(生产环境)
apk del gcc g++ make cmake

# 移除调试工具
apk del gdb strace ltrace

# 移除文档
apk del man-pages linux-docs

# 查看包的依赖关系
apk info -R nginx  # nginx 的依赖
apk info -r nginx  # 依赖 nginx 的包

12.2 只读根文件系统

配置 tmpfs 运行时

# /etc/fstab 只读配置
cat > /etc/fstab << 'EOF'
# 设备              挂载点   类型    选项                          dump fsck
/dev/sda1           /        ext4    ro,noatime,errors=remount-ro   0 1
/dev/sda2           /boot    ext4    ro,noatime                     0 2
tmpfs               /tmp     tmpfs   defaults,noexec,nosuid,nodev,size=2G 0 0
tmpfs               /run     tmpfs   defaults,noexec,nosuid,nodev,mode=0755 0 0
tmpfs               /var/log tmpfs   defaults,noexec,nosuid,nodev,size=512M 0 0
tmpfs               /var/tmp tmpfs   defaults,noexec,nosuid,nodev,size=512M 0 0
EOF

# 为需要写入的目录创建绑定挂载
# /etc 中需要写入的文件
mkdir -p /overlay/etc
mount -t tmpfs tmpfs /overlay/etc
# 将需要写入的配置文件软链接到 tmpfs

使用 overlayfs 实现可写层

# overlayfs 脚本
cat > /usr/local/bin/overlay-mount << 'SCRIPT'
#!/bin/sh
# 使用 overlayfs 包装只读根
LOWER="/target/rootfs"
UPPER="/tmp/overlay-upper"
WORK="/tmp/overlay-work"
MERGED="/target/merged"

mkdir -p "$UPPER" "$WORK" "$MERGED"
mount -t overlay overlay \
    -o lowerdir=$LOWER,upperdir=$UPPER,workdir=$WORK \
    "$MERGED"
SCRIPT
chmod +x /usr/local/bin/overlay-mount

12.3 Seccomp 系统调用过滤

Seccomp 基础

# 检查内核 Seccomp 支持
grep -i seccomp /boot/config-$(uname -r) 2>/dev/null || \
zgrep -i seccomp /proc/config.gz 2>/dev/null

# 安装 seccomp 工具
apk add libseccomp libseccomp-dev

# 使用 seccomp-tools 分析程序的系统调用
apk add strace
strace -c ./myprogram
# 输出系统调用统计,用于确定允许列表

Seccomp 配置文件

{
    "defaultAction": "SCMP_ACT_ERRNO",
    "defaultErrnoRet": 1,
    "architectures": [
        "SCMP_ARCH_X86_64"
    ],
    "syscalls": [
        {
            "names": [
                "read", "write", "open", "close", "stat",
                "fstat", "lstat", "poll", "lseek", "mmap",
                "mprotect", "munmap", "brk", "rt_sigaction",
                "rt_sigprocmask", "ioctl", "access", "pipe",
                "select", "sched_yield", "mremap", "msync",
                "mincore", "madvise", "dup", "dup2", "nanosleep",
                "getpid", "socket", "connect", "accept",
                "sendto", "recvfrom", "sendmsg", "recvmsg",
                "bind", "listen", "getsockname", "getpeername",
                "clone", "fork", "vfork", "execve", "exit",
                "wait4", "kill", "uname", "fcntl", "flock",
                "fsync", "fdatasync", "truncate", "ftruncate",
                "getdents", "getcwd", "chdir", "mkdir", "rmdir",
                "creat", "unlink", "readlink", "chmod", "chown",
                "gettimeofday", "getuid", "getgid", "geteuid",
                "getegid", "getppid", "getpgrp", "set_tid_address",
                "exit_group", "epoll_wait", "epoll_ctl", "tgkill",
                "openat", "mkdirat", "newfstatat", "unlinkat",
                "set_robust_list", "get_robust_list", "epoll_create1",
                "pipe2", "dup3", "epoll_create", "getrandom",
                "statx", "rseq", "clone3", "close_range",
                "readlinkat", "faccessat", "fchmodat", "fchownat"
            ],
            "action": "SCMP_ACT_ALLOW"
        }
    ]
}

Docker 中使用 Seccomp

# 运行容器时指定 seccomp 配置
docker run --security-opt seccomp=seccomp-profile.json \
    myapp:latest

# Docker 默认 seccomp 配置
# 禁止约 44 个危险系统调用
# 包括: reboot, mount, unmount, kexec_load 等

# 查看默认配置
docker info --format '{{.SecurityOptions}}'

12.4 Linux 命名空间隔离

命名空间类型

命名空间隔离内容系统调用标志
PID进程 IDCLONE_NEWPID
NET网络栈CLONE_NEWNET
MNT挂载点CLONE_NEWNS
UTS主机名CLONE_NEWUTS
IPC进程间通信CLONE_NEWIPC
USER用户/组CLONE_NEWUSER
CGROUPcgroup 根目录CLONE_NEWCGROUP
TIME系统时钟CLONE_NEWTIME

使用 unshare 创建命名空间

# 创建隔离环境
# PID + 网络 + 挂载 + 主机名隔离
unshare --pid --net --mount --uts --fork /bin/sh

# 在新命名空间中
hostname isolated-host
mount -t proc proc /proc
ps aux  # 只能看到当前命名空间的进程

# 使用 nsenter 进入现有命名空间
# 获取目标进程的 PID
PID=$(docker inspect -f '{{.State.Pid}}' container_name)

# 进入该容器的命名空间
nsenter --target $PID --pid --net --mount /bin/sh

使用 bubblewrap 沙箱

# 安装 bubblewrap
apk add bubblewrap

# 创建沙箱环境
bwrap \
    --ro-bind / / \
    --dev /dev \
    --proc /proc \
    --tmpfs /tmp \
    --bind /home/user/sandbox-home /home/user \
    --unshare-net \
    --unshare-pid \
    --die-with-parent \
    /bin/sh

# 程序沙箱化
bwrap \
    --ro-bind /usr /usr \
    --ro-bind /lib /lib \
    --ro-bind /lib64 /lib64 \
    --symlink usr/bin /bin \
    --symlink usr/sbin /sbin \
    --proc /proc \
    --dev /dev \
    --tmpfs /tmp \
    --unshare-all \
    --die-with-parent \
    --as-pid-1 \
    /usr/bin/myapp

12.5 cgroups 资源限制

cgroups v2 配置

# 检查 cgroups 版本
stat -fc %T /sys/fs/cgroup/
# cgroup2fs 表示 v2

# 创建 cgroup
mkdir /sys/fs/cgroup/mygroup

# 限制 CPU(最多使用 50% 单核)
echo "50000 100000" > /sys/fs/cgroup/mygroup/cpu.max

# 限制内存(最多 256MB)
echo "268435456" > /sys/fs/cgroup/mygroup/memory.max

# 限制 IO(读取 10MB/s)
echo "8:0 rbps=10485760" > /sys/fs/cgroup/mygroup/io.max

# 将进程加入 cgroup
echo $PID > /sys/fs/cgroup/mygroup/cgroup.procs

# 查看使用情况
cat /sys/fs/cgroup/mygroup/cpu.stat
cat /sys/fs/cgroup/mygroup/memory.current

使用 systemd-run (替代方案)

# 在 Alpine 上使用 cgroup-tools
apk add cgroup-tools

# 创建和管理 cgroup
cgcreate -g cpu,memory:/mygroup
cgset -r cpu.cfs_quota_us=50000 mygroup
cgset -r memory.limit_in_bytes=256M mygroup
cgexec -g cpu,memory:/mygroup mycommand

12.6 网络命名空间隔离

# 创建网络命名空间
ip netns add isolated

# 在隔离命名空间中运行程序
ip netns exec isolated ip addr show
ip netns exec isolated ping 8.8.8.8  # 失败:无网络

# 创建 veth 对连接命名空间
ip link add veth0 type veth peer name veth1
ip link set veth1 netns isolated

# 配置 IP
ip addr add 10.200.0.1/24 dev veth0
ip link set veth0 up

ip netns exec isolated ip addr add 10.200.0.2/24 dev veth1
ip netns exec isolated ip link set veth1 up
ip netns exec isolated ip link set lo up

# 测试连通性
ip netns exec isolated ping 10.200.0.1

# 允许隔离命名空间访问外网
echo 1 > /proc/sys/net/ipv4/ip_forward
iptables -t nat -A POSTROUTING -s 10.200.0.0/24 -o eth0 -j MASQUERADE
ip netns exec isolated ip route add default via 10.200.0.1

12.7 能力(Capabilities)管理

# 查看程序的能力
apk add libcap
getcap /usr/bin/ping

# 删除不必要的能力
# Docker 容器默认去掉大部分能力
# --cap-drop ALL --cap-add <仅需要的能力>

# 常用能力说明
# CAP_NET_BIND_SERVICE - 绑定 <1024 端口
# CAP_NET_RAW          - 使用原始套接字
# CAP_SYS_ADMIN        - 管理操作(危险)
# CAP_SYS_PTRACE       - 跟踪进程
# CAP_DAC_OVERRIDE     - 覆盖文件权限

# 程序能力设置
setcap 'cap_net_bind_service=+ep' /usr/local/bin/myapp
# 现在 myapp 可以绑定 80 端口而无需 root

# 删除能力
setcap -r /usr/local/bin/myapp

12.8 内核安全参数

cat >> /etc/sysctl.conf << 'EOF'
# ====== 内核安全加固 ======

# 禁用 SysRq 键
kernel.sysrq = 0

# 禁止 core dump
fs.suid_dumpable = 0

# 限制 dmesg 访问
kernel.dmesg_restrict = 1

# 限制 kernel 指针泄露
kernel.kptr_restrict = 2

# 启用地址空间随机化
kernel.randomize_va_space = 2

# 限制 perf_event 访问
kernel.perf_event_paranoid = 3

# 禁用 unprivileged BPF
kernel.unprivileged_bpf_disabled = 1

# 禁用 user namespaces(可选,影响某些应用)
# kernel.unprivileged_userns_clone = 0

# PID 最大值限制
kernel.pid_max = 32768

# 文件描述符限制
fs.file-max = 2097152
fs.nr_open = 1048576

# 网络安全
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.tcp_syncookies = 1
net.ipv6.conf.all.accept_redirects = 0
EOF

sysctl -p

12.9 安全加固自动化脚本

cat > /usr/local/bin/harden-alpine << 'SCRIPT'
#!/bin/sh
set -e

echo "=== Alpine Linux Hardening Script ==="

echo "[1/8] Updating system..."
apk update && apk upgrade

echo "[2/8] Removing unnecessary packages..."
apk del gcc g++ make gdb strace 2>/dev/null || true

echo "[3/8] Setting file permissions..."
chmod 600 /etc/shadow
chmod 600 /etc/gshadow
chmod 644 /etc/passwd
chmod 644 /etc/group
chmod 700 /root
chmod 600 /etc/ssh/sshd_config

echo "[4/8] Configuring SSH..."
cat > /etc/ssh/sshd_config << 'SSHEOF'
Port 22
PermitRootLogin prohibit-password
PasswordAuthentication no
PubkeyAuthentication yes
MaxAuthTries 3
X11Forwarding no
AllowAgentForwarding no
ClientAliveInterval 300
ClientAliveCountMax 2
SSHEOF

echo "[5/8] Setting sysctl parameters..."
cat >> /etc/sysctl.conf << 'SYSEOF'
kernel.sysrq = 0
kernel.dmesg_restrict = 1
kernel.kptr_restrict = 2
kernel.randomize_va_space = 2
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
SYSEOF
sysctl -p

echo "[6/8] Configuring firewall..."
iptables -F
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
/etc/init.d/iptables save

echo "[7/8] Setting up tmpfs..."
echo "tmpfs /tmp tmpfs defaults,noexec,nosuid,nodev,size=2G 0 0" >> /etc/fstab

echo "[8/8] Hardening complete!"
echo "Please reboot to apply all changes."
SCRIPT
chmod +x /usr/local/bin/harden-alpine

12.10 本章小结

加固技术效果难度
最小化安装减少攻击面
只读根防止持久化攻击⭐⭐
Seccomp限制系统调用⭐⭐⭐
命名空间进程隔离⭐⭐⭐
cgroups资源限制⭐⭐
Capabilities细粒度权限⭐⭐
内核参数全局安全策略⭐⭐

扩展阅读


上一章第 11 章:开发环境 下一章第 13 章:故障排查