Dockerfile中向ssh-agent添加私钥失败问题求助
我来帮你排查下这个问题,看起来你遇到了几个Docker构建中使用SSH密钥的常见坑,咱们一步步解决:
首先,先看你构建命令里的明显错误
你在传递ssh_pub_key的时候用了$(cat ~/.ssh/id_rsa),这会把私钥内容写到公钥文件里,完全不对!应该改成公钥文件的路径:
docker build -t ng-2-docker/client --build-arg ssh_prv_key="$(cat ~/.ssh/id_rsa)" --build-arg ssh_pub_key="$(cat ~/.ssh/id_rsa.pub)" .
然后是Dockerfile里的核心问题
1. ARG的作用域问题
你在FROM之前声明的ARG,在FROM之后的构建阶段是无法直接访问的——因为FROM会启动一个全新的构建上下文,之前的ARG会被重置。所以必须在FROM之后重新声明这些ARG:
FROM node:8.9.4 # 重新声明ARG,让后续指令能访问到构建参数 ARG ssh_prv_key ARG ssh_pub_key
2. SSH Agent的生命周期问题
每个RUN指令都是一个独立的shell会话,你在一个RUN里启动ssh-agent,到下一个RUN的时候这个进程已经退出了,所以ssh-add的效果根本无法延续到npm i的步骤。必须把启动ssh-agent、添加密钥、安装依赖放在同一个RUN里,确保它们在同一个会话中执行。
3. SSH目录的默认路径问题
你自定义了/ssh目录来存放密钥,但SSH默认会读取~/.ssh下的文件,虽然可以通过配置指定,但用默认路径能减少不必要的问题。
修复后的Dockerfile(基础版)
# Use an official Node runtime as a parent image FROM node:8.9.4 # 重新声明ARG,确保后续指令能访问构建参数 ARG ssh_prv_key ARG ssh_pub_key # Specify working directory in docker container WORKDIR /app # 配置SSH目录和权限 RUN mkdir -p /root/.ssh/ && chmod 0700 /root/.ssh/ # 添加密钥并设置正确权限 RUN echo "$ssh_prv_key" > /root/.ssh/id_rsa && \ echo "$ssh_pub_key" > /root/.ssh/id_rsa.pub && \ chmod 600 /root/.ssh/id_rsa && \ chmod 600 /root/.ssh/id_rsa.pub # 将bitbucket添加到已知主机,避免首次连接的确认提示 RUN ssh-keyscan bitbucket.org >> /root/.ssh/known_hosts && chmod 644 /root/.ssh/known_hosts # 把SSH操作和依赖安装放在同一个RUN,确保会话持续 RUN eval `ssh-agent -s` && \ ssh-add /root/.ssh/id_rsa && \ # 安装项目依赖 npm i && \ # 安装完成后立即删除密钥,减少泄露风险 rm -rf /root/.ssh/ # 复制项目文件 COPY . /app # 启动命令 CMD ["npm", "docker:rogers:local"] # 暴露端口 EXPOSE 4200
更安全的方案:多阶段构建
上面的基础版虽然能解决问题,但如果有人拿到你的镜像,还是有可能通过镜像层历史提取到SSH密钥(因为rm只是在当前层标记删除,之前的层依然保留密钥)。用多阶段构建可以彻底避免这个问题:
# 第一阶段:仅用于安装依赖,包含SSH密钥操作 FROM node:8.9.4 AS builder ARG ssh_prv_key ARG ssh_pub_key WORKDIR /app # 配置SSH RUN mkdir -p /root/.ssh/ && chmod 0700 /root/.ssh/ RUN echo "$ssh_prv_key" > /root/.ssh/id_rsa && \ echo "$ssh_pub_key" > /root/.ssh/id_rsa.pub && \ chmod 600 /root/.ssh/id_rsa && \ chmod 600 /root/.ssh/id_rsa.pub RUN ssh-keyscan bitbucket.org >> /root/.ssh/known_hosts && chmod 644 /root/.ssh/known_hosts # 复制package文件并安装依赖 COPY package.json package-lock.json* ./ RUN eval `ssh-agent -s` && ssh-add /root/.ssh/id_rsa && npm i # 第二阶段:构建最终镜像,仅复制需要的文件 FROM node:8.9.4 WORKDIR /app # 从builder阶段复制已经安装好的node_modules COPY --from=builder /app/node_modules ./node_modules # 复制项目源码 COPY . . # 启动命令 CMD ["npm", "docker:rogers:local"] # 暴露端口 EXPOSE 4200
这个方案里,SSH密钥只存在于builder阶段的镜像中,最终的镜像完全不包含任何SSH相关的文件,安全性大大提升。
额外注意事项
如果你的SSH私钥是带密码的,ssh-add会要求输入密码,这时候构建会卡住。建议你生成一个无密码的SSH密钥专门用于Docker构建,并且只给这个密钥授予拉取目标私有仓库的权限,最小化风险。
内容的提问来源于stack exchange,提问作者Usman Tahir

