Docker 开启 SSL 验证

最近看 OJ 项目的远程开发阶段,然后踩坑踩了 2 天😂

Docker 版本:在 CentOS 安装 sudo yum install docker-ce-20.10.9 docker-ce-cli-20.10.9 containerd.io

Client: Docker Engine - Community
 Version:           20.10.9
 API version:       1.41
 Go version:        go1.16.8
 Git commit:        c2ea9bc
 Built:             Mon Oct  4 16:08:25 2021
 OS/Arch:           linux/amd64
 Context:           default
 Experimental:      true

Server: Docker Engine - Community
 Engine:
  Version:          20.10.9
  API version:      1.41 (minimum version 1.12)
  Go version:       go1.16.8
  Git commit:       79ea9d3
  Built:            Mon Oct  4 16:06:48 2021
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.6.31
  GitCommit:        e377cd56a71523140ca6ae87e30244719194a521
 runc:
  Version:          1.1.12
  GitCommit:        v1.1.12-0-g51d5e94
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0

SpringBoot 版本: 2.7.14

Java-Docker 依赖

<!-- https://mvnrepository.com/artifact/com.github.docker-java/docker-java -->
<dependency>
    <groupId>com.github.docker-java</groupId>
    <artifactId>docker-java</artifactId>
    <version>3.3.4</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.github.docker-java/docker-java-transport-httpclient5 -->
<dependency>
    <groupId>com.github.docker-java</groupId>
    <artifactId>docker-java-transport-httpclient5</artifactId>
    <version>3.3.4</version>
</dependency>

不设置 SSL 认证

vim /usr/lib/systemd/system/docker.service

主要添加 -H tcp://0.0.0.0:2376 进去,只需要修改这个位置就好了

image-202405101444190211

然后重启 Docker 服务

systemctl daemon-reload
systemctl restart docker

然后查看是否守护线程启动成功

systemctl status docker.service

image-20240510144732486

Java 调用代码

	 // 获取默认的 Docker Client
    DockerClientConfig config = DefaultDockerClientConfig.createDefaultConfigBuilder()
            .withDockerHost("tcp://服务器IP:2376")
            .build();
    DockerHttpClient httpClient = new ApacheDockerHttpClient.Builder()
            .dockerHost(config.getDockerHost())
            .maxConnections(100)
            .connectionTimeout(Duration.ofSeconds(30))
            .responseTimeout(Duration.ofSeconds(45))
            .build();
    DockerClient dockerClient = DockerClientImpl.getInstance(config, httpClient);
    PingCmd pingCmd = dockerClient.pingCmd();
    pingCmd.exec();

配置 SSL 验证

官方文档

需要注意一点 官方文档写的 TLS 应该在 2376 端口,这里之前踩了大坑(之前一直使用 2375 一直不行)

当然我们需要开启服务器的2376 端口的防火墙(和宝塔如果有的话)!!!

image-20240510174255904

将以下示例中 $HOST 的所有实例替换为 Docker 守护程序主机(服务器 IP)

首先创建存储相关信息的文件夹

mkdir -p /usr/local/certs.d/dockerd/ca
cd /usr/local/certs.d/dockerd/ca

让后在这个文件夹下面执行对应的命令

$ openssl genrsa -aes256 -out ca-key.pem 4096
Generating RSA private key, 4096 bit long modulus
..............................................................................++
........++
e is 65537 (0x10001)
Enter pass phrase for ca-key.pem:
Verifying - Enter pass phrase for ca-key.pem:

$ openssl req -new -x509 -days 365 -key ca-key.pem -sha256 -out ca.pem
Enter pass phrase for ca-key.pem:
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): CN
State or Province Name (full name)[]: Beijing 
Locality Name (eg, city) []: Beijing
Organization Name (eg, company) [Internet Widgits Pty Ltd]: China
Organizational Unit Name (eg, section) []: developer
Common Name (e.g. server FQDN or YOUR name) []:$HOST
Email Address []: 填写邮箱
$ openssl genrsa -out server-key.pem 4096
Generating RSA private key, 4096 bit long modulus
.....................................................................++
.................................................................................................++
e is 65537 (0x10001)

$ openssl req -subj "/CN=$HOST" -sha256 -new -key server-key.pem -out server.csr

接下来,我们将使用我们的 CA 签署公钥:

这里官方文档使用了 DNS 我们可以不写

$ echo subjectAltName = IP:$HOST,IP:127.0.0.1 >> extfile.cnf

