错误信息
http: server gave HTTP response to HTTPS client
各个程序会加上自己的前缀:Docker 打印“Error response from daemon: Get \"https://<registry>/v2/\": http: server gave HTTP response to HTTPS client”,Go 程序打印“Get \"https://…\": http: server gave …”。如果回复也不是 HTTP,Go 会报告“tls: first record does not look like a TLS handshake”。
Go net/http — the TLS handshake received a plain HTTP response (also printed by Docker, Helm, Terraform)
含义
Go 的 HTTP 客户端发起了 TLS 握手,服务器却用明文 HTTP 响应作答;Go 识别出“HTTP/”字节,于是报告服务器使用的是 HTTP 而不是 HTTPS。最常见的情况是对未启用 TLS 的自托管镜像仓库执行 docker pull 或 docker login:对于不在环回地址上、也没有被显式标记为不安全的镜像仓库,Docker 一律要求使用 HTTPS。
原因
未启用 TLS 的容器镜像仓库
常见为了测试而以明文 HTTP 启动的 registry:2(distribution)、Harbor 或 Nexus 镜像仓库。Docker、containerd、Podman、Helm 和 Skopeo 对 localhost 以外的任何地址都要求使用 HTTPS。
https:// 搭配了 HTTP 端口
常见URL 或配置把 https:// 与 80、8080 端口或某个内部服务端口组合在一起。
TLS 由另一个端口上的代理终止
偶见后端在负载均衡器后面使用 HTTP;绕过负载均衡器、用 https:// 直接访问后端端口的客户端会遇到这个错误。
修复方法
- 1.
确认该端口使用明文 HTTP
如果用 http:// 访问时返回了 HTTP 状态行,就可以确认。改用 TLS 端点(或者在可以接受的场合使用 http://)。
bash curl -si http://registry.example.com:5000/v2/ | head -1 # answers over plain HTTP? - 2.
通过 TLS 提供镜像仓库服务
为镜像仓库提供证书(公共 ACME 或公司 CA)并完成配置,或者在它前面放一个终止 TLS 的代理。
yaml # Docker registry (distribution) config.yml: serve TLS http: addr: :5000 tls: certificate: /certs/registry.example.com.fullchain.pem key: /certs/registry.example.com.key - 3.
最后手段:把镜像仓库标记为不安全(daemon.json)
把该镜像仓库列入 /etc/docker/daemon.json 的 insecure-registries 中(并重启 dockerd),Docker 就会用明文 HTTP 与它通信。此后镜像和凭证的传输既不加密,也不经过身份验证;只在隔离的实验网络中使用。
json { "insecure-registries": ["registry.example.com:5000"] }