# x509: certificate signed by unknown authority（Docker）怎么修

> docker pull/login 报 x509: certificate signed by unknown authority，是 Docker 守护进程不信任仓库的 CA。用 certs.d 配置 CA，不要用 insecure-registries。

Source: https://ssl.sh/zh/errors/docker-x509-certificate-signed-by-unknown-authority/
Last updated: 2026-11-10

**`docker pull`、`docker push` 或 `docker login` 报 `x509: certificate signed by unknown authority`，意思是 Docker 守护进程没法用它信任的根证书验证镜像仓库的证书。** 典型情况是 Harbor、Nexus、GitLab、`registry:2` 等私有仓库用了内部 CA 或自签名证书，或者公司代理重新签发了连接。正确做法是通过 `certs.d` 或系统信任库把这张 CA 交给守护进程，而不是把仓库标记为不安全。

较新的 Docker 会显示 `tls: failed to verify certificate: x509: certificate signed by unknown authority`，是同一个错误。

## 快速修复

最快的修法是把仓库的 CA 证书放进 `/etc/docker/certs.d/<仓库地址>/ca.crt`，只对这个仓库生效。在 Linux 的 Docker 主机上执行：

```bash
sudo mkdir -p /etc/docker/certs.d/registry.example.com
sudo cp registry-ca.crt /etc/docker/certs.d/registry.example.com/ca.crt
docker pull registry.example.com/team/app:latest
```

仓库带端口时目录名要带上端口：`/etc/docker/certs.d/registry.example.com:5000/`。这个目录下以 `.crt` 结尾的文件都会被当作 CA 根证书。如果仍然失败，执行 `sudo systemctl restart docker` 重启守护进程。

不确定仓库证书是谁签的，先看签发者：

```bash
openssl s_client -connect registry.example.com:443 -servername registry.example.com -showcerts </dev/null | grep -E 's:|i:'
```

## 原因

这个报错有 4 种原因，最常见的是 Harbor 等私有仓库用了内部 CA 或[自签名证书](https://ssl.sh/zh/glossary/self-signed-certificate/)。

### 1. 仓库用了内部 CA 或自签名证书

国内很多团队自建 Harbor 时按教程生成了自签名 CA。

- **确认：** 签发者是公司 CA，或者证书是自签名的（`s:` 和 `i:` 相同）。
- **修复：** 按上面的方法通过 `certs.d` 分发 CA（证书本身是自签名时才分发服务器证书），或者在系统层面信任它。Harbor 安装目录里通常保留了当初生成的 `ca.crt`。

### 2. 公司代理解密 HTTPS

在办公网络里连 Docker Hub 等公网仓库也报错，因为代理出示的是它自己签发的证书。

- **确认：** `docker pull hello-world` 同样失败，签发者是代理设备。
- **修复：** 把代理根证书加入宿主机的系统信任库，然后重启 Docker：

```bash
sudo cp proxy-root.crt /usr/local/share/ca-certificates/
sudo update-ca-certificates
sudo systemctl restart docker
```

在 Linux 上 Docker 会把 `certs.d` 的根证书和系统根证书合并使用，这样所有仓库都能生效。CentOS/RHEL 系统改用 `/etc/pki/ca-trust/source/anchors/` 和 `update-ca-trust`。

### 3. 仓库没发中间证书

仓库前面的 Nginx 只配置了站点证书，没发[中间证书](https://ssl.sh/zh/glossary/intermediate-certificate/)。浏览器能打开，Docker（Go 编写，不会自动补中间证书）却报错。

- **确认：** `openssl s_client -showcerts` 只列出一张证书。
- **修复：** 在仓库上配置完整证书链，可用[证书链补全工具](https://ssl.sh/zh/tools/chain-builder/)检查。

### 4. 证书没有 SAN

如果报错是 `relies on legacy Common Name field`，说明仓库证书没有 SAN，见[旧式 Common Name 报错](https://ssl.sh/zh/errors/go-x509-certificate-relies-on-legacy-common-name/)。

## 各环境修法

各环境的原则相同：把 CA 交给实际发起连接的进程。Docker 守护进程、containerd、Podman 和镜像构建各读各的证书目录。

**镜像加速器：** `daemon.json` 里的 `registry-mirrors` 指向的加速地址如果用的是公网证书，一般不会报这个错；如果是公司内部搭的加速缓存，同样要把它的 CA 放进 `certs.d`。阿里云 ACR、腾讯云 TCR 等云厂商仓库使用公网证书，报错时优先怀疑公司代理。

**Rootless Docker：** 证书目录是 `$XDG_CONFIG_HOME/docker/certs.d`（默认 `~/.config/docker/certs.d`），不是 `/etc/docker/certs.d`。

**Docker Desktop（macOS/Windows）：** Docker Desktop 可以使用宿主机证书库里的 CA。把根证书加入 macOS“系统”钥匙串或 Windows“受信任的根证书颁发机构”，再重启 Docker Desktop。针对某个仓库的客户端证书放在宿主机的 `~/.docker/certs.d`。

**Windows Server 上的 Windows 容器：** 放到 `%PROGRAMDATA%\docker\certs.d\registry.example.com\ca.crt`，仓库有端口时去掉冒号。

**用 containerd 的 Kubernetes 节点：** kubelet 通过 containerd 拉镜像，containerd 不读 Docker 的 `certs.d`。要在 containerd 自己的仓库主机配置里（`config_path` 指定目录下的 `hosts.toml`）设置 CA，然后重启 containerd。

**Podman：** 目录结构相同，路径是 `/etc/containers/certs.d/<仓库地址>/ca.crt`。

**`docker build` 内部：** 每个镜像有自己的信任库，遇到代理时在 Dockerfile 里加入根证书：

```dockerfile
COPY proxy-root.crt /usr/local/share/ca-certificates/
RUN update-ca-certificates
```

## 不要这样做

这 3 种做法都会让镜像来源失去校验，伪造的仓库可以借此投放恶意镜像。

- **在 `daemon.json` 里写 `"insecure-registries": ["registry.example.com"]`。** 这会关闭该仓库的证书校验，伪造的仓库可以塞给你一个带后门的镜像，而你会带着生产环境的密钥去运行它。
- **把服务器证书而不是 CA 复制进 `certs.d`。** 仓库证书一续期就又会报错。
- **在内网关闭仓库的 TLS、改用明文 HTTP。** 内网恰恰是篡改镜像最容易的地方。
