错误信息
Error response from daemon: Get "https://registry.example.com:5000/v2/": tls: failed to verify certificate: x509: certificate signed by unknown authority
消息中的镜像仓库主机和端口是你自己的值;docker push、pull 和 login 都会把它报告为“Error response from daemon: Get \"https://<registry>/v2/\": …”。containerd、nerdctl、Podman 以及拉取镜像的 Kubernetes 节点会打印同样的 x509 文本,只是带有各自的前缀。
Docker Engine (dockerd) — docker pull / push / login against a registry with an untrusted certificate
含义
Docker 守护进程(而不是 docker CLI)尝试访问镜像仓库位于 /v2/ 的 API,但无法验证其 TLS 证书:证书链无法通向守护进程信任的 CA。dockerd 信任宿主机的系统 CA 证书包,以及放在 /etc/docker/certs.d/<registry>:<port>/ 中、按镜像仓库区分的 CA。常见原因是使用公司证书的私有镜像仓库,以及执行 TLS 检查的代理。
原因
使用私有证书的自托管镜像仓库
常见使用公司 CA 或自建 CA 签发证书的 Harbor、GitLab 镜像仓库、Nexus 或 registry:2。
Docker Hub 或其他镜像仓库前面有执行 TLS 检查的代理
常见守护进程看到的是代理的证书;如果宿主机的证书存储中没有代理的根证书,每次拉取都会失败。
CA 安装在了守护进程不会查找的位置
偶见如果守护进程运行在别处(Docker Desktop 虚拟机、远程 DOCKER_HOST、CI 运行器),把 CA 复制到客户端机器的证书存储中不会起任何作用。certs.d 目录名必须与镜像仓库的主机和端口完全一致。
镜像仓库没有发送中间证书
偶见Go(dockerd)不会自行获取中间证书;只发送叶子证书的镜像仓库即使使用公共 CA 也会失败。
修复方法
- 1.
在 certs.d 中为该镜像仓库安装 CA
/etc/docker/certs.d/<host>:<port>/ca.crt 在每次请求时都会被读取;在复现中,添加该文件后 docker login 立即成功。
bash # Trust the registry's CA for this registry only (no daemon restart needed) sudo mkdir -p /etc/docker/certs.d/registry.example.com:5000 sudo cp corp-root-ca.pem /etc/docker/certs.d/registry.example.com:5000/ca.crt docker login registry.example.com:5000 - 2.
在系统范围安装 CA 并重启 dockerd
对于代理以及涉及多个镜像仓库的情况,把根证书添加到宿主机的证书包中;守护进程会在启动时读取它。
bash # Or system-wide (proxy roots, many registries), then restart the daemon sudo cp corp-root-ca.pem /usr/local/share/ca-certificates/corp-root-ca.crt sudo update-ca-certificates sudo systemctl restart docker - 3.
Docker Desktop:在宿主机上信任根证书
Docker Desktop 的虚拟机在启动时从宿主机操作系统获取受信任的根证书,因此要把根证书安装在宿主机上,然后重启 Docker Desktop。
text Docker Desktop: the daemon runs in a VM. Add the root to the host's trust store (macOS Keychain "System" with "Always Trust", Windows "Trusted Root Certification Authorities") and restart Docker Desktop; it imports trusted host roots into the VM. - 4.
检查镜像仓库的证书链
如果只发送了叶子证书,就为镜像仓库或其反向代理配置完整证书链。
bash openssl s_client -connect registry.example.com:5000 -servername registry.example.com -showcerts </dev/null 2>/dev/null \ | grep -E '^ *[0-9]+ s:|^ *i:'
相关错误
x509: certificate signed by unknown authorityGo crypto/x509tls: failed to verify certificate: x509: certificate signed by unknown authorityGo crypto/tls (Go ≥ 1.20)http: server gave HTTP response to HTTPS clientGo net/httpUnable to connect to the server: tls: failed to verify certificate: x509: certi…kubectl (client-go)