Docker API 与 2375 端口
2375 是 Docker 守护进程(dockerd)Remote API 的未加密端口。Docker 默认只监听本机 Unix socket,一旦以 TCP 方式打开监听,任何能连通该端口的程序都能调用完整 API:列举与操作全部容器、以特权模式创建新容器并挂载宿主机目录,从而取得宿主机 root。Docker API 自身不做认证与授权,因此 2375 属于最严重的一类暴露面,2376 是它的 TLS 版本。
基本信息
| 服务 | Docker Engine REST API(dockerd) |
|---|---|
| 协议 | TCP(未加密);2376 为 TLS 版本 |
| 默认状态 | 只监听 unix:///var/run/docker.sock,不开放网络端口 |
| 风险等级 | 极高,暴露即等同交出宿主机控制权 |
| 远程访问正解 | SSH 之上用 docker context,或 2376 加客户端证书 |
| 常见排查入口 | ss -lntp 看 2375 是否在听、daemon.json 与 systemd 单元文件 |
远程管理的替代路径
- 本机运维:直接在宿主机上操作,走 Unix socket,不开放任何 TCP 端口
- CI 构建机:用 docker context 走 SSH,凭据复用系统 SSH 密钥与审计
- 必须走网络时:启用 2376 并校验客户端证书,防火墙只放行指定 CI 主机
- 集群编排:交给 Kubernetes 等编排层管理,用本地 socket 或专用通道
- 监控采集:在节点上部署采集器从本地 socket 读取,不把 API 暴露到网络
检测是否暴露未加密 API
检测是否暴露未加密 API
ss -lntp | grep 2375 # 本机确认是否在监听 2375
curl -s http://192.0.2.10:2375/version # 能返回版本 JSON 说明未加密开放
curl -s http://192.0.2.10:2375/containers/json # 能列出容器即已被完全控制
curl -s http://192.0.2.10:2375/info | head -5 # 返回 Docker 版本、驱动与运行参数暴露后的风险路径
| 风险 | 说明 | 后果 |
|---|---|---|
| 容器创建逃逸 | 新建容器并挂载宿主机根目录或 /var/run | 直接读写宿主机文件系统 |
| 凭据窃取 | 读取其他容器环境变量与挂载的密钥文件 | 数据库口令与云访问密钥外泄 |
| 资源滥用 | 以特权模式运行挖矿或代理容器 | 算力被占用,IP 被列入黑名单 |
| 业务破坏 | 停止、删除容器与卷 | 业务中断且数据可能不可恢复 |
关闭 TCP 监听,仅保留本地 socket
关闭 TCP 监听,仅保留本地 socket
// /etc/docker/daemon.json
{
"hosts": ["unix:///var/run/docker.sock"] // 只保留本地 socket,去掉 tcp 监听
}
# 改完执行 systemctl daemon-reload && systemctl restart docker
# 确认 docker info 中的 Server 版本正常,且 ss -lntp 不再有 2375改为 TLS 远程访问(systemd 覆盖示例)
改为 TLS 远程访问(systemd 覆盖示例)
# /etc/systemd/system/docker.service.d/override.conf
[Service]
ExecStart=
ExecStart=/usr/bin/dockerd --tlsverify --tlscacert=/etc/docker/ca.pem --tlscert=/etc/docker/server-cert.pem --tlskey=/etc/docker/server-key.pem -H=fd:// -H=tcp://0.0.0.0:2376
# 客户端:docker --tlsverify --tlscacert=ca.pem --tlscert=cert.pem --tlskey=key.pem -H=tcp://host:2376 ps首选方案:SSH 之上的 docker context
首选方案:SSH 之上的 docker context
docker context create remote --docker 'host=ssh://ops@192.0.2.10' # 复用 SSH 认证与审计
docker context use remote # 切换后命令在远端执行
docker --context remote ps # 单条命令指定上下文
docker context ls # 确认当前上下文,避免误操作本机不要以为内网就安全:内网任意一台失陷主机都能在数秒内通过 2375 取得全部宿主机的 root,横向移动成本极低。
等价暴露:用 socat 或类似工具把 /var/run/docker.sock 转发到 TCP 端口,效果与自己开一个 2375 完全相同,排查时要一并检查这类转发进程与容器。
监听方式与安全性
| 监听方式 | 可达范围 | 安全性 |
|---|---|---|
| unix:///var/run/docker.sock | 仅本机进程与挂载它的容器 | 默认方案,推荐 |
| tcp://127.0.0.1:2375 | 本机任意用户 | 本机提权门槛降低,风险中等 |
| tcp://0.0.0.0:2375 | 任何网络可达的主机 | 不可接受 |
| tcp://0.0.0.0:2376 且校验证书 | 持有客户端证书的主机 | 可接受的远程方案 |
检查与处置清单
- 检查守护进程启动参数与 daemon.json 中是否出现 tcp 监听
- 检查 systemd 单元及其 drop-in 覆盖文件,确认监听没有被别处重新加回
- 检查是否存在把 docker.sock 转发到网络的转发进程
- 检查容器是否挂载了 docker.sock,挂载即按高危处理
- 收敛为只保留本地 socket,重启守护进程后复核监听状态
- 曾对外暴露过的主机按已失陷对待,轮换其上的全部凭据
排查时别只看 2375:Harbor、Portainer、各类面板与自研脚本都可能代理 Docker API,端口号不同但权限等价;确认暴露面时要按进程与挂载关系追,而不是只扫一个端口。
若确需远程管理,先把访问路径换成 SSH 隧道:ssh -L 2375:/var/run/docker.sock 之类的做法并不通用,更稳妥的是用 docker context 走 SSH,把网络暴露面降到零。