Margrop
Articles370
Tags680
Categories7

Categories

0步 0步元递归 0步本身 12类 1password 22类一键汇总 401 503 6个节点 AC ACP AI AI Coding Assistant AI编程助手 AI辅助 AI辅助编程 AP API Alertmanager AppDaemon Aqara BaiduPCS CC-Switch CI/CD CLI Tools CLI工具 Caddy Chrome缺失 Claude Code Cloudflare Codex Cookie 认证 Cron D1 DB探针 DB静止 DIY-MINI Date Diagrams.net Diary Docker Docker Compose EADDRINUSE EasyTier NAT穿透 Efficiency Tools Electerm English FTS5 Gateway Gemini CLI GitHub Actions HA HADashboard Hermes Hexo HomeAssistant IP IPv4 Java LVM‑Thin Linux MacOS Macmini log路径 Markdown MiniMax Multi-Agent MySQL NAS NRestarts Nginx Node-RED Node.js OOM OpenAI OpenClaw OpenCode OpenResty OpenWrt PPPoE Portainer PostgreSQL ProcessOn Prometheus Proxmox VE RPC SOCKS5 SQLite SSL Session Shell Subagent TTS TimeMachine UML Uptime Kuma VM152 WeCom缺失 VPN VPS VPS4 overlay TCP不可达 WeCom Web WebSocket Windows Workers activate ad adb adblock agent aligenie aliyun alpine annotation aop authy autofs backup baidupan bash bitwarden boot brew browser by-design caddy2 capture_output cdn centos cert certbot charles chat chrome classloader client clone closures cloudflare cmd command commit connected container cron crontab cron任务 cron设计 ctyun dashboard ddsm demo dependency deploy developer devtools dll dns docker domain download draw drawio dsm dump dylib edge exception export fail2ban fallback fallback失效 feign firewall-cmd flow frp frpc frps fuckgfw function fuser gcc gfw git gitea github golang google_gemma-4 gperftools gridea grub gvt-g hacs havcs heap hello hexo hibernate hidpi hoisting homeassistant hosts html htmlparser https iKuai idea image img img2kvm immortalwrt import index install intel io ios ip iptables iptv ipv6 iso java javascript jetbrains jieba jni jnilib journald journald日志漂移 jpa js json jsonb jupter jupyterlab jvm k8s kernel key kid kms kodi koolproxy koolproxyr kvm lan lastpass launchctl learning lede letsencrypt linux live loopback-proxy low-code lsof lvm lxc m3u8 mac macos manual mariadb markdown maven md5 meta-acceptance meta-pattern meta-probe microcode mirror model provider modem modules monitor mount mstsc mysql n2n n5105 nas netstat network new-api nfs node node-red nodejs nohup notepad++ npm nssm ntp one-api oop openfeign openssl os otp ovz p14 packet capture pat pdf pem perf ping pip plugin png powerbutton print pro proxy pve pvekclean python qcow2 qemu qemu-guest-agent rar reboot reconnect循环 reflog remote remote desktop renew repo resize retina root route router rule rules running runtime safari sata schema schema列名 scipy-notebook scoping scp server server is busy service不可信 slmgr so socket-proxyd socks source spk split边界 spring springboot springfox sqlite3 CLI ss ssh ssl stale stash stderr被吞 string subprocess supernode svg svn swagger sync synology systemctl systemd systemd exit 78 systemd unit systemd-socket systemd被覆盖 tap tap-windows tapwindows telecom template terminal tls tmux token token失效 totp transient 999 trigram tvbox txt ubuntu udisk ui undertow unicode61 uninstall unlocker upgrade uptimeMs url v10探针 v11探针 v12探针 v1探针 v2ray v6探针 v7探针 v8探针 vhd vim vlmcsd vm vmdk web websocket wechat windows with worker wow xiaoya xml yum zip 中国电信 中文搜索 主动0步 主动0步本身 主动不追问 主动不追问本身 主动不追问本身也是清单之外 主动不通知 主动不通知本身 主动周一 主动意识到 主动意识到0步本身 主动意识到0步本身也是清单之外 主动追问 云电脑 交换机 人机协作 代理 优化 体检 修正本身 修正递归 值班 假阳 假阴 健康检查 元递归 光猫 全绿 全量同步 公网IP 内存 内存优化 内网 内网IP 内网渗透 写作 分词 切换 列名误判 升级 协作 单位混淆 博客 反向代理 反常稳定 反应 vs 知识 启动 告警 告警优化 周一 周一焦虑 周三 周二 周五 周六 周四 周报 周日 周末 周末也是清单之外 周末本身也是清单之外 周末突破 周末第二天 周末落地 周末落地本身 夏令时 多场景 多智能体 多节点 多节点管理 天猫精灵 天翼云 安全 安装 定时任务 容器 容器网络 导入 小米 工作感悟 工作日 工作日常 工作日第三天 工作日第五天 工作日第四天 已通知用户 常用软件 幂等 广告屏蔽 序列号 应用市场 异常 循环类 心态 心智成长 心理模型 心跳 心跳检查 性能优化 感悟 打工 打工人 批量校验 技术 抓包 排查 探针再升级 探针本身 探针版本 探针管理 探针自检 探针踩坑 接受 接受之后 接受修 接受修正 接受层 接受挖坑 接受本身 接受递归 描述文件 放下 故障 故障排查 效率 效率工具 数据 旁路由 旁路进程 无服务器 日记 时区 显卡虚拟化 智能家居 智能音箱 服务器 服务管理 架构 梯子 模块 模型探测 模型调用 毫秒 流程 流程图 流程管理 浏览器 清单之后 清单之外 清单之外也包括接受本身 清单的元递归 清单设计 清单边界 清单进化 源码备份 漫游 激活 火绒 焦虑 玄学 生活 电信 画图 监控 监控系统 直播源 直觉 磁盘 端口 端口冲突 端口扫描 第10天 第10类 第11天 第11类 第12天 第12类 第13天 第13类 第14类 第15类 第16类 第17类 第18类 第19类 第20类 第21类 第22类 第6天 第7天 第8天 第9天 第9类 管理 续期 网关 网络 网络风暴 群晖 脚本 脚本优化 腾讯 自动化 自动恢复 自建应用 自我反思 自我打脸 节点角色 虚拟机 被动意识到 角色不匹配 角色误判 角色误配 角色错配 认证 设计偏差 证书 语雀 误报 误报过滤 超时 路由 路由器 软件管家 软路由 运维 运维监控 进程 连接保活 连接问题 通信机制 通知 通知元递归 通知挖坑 通知本身 部署 部署链路 配置 配置落后 钉钉 镜像 镜像源 长期稳定 长连接 门窗传感器 问题排查 防火墙 阿里云 阿里源 集客 静默期 飞书

