Docker 使用证书加固 Docker Daemon 安全

1 背景知识

Docker Daemon 启动的服务对外提供的是 HTTP 接口,为了增强 HTTP 连接的安全性,我们通过设置 TLS 来认证客户端是可信的,只有通过证书验证的客户端才可以连接 Docker Daemon

通常情况下服务器和客户端证书都需要通过第三方 CA 签发,在本实验中为了操作方便,我们使用的是自签名的证书。

2 设置环境变量

  1. 设置一个 RANDFILE 的环境变量:防止报错。
[docker@node1 ~]$ export RANDFILE=.rnd

  1. 创建存放证书的目录。
[ docker@node1 ~]$ mkdir -p ~/. docker
[ docker@node1 ~]$ cd ~/. docker

3 创建 CA 证书

  1. 创建 CA 私钥。
    创建 CA 私钥,注意需要输入密码,这个密码是不会显示的,请务必记住。
#kingbase>
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:        # 此处输入你想设置的密码(kingbase)
Verifying - Enter pass phrase for ca-key.pem:  # 再次输入

2、创建 CA 公钥。
ca-key.pem 创建用来签名的公钥 ca.pem,输入必要的信息:

#kingbase> 
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]:Beijing
Locality Name (eg, city) []:Beijing
Organization Name (eg, company) [Internet Widgits Pty Ltd]:kingbase # 输入组织名
Organizational Unit Name (eg, section) []:kingbase
Common Name (e.g. server FQDN or YOUR name) []:192.168.40.111 # 输入服务器域名
Email Address []:kingbase@kingbase.com
输入的信息 输入信息
Country Name (2 letter code) [AU]: CN
State or Province Name Beijing
Locality Name Beijing
Organization Name kingbase
Organizational Unit Name kingbase
Common Name 192.168.40.111

4 服务端证书配置

接着创建服务器的 key 和证书 server.csr,然后使用 CA 证书签发服务器证书。

#kingbase>
openssl genrsa -out server-key.pem 4096
Generating RSA private key, 4096 bit long modulus
....................................................................................................................................................++
.................................................................................................................................................................................................................++
e is 65537 (0x10001)
#kingbase>
openssl req -subj "/CN=192.168.40.111" -sha256 -new -key server-key.pem -out server.csr
#kingbase> 
echo subjectAltName = IP:192.168.40.111 >> extfile.cnf
# 设置仅用于服务器身份验证
echo extendedKeyUsage = serverAuth >> extfile.cnf
#kingbase>
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=localhost
Getting CA Private Key
Enter pass phrase for ca-key.pem:   # 输入密码。

上面的步骤中 extfile.cnf 文件的作用是添加允许连接的 IP 地址,注意服务器证书创建时需要输入服务器的域名,在本实验中我们是用的是 *

5 客户端证书配置

客户端的证书用来连接 Docker Daemon 服务,同服务器端证书的操作类似,先创建 keycsr 证书,然后使用 CA 签发:

#kingbase>
openssl genrsa -out client-key.pem 4096
openssl req -subj '/CN=client' -new -key client-key.pem -out client.csr
echo extendedKeyUsage = clientAuth > client-extfile.cnf
openssl x509 -req -days 365 -sha256 -in client.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out cert.pem -extfile client-extfile.cnf

终端会有如下显示:

Signature ok
subject=/CN=client
Getting CA Private Key
Enter pass phrase for ca-key.pem:   # 输入之前设置的密码

现在查看 /home/docker/.docker 目录下所有创建的证书和 key 文件:
image.png

已经生成了 cert.pemserver-cert.pem,我们就可以删除两个证书签名请求了:

#kingbase>
rm -v client.csr server.csr

为防止意外损坏密钥,我们删除其写入权限:

#kingbase>
chmod -v 0400 ca-key.pem server-key.pem client-key.pem
chmod -v 0444 ca.pem server-cert.pem cert.pem

6 置服务端

  1. 修改 daemon.json
#root>
vi /etc/docker/daemon.json
{
"graph": "/data",
"tls": true,
"tlscacert": "/home/docker/.docker/ca.pem",
"tlscert": "/home/docker/.docker/server-cert.pem",
"tlskey": "/home/docker/.docker/server-key.pem",
"hosts": ["tcp://0.0.0.0:2376", "unix:///var/run/docker.sock"],
"tlsverify": true
}
说明
"tls": true 默认 false, 启动 TLS 认证开关。
"tlscacert": "ca. pem" 默认 ~/. docker/ca. pem,通过 CA 认证过的 certificate 文件路径
"tlscert": "server-cert. pem" 默认 ~/. docker/cert. pem ,TLS 的 certificate 文件路径
"tlskey": "server-key. Pem" 默认 ~/. docker/key. pem,TLS 的 key 文件路径。
"tlsverify": true 默认 false ,使用 TLS 并做后台进程与客户端通讯的验证。
"hosts": 设置 Docker API 接口监听地址。
  1. 修改 docker. service 文件
Warning

编者注:因为在 systemd 用于启动 Docker 守护程序的系统上 -H 已设置,因此您无法使用该 hostsdaemon.json 来添加侦听地址。具体请参阅:
https://docs.docker.com/engine/admin/systemd/#custom-docker-daemon-options

  1. 找到 ExecStart 并修改成以下内容
#root>
vi /usr/lib/systemd/system/docker.service
ExecStart=/usr/bin/dockerd  --containerd=/run/containerd/containerd.sock
  1. 生效修改的内容
#root>
systemctl daemon-reload
  1. 重启 Docker Daemon 进程。
[root@node1 ~]# service docker restart

7 客户端连接 Docker Daemon

直接使用不增加证书参数的方式连接并执行 docker image ls,系统会返回不能连接的错误。
而使用加密的客户端连接方式则可以正确连接。新开一个终端命令窗口执行如下命令:

  1. 使用 TLS 加密连接方式连接到 Docker 服务。
docker --tlsverify --tlscacert=/home/docker/.docker/ca.pem --tlscert=/home/docker/.docker/cert.pem --tlskey=/home/docker/.docker/client-key.pem -H=192.168.40.111:2376 image ls