$ echo extendedKeyUsage = serverAuth >> extfile.cnf
openssl x509 -req -days 365 -sha256 -in server.csr -CA ca.pem -CAkey ca-key.pem 
  -CAcreateserial -out server-cert.pem -extfile extfile.cnf
Signature ok
subject=/CN=your.host.com
Getting CA Private Key
Enter pass phrase for ca-key.pem:
$ openssl genrsa -out key.pem 4096
Generating RSA private key, 4096 bit long modulus
.........................................................++
................++
e is 65537 (0x10001)

$ openssl req -subj '/CN=client' -new -key key.pem -out client.csr
$ echo extendedKeyUsage = clientAuth > extfile-client.cnf
$ openssl x509 -req -days 365 -sha256 -in client.csr -CA ca.pem -CAkey ca-key.pem 
  -CAcreateserial -out cert.pem -extfile extfile-client.cnf
Signature ok
subject=/CN=client
Getting CA Private Key
Enter pass phrase for ca-key.pem:

删除一些不重要的文件

rm -v client.csr server.csr extfile.cnf extfile-client.cnf

修改文件的权限

$ chmod -v 0400 ca-key.pem key.pem server-key.pem
$ chmod -v 0444 ca.pem server-cert.pem cert.pem

最后

vi /etc/docker/daemon.json

填写相关内容

{
    "registry-mirrors": ["https://mirror.ccs.tencentyun.com"],
    "tls": true,
    "tlscacert": "/usr/local/certs.d/dockerd/ca/ca.pem",
    "tlscert": "/usr/local/certs.d/dockerd/ca/server-cert.pem",
    "tlskey": "/usr/local/certs.d/dockerd/ca/server-key.pem"
}

然后重启 Docker 服务

systemctl daemon-reload
systemctl restart docker

然后查看是否守护线程启动成功

systemctl status docker.service

image-20240510175932066

测试连通性

我们需要在服务器上下载下面三个文件 ca.pemkey.pemcert.pem 就在 /usr/local/certs.d/dockerd/ca目录下

PS 执行这个命令的时候我们需要在 ssl 目录里面放入这三个文件

curl https://服务器IP:2376/version --cert cert.pem --key key.pem  --cacert ca.pem

image-20240510181817369

Java 调用代码

─src
│  ├─main
│  │  ├─java
│  │  └─resources
│  │      ├─ca 文件放在这个目录下  

而且文件的名称需要下面这三个名称即 ca.pemkey.pemcert.pem

image-20240510180633477

   		URL url = ClassLoaderUtil.getClassLoader().getResource("ca");
        DockerClientConfig config = DefaultDockerClientConfig.createDefaultConfigBuilder()
                .withDockerHost("tcp://服务器 IP :2376")
                .withDockerTlsVerify(true)
            	// 下面三个参数不加也能运行成功
                // .withRegistryPassword("密码")
                // .withRegistryUsername("用户名")
                // .withRegistryEmail("邮箱")
                // 这里如果不截取的话会报错 url.getPath()结果是 /E:/yuoj-code-sandbox-master/target/classes/ca 
            	// 多一个 / 所以要截取,截取之后才不会报错
                .withDockerCertPath(url.getPath().substring(1)) 
                .build();
        DockerHttpClient httpClient = new ApacheDockerHttpClient.Builder()
                .dockerHost(config.getDockerHost())
            	// 这里不要忘了啊,如果不加的话就会是 Status 400: Client sent an HTTP request to an HTTPS server 
                .sslConfig(config.getSSLConfig())
                .maxConnections(100)
                .connectionTimeout(Duration.ofSeconds(30))
                .responseTimeout(Duration.ofSeconds(45))
                .build();
        DockerClient dockerClient = DockerClientImpl.getInstance(config, httpClient);
        PingCmd pingCmd = dockerClient.pingCmd();
        pingCmd.exec();

报错小结

1、这里如果 Docker 版本过高会报一下错误 {“message”:“client version 1.23 is too old. Minimum supported API version is 1.24, please upgrade your client to a newer version”}

解决办法:降低服务器 Docker 版本

2、还有一个报错 Cannot pull images when logged in to Docker Desktop (Status 500: unauthorized: incorrect username or password)

这个报错解决方法是,在 Docker 上登录一个账号就行了

3、报错信息 Status 400: Client sent an HTTP request to an HTTPS server

解决办法:首先确定 .sslConfig(config.getSSLConfig()) 写了没有,其次在确认一下公钥私钥证书是否放到了对应的目录下面

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>