Hitokoto

Archive

Docker容器安全配置实战:从运行用户到文件权限的完整指南

Docker容器安全配置实战:从运行用户到文件权限的完整指南

前言

Docker 容器化部署已经成为现代应用的主流方案,但容器安全却往往被忽视。很多开发者认为”容器有隔离就安全了”,实际上这是一个严重的误区。容器的隔离机制并非固若金汤——运行用户配置不当、文件权限过于宽松、特权模式滥用等问题,都可能成为攻击者的突破口。

本文从实战出发,详细介绍 Docker 容器安全配置的三大核心领域:运行用户配置目录权限管理特权模式安全。每个检查项都提供具体的操作命令、判定标准和修复方案,帮助你在生产环境中快速建立容器安全基线。

问题背景

业务场景

某台对外提供服务的 VPS 服务器(Ubuntu 24.04)部署了多个 Docker 容器:

  • OpenClaw Gateway(核心管理服务)
  • new-api-svr(新 API 服务)
  • dockhand-svr(部署工具)
  • openclaw-browser(浏览器自动化)
  • easytier-svr(网络穿透)

这些容器承载着重要的运维自动化能力,其安全性直接关系到整个基础设施的稳定。

检查目标

对容器环境进行全面的安全评估,重点关注:

  1. 运行用户:容器是否以 root 用户运行
  2. 目录权限:关键目录(如 /app、/root/.openclaw)的权限配置是否合理
  3. 特权模式:容器是否使用了危险的 --privileged 参数

