如何在 Docker 容器内使用 SSH 密钥?
容器内用 SSH 密钥:仅挂载所需密钥与已核验 known_hosts,只读不防止密钥泄露;构建期可用 BuildKit 的 --mount=type=ssh;切忌 COPY 私钥进镜像。本文给出各方案与安全对比。
核心原则:私钥绝不 COPY 进镜像(镜像层会永久留存,推到仓库就泄露)。运行时用法:仅把所需密钥与已核验known_hosts分别只读挂载;构建期用法(如构建时拉私有 git 仓库):用 BuildKit 的 SSH 挂载 RUN --mount=type=ssh,密钥不落镜像层。
方案一:运行时挂载(最常见)
docker run -it --mount "type=bind,src=$HOME/.ssh/id_ed25519_work,dst=/root/.ssh/id_ed25519,readonly" --mount "type=bind,src=$HOME/.ssh/known_hosts,dst=/root/.ssh/known_hosts,readonly" my-image
:ro只限制写入,容器仍能读取并窃取私钥,不能把只读视为保密保证- 仅挂载任务所需文件;路径属于Docker daemon主机,远端context/Desktop路径行为需单独核对
- 权限要求:ssh 要求私钥 600,挂载保持宿主权限一般没问题
容器内验证:
ssh -T git@github.com
方案二:构建期 SSH 挂载(拉私有依赖)
Dockerfile:
# syntax=docker/dockerfile:1
FROM python:3.12-slim
RUN apt-get update && apt-get install -y git openssh-client
RUN mkdir -p -m 0700 ~/.ssh
COPY verified_known_hosts /root/.ssh/known_hosts
# verified_known_hosts须通过可信渠道核对主机密钥,不能盲信ssh-keyscan结果
RUN --mount=type=ssh pip install git+ssh://git@github.com/org/private-repo.git
构建命令:
eval $(ssh-agent) && ssh-add
DOCKER_BUILDKIT=1 docker build --ssh default .
密钥通过 ssh-agent 转发进构建过程,不会留在任何镜像层——这是与 COPY 私钥的本质区别。
方案三:ssh-agent 套接字转发(docker run)
docker run -it \
-v "$SSH_AUTH_SOCK:/ssh-agent" \
-e SSH_AUTH_SOCK=/ssh-agent \
my-image
容器内 ssh 直接使用宿主机的 agent,私钥文件不直接进入容器,但容器可利用agent提供的认证权限,仍只对可信任务开放并限制密钥。
反面清单
| 做法 | 问题 |
|---|---|
COPY id_rsa /root/.ssh/ |
私钥永久留在镜像层,推仓库=泄露 |
ENV SSH_KEY=... |
docker inspect/history 可见 |
| 密钥放共享卷长期挂着 | 权限管理困难,用完即挂更好 |
常见问题(FAQ)
Q:挂载后 ssh 报 "permissions are too open"?
A:Windows 挂载目录权限模拟问题;只读挂载不能在容器内chmod;应在宿主侧修复权限/ACL并核对UID映射,或按平台文档使用agent转发。
Q:容器内访问 Git 还要配什么?
A:配置可信known_hosts与有权限的SSH公钥/Deploy Key;user.name/email只决定提交元数据,不决定SSH登录身份。
Q:K8s 里怎么用?
A:私钥放 Secret,volume 挂载(defaultMode 0600);构建期可用BuildKit SSH挂载,不应假定Kaniko提供等价机制。Secret的编码不是加密,还需RBAC和密钥生命周期管理。