假设的复现环境:
A服务器:ip为192.168.1.1,Docker Registry域名和端口为docker-registry.orztip.internal:9933
B服务器:ip为192.168.1.2
以上服务器的docker使用rootless模式运行,运行docker rootless的用户名为demo。
(备注:以下内容也适用于排查docker使用常规root模式运行的问题)
docker配置目录下已经放置自签名证书ca文件,即文件在“【docker配置目录】/certs.d/docker-registry.orztip.internal:9933/ca.crt”。
现象:
A服务器使用自签名证书搭建了一个Docker Registry,然后B服务器向A服务器使用docker pull拉取镜像时总出现连接不上、或者其他错误(比如mirror docker hub时A服务器并没有拉取镜像缓存下来)。
此时A服务器查看docker logs时会有如下报错:
registry-mirror_1 | 2023/01/25 15:17:22 http: TLS handshake error from 192.168.1.2:59725: remote error: tls: bad certificate
排查:
以下均在B服务器上操作。
开启docker调试模式和详细日志,见:《如何开启docker的调试模式和详细日志输出》:https://www.orztip.com/?p=761&article_title=docker-debug-mode-and-verbose-log
重启docker后,再次尝试运行docker pull动作,然后到/var/log/syslog或者/var/log/messages查找日志,此时会看到如下日志:
Jan 26 00:12:38 B服务器 dockerd-rootless.sh[3291]: time="2023-01-26T00:12:38.689846619+08:00" level=debug msg="hostDir: /home/demo/.config/docker/certs.d/docker-registry.orztip.internal:9933"
Jan 26 00:12:38 B服务器 dockerd-rootless.sh[3291]: time="2023-01-26T00:12:38.689930920+08:00" level=debug msg="crt: /home/demo/.config/docker/certs.d/docker-registry.orztip.internal:9933/ca.crt"
Jan 26 00:12:38 B服务器 dockerd-rootless.sh[3291]: time="2023-01-26T00:12:38.689998990+08:00" level=debug msg="Trying to pull ubuntu from https://docker-registry.orztip.internal:9933/ v2"
Jan 26 00:12:38 B服务器 dockerd-rootless.sh[3291]: time="2023-01-26T00:12:38.695741621+08:00" level=warning msg="Error getting v2 registry: Get \"https://docker-registry.orztip.internal:9933/v2/\": x509: certificate relies on legacy Common Name field, use SANs instead"
Jan 26 00:12:38 B服务器 dockerd-rootless.sh[3291]: time="2023-01-26T00:12:38.695776571+08:00" level=info msg="Attempting next endpoint for pull after error: Get \"https://docker-registry.orztip.internal:9933/v2/\": x509: certificate relies on legacy Common Name field, use SANs instead"
Jan 26 00:12:38 B服务器 dockerd-rootless.sh[3291]: time="2023-01-26T00:12:38.695807201+08:00" level=debug msg="Trying to pull ubuntu from https://registry-1.docker.io v2"
Jan 26 00:12:42 B服务器 dockerd-rootless.sh[3291]: time="2023-01-26T00:12:42.218347552+08:00" level=debug msg="Fetching manifest from remote" digest="sha256:0e0402cd13f68137edb0266e1d2c682f217814420f2d43d300ed8f65479b14fb" error="context canceled" remote="docker.io/library/ubuntu:20.04"
故障最终原因:
日志中存在“ x509: certificate relies on legacy Common Name field, use SANs instead”。
顺着排查才知道,原来Go 1.15开始废弃X.509 CommonName【1】【2】,那么使用Go语言编写的docker自然也不认字段CN(Common Name),必须使用X.509的扩展字段SANs(Subject Alternate Names)。
解决:
重新生成带扩展字段SANs(Subject Alternate Names)的自签名证书。
参见docker registry的文档,使用openssl的命令行如下(注意Common Name仍然要手动输入,内容和subjectAltName一样的域名):
openssl req \
-newkey rsa:4096 -nodes -sha256 -keyout domain.key \
-addext "subjectAltName = DNS:docker-registry.orztip.internal" \
-x509 -days 3650 -out domain.crt
输出示例:
demo@A服务器: $ openssl req \
-newkey rsa:4096 -nodes -sha256 -keyout domain.key \
-addext "subjectAltName = DNS:docker-registry.orztip.internal" \
-x509 -days 3650 -out domain.crt
Generating a RSA private key
.......................................................++++
..............................................................................++++
writing new private key to 'domain.key'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:docker-registry.orztip.internal
Email Address []:
验证证书是否带SANs扩展字段,CN字段是否和SANs扩展字段中的域名是否一致:
openssl x509 -in domain.crt -text -noout
示例:
demo@A服务器$ openssl x509 -in domain.crt -text -noout
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
[略]
Signature Algorithm: sha256WithRSAEncryption
Issuer: C = AU, ST = Some-State, O = Internet Widgits Pty Ltd, CN = docker-registry.orztip.internal
Validity
Not Before: Jan 26 09:47:30 2023 GMT
Not After : Jan 23 09:47:30 2033 GMT
Subject: C = AU, ST = Some-State, O = Internet Widgits Pty Ltd, CN = docker-registry.orztip.internal
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public-Key: (4096 bit)
Modulus:
[略]
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Key Identifier:
[略]
X509v3 Authority Key Identifier:
keyid:[略]
X509v3 Basic Constraints: critical
CA:TRUE
X509v3 Subject Alternative Name:
DNS:docker-registry.orztip.internal
Signature Algorithm: sha256WithRSAEncryption
[略]
然后重新使用domain.crt和domain.key部署A服务器上的docker registry,并且重启docker。
同时使用domain.crt文件覆盖掉B服务器上部署的ca文件,即B服务器上的“【docker配置目录】/certs.d/docker-registry.orztip.internal:9933/ca.crt”。
最后验证一下,应该就可以正常运行了。
参考文章:
【1】https://go.dev/doc/go1.15#commonname
【2】https://github.com/johanbrandhorst/certify/issues/122
【3】https://docs.docker.com/registry/insecure/
本页永久链接:https://www.orztip.com/?p=768&article_title=docker-registry-self-sign-cert-error-tls-bad-certificate