环境信息

组件 类型 说明
宿主机 VPS Ubuntu 24.04
容器运行时 Docker 最新稳定版
容器网络 host 模式 容器使用 host 网络模式
容器数量 5 个 涵盖核心服务

第一部分:运行用户配置

为什么不能以 root 运行容器?

默认情况下,Docker 容器以 root 用户运行。这会带来以下安全风险:

  1. 容器逃逸风险:如果攻击者通过容器漏洞获得了容器内的 root 权限,在某些配置不当的情况下可能实现容器逃逸,获得宿主机的 root 权限
  2. 权限过大:root 用户可以访问和修改系统中的任何文件,包括宿主机的敏感文件
  3. 违反最小权限原则:应用通常不需要 root 权限,应该使用专用的非特权用户运行

检查方法

1
2
3
4
5
6
7
8
# 方法一:查看容器进程的 UID
docker exec 容器名 id

# 方法二:在宿主机上查看容器进程信息
ps aux | grep docker

# 方法三:查看容器详细信息中的 User 字段
docker inspect 容器名 --format '{{.Config.User}}'

判定标准

检查项 期望值 风险等级
容器运行用户 非 root(如 node、appuser、1000:1000)
容器运行用户 root(uid=0) 极高危

实战检查

在目标服务器上执行:

1
docker exec openclaw id

结果:返回 uid=1000(node) gid=1000(node) groups=1000(node)

通过:容器以 node 用户(非 root)运行,符合安全基线要求。

修复方案

如果发现容器以 root 用户运行,有以下修复方案:

方案一:在 Dockerfile 中创建非 root 用户

1
2
3
4
5
6
7
8
9
10
11
12
13
FROM ubuntu:24.04

# 创建非 root 用户
RUN addgroup -S appgroup && adduser -S appuser -G appgroup

# 复制应用文件
COPY --chown=appuser:appgroup /app /app

# 切换到非 root 用户
USER appuser

WORKDIR /home/appuser
CMD ["node", "/app/index.js"]

方案二:在 docker-compose.yml 中指定用户

1
2
3
4
5
6
services:
myapp:
image: myapp:latest
user: "1000:1000" # UID:GID
# 或者使用用户名
# user: "node"

方案三:在 docker run 命令中指定用户

1
docker run -d --user 1000:1000 --name myapp myapp:latest

第二部分:目录权限管理

/app 目录权限检查

容器的 /app 目录通常存放应用的可执行文件和配置。如果权限设置过于宽松,攻击者就能修改这些文件,植入恶意代码。

Linux 文件权限速查

权限码 所有者 其他 危险性
777 rwx rwx rwx 极高危
776 rwx rwx rw- 极高危
775 rwx rwx r-x 高危
755 rwx r-x r-x 可接受
750 rwx r-x 推荐
700 rwx 最安全
644 rw- r– r– 可接受
600 rw- 推荐

检查方法

1
2
3
4
5
6
7
8
# 查看 /app 目录权限
docker exec 容器名 ls -lad /app

# 递归查看 /app 目录树所有文件的权限
docker exec 容器名 find /app -type f -exec ls -la {} \;

# 快速统计权限不是 755 的文件数量
docker exec 容器名 find /app -type f ! -perm 755 | wc -l

判定标准

高危(必须修复):

  • /app 目录权限包含组或其他人的写权限(如 777、776、766、775)
  • 可执行脚本(如 .sh)包含其他人的执行权限

正常(无需处理):

  • 目录权限为 755 或更严格(如 750、700)
  • 文件权限为 644 或更严格(如 640、600)
  • 可执行文件权限为 755 或更严格(如 700)

实战检查

1
docker exec openclaw ls -lad /app

