Docker build 切换用户后,SSH 挂载报 publickey
切到非 root 用户后 SSH 构建报 publickey,检查 agent 套接字的默认属主和实际 UID。套接字可读、agent 身份与仓库授权分别核对。
同一套 SSH agent,root 执行的构建能访问仓库,Dockerfile 切到非 root 用户后却报 Permission denied (publickey)。先检查当前 RUN 能否访问 BuildKit 提供的 agent 套接字。--ssh default 把 agent 交给构建器,但不自动让镜像里的每个用户都有权使用它。
这篇针对 Linux BuildKit 的 RUN --mount=type=ssh。宿主机 agent 没有身份、远端账号没有仓库权限,也会产生 publickey 错误,不能只看到这几个字就认定 UID 不匹配。
先在失败步骤的身份下读取 agent 状态
默认 SSH 挂载的套接字属主 UID、GID 均为 0,模式为 0600。基础镜像或前面的 USER 若已经选了普通用户,该用户可能无法连接套接字。
在原来需要 SSH 的位置临时使用只读检查,镜像需有 id、ls 和 ssh-add:
RUN --mount=type=ssh,required=true \
id && ls -ln "$SSH_AUTH_SOCK" && ssh-add -l
这条检查读取执行身份、套接字属性和 agent 身份指纹,不输出私钥。指纹及账号注释仍可能属于内部信息,分享日志前脱敏。required=true 可以在未提供挂载时明确失败,但套接字存在不代表里面一定有可用身份。
如果结果是 Error connecting to agent: Permission denied,对照 id 与套接字数字属主。若 agent 可连接但没有身份,应先在客户端确认 agent 已载入所需密钥;若身份可读、远端仍拒绝,则转去检查账号和仓库授权。
uid 要匹配实际执行用户
确认该步骤实际 UID 是 1000 后,可把挂载改为:
RUN --mount=type=ssh,uid=1000,required=true \
ssh-add -l
1000 是示例值,其他镜像可能使用 100、1001 或自定义身份;按当前镜像的实际用户设置,不能把用户名相同当作 UID 必然相同。
验证套接字可读后,把原仓库操作放回同一条带 SSH 挂载的 RUN。SSH 挂载只在该条指令执行期间可用,后面另一条 RUN 需要再次声明。保留原构建上下文、Dockerfile、标签和客户端 SSH 参数,不在修 UID 时顺带切换其他构建输入。
套接字可用后,还要检查认证两端
agent 身份列表可读只说明当前用户能请求 agent 服务,不能证明远端会接受这把密钥。客户端应载入正确身份,远端账号应具有目标仓库的读取权限。即使仓库公开,采用 SSH 地址访问时也可能需要完成 SSH 身份认证。
构建用户的 known_hosts 还需可读,并按可信渠道核对服务端指纹;不要用关闭主机密钥校验掩盖另一个错误。如果新日志变成 Host key verification failed,那已经是不同分支。
不要把套接字改成所有用户可读写,也不要将私钥通过 COPY、ARG 或 ENV 放进镜像。agent 转发应只提供给可信的构建指令;修改挂载属主不替代这一授权边界。
最终验证要让实际仓库操作执行,而不只看到缓存命中。先在受控构建中检查必要步骤,确认认证和所需文件取得成功,再按原发布流程交付。保存原 Dockerfile 与可回退镜像,核对后移除临时诊断;恢复配置时保留原非 root 运行身份。


