使用TLS保护DockerAPI

Avatar photo

###下文中所有的 $HOST 都使用docker daemon的host的DNS,基本就是服务器ip
我们要做的是,在dockerd中 使用 tlservify 和 tlscacert 标识来信任一个 CA 证书。

1.在Docker Daeom的Host机上创建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) [AU]:CN
State or Province Name (full name) [Some-State]:Shanghai
Locality Name (eg, city) []:Shanghai
Organization Name (eg, company) [Internet Widgits Pty Ltd]:GiantRoot Inc
Organizational Unit Name (eg, section) []:cloud server
Common Name (e.g. server FQDN or YOUR name) []:$HOST
Email Address []:jusesgod@163.com

**确保$HOST是客户端用来连接Daemon的名称。

2.创建服务器证书

$ 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

3.通过我们的CA证书,给公钥签名
A:设置允许访问的连接,允许通过$HOST和 127.0.0.1访问

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

**如果$HOST是ip地址的话,就不用写DNS:******了,直接写IP:XXXXXXXX

B:设置Docker Daemon的键的扩展用法属性设置为仅用于 服务器身份验证

$ echo extendedKeyUsage = serverAuth >> extfile.cnf

C:生成签名证书

$ 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

4.生成客户端证书

$ 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

为了保护CA的只读性,删除证书的写属性

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

现在可以这样启动dockerd

$ dockerd \
    --tlsverify \
    --tlscacert=ca.pem \
    --tlscert=server-cert.pem \
    --tlskey=server-key.pem \
    -H=0.0.0.0:2376

**注意在docker.service文件中的配置:

ExecStart=/usr/bin/dockerd --tlsverify --tlscacert=/root/.docker/ca.pem --tlscert=/root/.docker/server-cert.pem --tlskey=/root/.docker/server-key.pem -H 0.0.0.0:2375 -H fd:// --containerd=/run/containerd/containerd.sock

客户端

$ docker --tlsverify \
    --tlscacert=ca.pem \
    --tlscert=cert.pem \
    --tlskey=key.pem \
    -H=$HOST:2376 version

最后通过服务器测试访问:

$ docker --tlsverify     --tlscacert=ca.pem     --tlscert=cert.pem     --tlskey=key.pem     -H=106.12.80.25 version
Client: Docker Engine - Community
Version:           25.0.3
API version:       1.44
Go version:        go1.21.6
Git commit:        4debf41
Built:             Tue Feb  6 21:17:10 2024
OS/Arch:           linux/amd64
Context:           default


Server: Docker Engine - Community
Engine:
  Version:          25.0.3
  API version:      1.44 (minimum version 1.24)
  Go version:       go1.21.6
  Git commit:       f417435
  Built:            Tue Feb  6 21:16:08 2024
  OS/Arch:          linux/amd64
  Experimental:     false
containerd:
  Version:          1.6.28
  GitCommit:        ae07eda36dd25f8a1b98dfbf587313b99c0190bb
runc:
  Version:          1.1.12
  GitCommit:        v1.1.12-0-g51d5e94
docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0

失败结果:

$ docker --tlsverify     --tlscacert=ca.pem     --tlscert=cert.pem     --tlskey=key.pem     -H=106.12.80.25 version
Client: Docker Engine - Community
Version:           25.0.3
API version:       1.44
Go version:        go1.21.6
Git commit:        4debf41
Built:             Tue Feb  6 21:17:10 2024
OS/Arch:           linux/amd64
Context:           default
error during connect: Get "https://106.12.80.25:2375/v1.24/version": tls: failed to verify certificate: x509: certificate is valid for 127.0.0.1, not 106.12.80.25

在idea中配置:
Engin API URL:https://106.12.80.25:2375
certificates folder:证书所在目录
就能安全访问了。

参考文档:
Docker文档:Protect the Docker daemon socket | Docker Docs
Docker配置tls中文文档 Docker开启安全的TLS远程连接访问方式_docker_脚本之家 (jb51.net)