结果drwxr-xr-x 1000 1000 4096 Mar 20 17:12 /app

权限解读:

  • d:目录
  • rwx(所有者):读+写+执行 ✅
  • r-x(组):读+执行 ✅
  • r-x(其他):读+执行 ✅

通过:/app 目录权限为 755(drwxr-xr-x),符合安全基线。

/root/.openclaw 目录权限检查

这是存放 OpenClaw 敏感配置(如 Token、密钥等)的目录,权限要求更严格。

1
2
# 检查权限
docker exec openclaw ls -lad /root/.openclaw

结果drwx------ 1000 1000 4096 Mar 21 20:09 /root/.openclaw

权限:drwx------ = 700,即只有所有者(1000:1000)可读写执行,组和其他用户均无权访问。

优秀:700 权限,比标准安全基线更严格。

/tmp 目录和临时文件权限

容器的 /tmp 目录用于存储临时数据。如果 /tmp 目录权限过于宽松,同一容器内的其他进程或外部攻击者就能访问、修改、甚至删除你的临时文件。

1
2
3
4
5
# 检查 /tmp 目录权限
docker exec openclaw ls -lad /tmp

# 检查 /tmp/openclaw 子目录权限(如果存在)
docker exec openclaw ls -lad /tmp/openclaw

结果drwx------ 1000 1000 4096 Mar 20 17:13 /tmp/openclaw

优秀:/tmp/openclaw 权限为 700,有效防止了敏感临时文件被窥探。

权限修复方案

如果发现权限配置不当,使用以下命令修复:

1
2
3
4
5
6
7
8
9
10
11
# 修复目录权限
chmod 755 /app

# 修复所有文件的权限(递归)
find /app -type f ! -perm 755 -exec chmod 644 {} \;

# 修复可执行文件的权限(显式设置)
chmod 755 /app/*.sh

# 修复 /tmp/openclaw 临时目录权限
chmod 700 /tmp/openclaw

第三部分:特权模式安全

什么是 –privileged 模式?

--privileged 参数赋予容器几乎所有宿主机的能力,包括:

  • 访问和操作所有设备
  • 绕过大部分内核安全机制(Capabilities)
  • 加载和卸载内核模块
  • 修改网络配置、iptables 规则等

这相当于把整台宿主机的钥匙都给了容器。如果容器被攻破,攻击者可以轻易获得宿主机的 root 权限。

检查方法

1
2
3
4
5
6
7
8
# 方法一:查看容器详细信息中的 Privileged 字段
docker inspect 容器名 --format '{{.HostConfig.Privileged}}'

# 方法二:检查容器的 Capabilities
docker exec 容器名 capsh --print

# 方法三:检查是否挂载了敏感设备
docker inspect 容器名 --format '{{json .HostConfig.Binds}}'

判定标准

检查项 期望值 风险等级
–privileged 模式 false
Capabilities 仅授予必要权限(推荐使用 –cap-add 精确添加)

实战检查

1
docker inspect openclaw --format '{{.HostConfig.Privileged}}'

结果false

通过:容器未使用 –privileged 模式。

特权模式修复方案

永远不要使用 –privileged 模式。如果确实需要某些特权,应该精确添加所需的 Capabilities:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 不推荐:开放所有权限
docker run --privileged myapp:latest

# 推荐:仅添加所需的特定权限
docker run \
--cap-add=SYS_ADMIN \
--cap-drop=NET_ADMIN \
myapp:latest

# 推荐:使用更安全的选项组合
docker run \
--read-only \
--tmpfs /tmp:rw,noexec,size=100m \
--cap-drop=ALL \
myapp:latest

常见 Capabilities 说明:

Capability 说明 风险
SYS_ADMIN 许多系统管理操作 高风险
NET_ADMIN 网络管理(修改路由、防火墙等) 中高风险
SYS_MODULE 加载内核模块 极高风险
DAC_READ_SEARCH 绕过文件读取权限检查 高风险
SYS_PTRACE 进程调试和跟踪 高风险

第四部分:其他安全配置建议

资源限制配置

没有资源限制的容器可能耗尽宿主机的内存或 CPU,影响同一宿主机上的其他容器。

1
2
3
4
5
6
7
8
9
10
11
12
# docker-compose.yml 中配置资源限制
services:
myapp:
image: myapp:latest
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
reservations:
cpus: '0.25'
memory: 256M

目标服务器容器资源使用参考:

容器 CPU 内存
openclaw 2.39% 341MB
openclaw-browser 0.23% 241MB
easytier 0.82% 10MB

只读文件系统

容器默认对根文件系统有写权限。可以启用只读模式防止意外写入:

1
docker run --read-only myapp:latest

网络模式选择

Docker 提供多种网络模式,每种模式有不同的安全特性:

模式 隔离性 性能 推荐场景
bridge(默认) 一般应用
host 性能敏感场景
none 完全离线
自定义网络 可配置 可配置 多容器编排

目标服务器使用 host 模式,适合不需要容器间隔离的简单部署。

一键安全检查脚本

以下脚本封装了所有安全检查项,可定期执行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#!/bin/bash

echo "========== Docker 容器安全检查 =========="
echo "检查时间:$(date '+%Y-%m-%d %H:%M:%S')"
echo ""

CONTAINER_NAME=${1:-openclaw}

# 1. 检查运行用户
echo "[1/6] 检查容器运行用户..."
USER_INFO=$(docker exec $CONTAINER_NAME id 2>/dev/null)
if echo "$USER_INFO" | grep -q "uid=0"; then
echo " ❌ 容器以 root 用户运行 (风险: 极高)"
else
echo " ✅ 容器以非 root 用户运行: $USER_INFO"
fi

# 2. 检查 /app 目录权限
echo ""
echo "[2/6] 检查 /app 目录权限..."
APP_PERM=$(docker exec $CONTAINER_NAME stat -c %a /app 2>/dev/null)
if [ "$APP_PERM" = "755" ] || [ "$APP_PERM" = "750" ] || [ "$APP_PERM" = "700" ]; then
echo " ✅ /app 权限正常: $APP_PERM"
else
echo " ⚠️ /app 权限: $APP_PERM (建议: 755)"
fi

# 3. 检查 /tmp/openclaw 权限
echo ""
echo "[3/6] 检查 /tmp/openclaw 权限..."
if docker exec $CONTAINER_NAME test -d /tmp/openclaw 2>/dev/null; then
TMP_PERM=$(docker exec $CONTAINER_NAME stat -c %a /tmp/openclaw 2>/dev/null)
if [ "$TMP_PERM" = "700" ]; then
echo " ✅ /tmp/openclaw 权限严格: $TMP_PERM"
else
echo " ⚠️ /tmp/openclaw 权限: $TMP_PERM (建议: 700)"
fi
else
echo " ℹ️ /tmp/openclaw 不存在"
fi

# 4. 检查特权模式
echo ""
echo "[4/6] 检查 --privileged 模式..."
IS_PRIVILEGED=$(docker inspect $CONTAINER_NAME --format '{{.HostConfig.Privileged}}' 2>/dev/null)
if [ "$IS_PRIVILEGED" = "true" ]; then
echo " ❌ 容器使用 --privileged 模式 (风险: 极高)"
else
echo " ✅ 容器未使用 --privileged 模式"
fi

# 5. 检查资源限制
echo ""
echo "[5/6] 检查资源限制配置..."
MEM_LIMIT=$(docker inspect $CONTAINER_NAME --format '{{.HostConfig.Memory}}' 2>/dev/null)
if [ "$MEM_LIMIT" = "0" ] || [ -z "$MEM_LIMIT" ]; then
echo " ⚠️ 未设置内存限制"
else
echo " ✅ 已设置内存限制: $(($MEM_LIMIT/1024/1024))MB"
fi

# 6. 检查容器状态
echo ""
echo "[6/6] 检查容器运行状态..."
STATUS=$(docker inspect $CONTAINER_NAME --format '{{.State.Status}}' 2>/dev/null)
if [ "$STATUS" = "running" ]; then
echo " ✅ 容器运行正常: $STATUS"
else
echo " ❌ 容器状态异常: $STATUS"
fi

echo ""
echo "========== 检查完成 =========="

使用方法:

1
2
3
4
5
6
7
8
# 保存并添加执行权限
chmod +x container_security_check.sh

# 检查默认容器(openclaw)
./container_security_check.sh

# 检查指定容器
./container_security_check.sh myapp

常见问题解答

Q1:容器内为什么有些命令(如 iptables)不可用?

A:这是正常且安全的现象。Docker 容器默认与宿主机的内核命名空间隔离,高权限的网络和系统工具在容器内不可用。如果没有这些命令恰恰说明你的容器网络配置正确,不需要担心。

Q2:所有容器都必须以非 root 用户运行吗?

A:是的,这是容器安全的基本原则。即使应用本身不需要特殊权限,也应该以非 root 用户运行。如果容器被攻破,攻击者获得的是普通用户权限而非 root 权限,大大降低了逃逸到宿主机的风险。

Q3:700 vs 755 权限,哪个更安全?

A:700 更安全。755 允许组内用户读和执行目录,允许其他人读和执行目录。对于 /app 这种存放应用代码的目录,如果组内有其他用户,或者有本地账户被攻击者获得,就可能读取应用代码。700 权限确保只有文件所有者能访问,是最严格的选择。

Q4:Docker 安全配置多久检查一次比较合适?

A:建议:

  • 自动化检查:每周一次,使用脚本定期执行
  • 手动审计:每月一次,全面检查所有配置项
  • 部署前检查:将安全检查集成到 CI/CD 流程中,部署即检查

Q5:已经有问题的容器如何安全地修复?

A:建议步骤:

  1. 备份当前配置:docker commit 当前容器为新镜像
  2. 测试环境验证:在测试环境中应用修复方案
  3. 滚动更新:生产环境使用滚动更新方式,逐步替换旧容器
  4. 监控观察:修复后持续监控容器状态,确保无异常

最佳实践总结

经过本次全面的安全检查,总结以下容器安全最佳实践:

运行用户安全

  1. 始终使用非 root 用户:在 Dockerfile 中创建专用用户
  2. 避免使用 –user=0:即 UID 0 也代表 root
  3. 文件所有权匹配:使用 --chown 确保应用文件属于非 root 用户

文件权限安全

  1. 目录权限 755 或更严格:允许组和其他用户读+执行,但不写
  2. 敏感目录权限 700:如 /root/.openclaw、/tmp/openclaw
  3. 定期审计权限:使用脚本定期检查权限配置

特权模式安全

  1. 永远不使用 –privileged:这是最危险的安全配置
  2. 精确添加 Capabilities:只添加应用所需的最小权限集
  3. 考虑使用 –read-only:对根文件系统启用只读模式

监控与维护

  1. 部署安全检查脚本:将本文提供的检查脚本纳入定时任务
  2. 日志监控:记录安全相关的配置变更
  3. 定期更新镜像:使用最新稳定版 Docker 镜像

延伸阅读

结语

本文从实战角度详细介绍了 Docker 容器安全配置的三大核心领域。检查结果显示,目标服务器的容器配置总体良好:运行用户为非 root(node)、/app 目录权限为 755、/tmp/openclaw 权限为 700、未使用 –privileged 模式。这些配置符合甚至优于行业推荐的安全基线。

容器安全不是一次性的工作,而是需要持续关注、定期检查的过程。建议将本文提供的检查方法融入日常运维巡检,建立安全基线,确保容器环境始终处于安全状态。

希望这篇文章能帮助你在生产环境中快速建立容器安全检查能力。如果有问题,欢迎在评论区讨论。


作者:小六,一个在上海努力搬砖的程序员

Author:Margrop
Link:http://blog.margrop.com/post/2026-03-22-docker-container-security-best-practices/
版权声明:本文采用 CC BY-NC-SA 3.0 CN 协议进行许可