默认情况下,docker使用sock文件形式启动,不对外提供访问接口,想要使用类似 docker-py 这种包来远程调用docker的 API,则必须将docker监听在某个端口来提供服务,但直接暴露HTTP接口是很危险的,攻击者可以很轻松的通过docker暴露的端口进入某个容器,进而获取高级权限,所以必须使用 tlsverify
参数和 tlscacert
参数指向一个受信任的CA,这样客户端必须通过使用CA签名的证书访问docker接口才可以
为方便批量配置,整理脚本如下
#!/bin/bash
#
set -e
certs_dir=docker_certs # 存放证书的路径
HOSTNAME=$(hostname)
DOCKER_PORT=2376 # 暴露的docker端口
if [ ! -d $certs_dir ];then
mkdir -p $certs_dir
fi
您暂时无权查看此隐藏内容!
systemctl daemon-reload
systemctl restart docker
直接执行脚本即可
验证:
如下所示,如果出现 API listen on [::]:2376
和 API listen on /var/run/docker.sock
表示修改成功了
[root@localhost certs]# systemctl status docker
● docker.service - Docker Application Container Engine
Loaded: loaded (/usr/lib/systemd/system/docker.service; disabled; vendor preset: disabled)
Active: active (running) since Tue 2020-04-21 22:10:13 CST; 6min ago
Docs: https://docs.docker.com
Main PID: 21419 (dockerd)
Memory: 30.7M
CGroup: /system.slice/docker.service
└─21419 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock -H=0.0.0.0:2376 --tlsverify --tlscacert=/root/certs/docker_certs/ca.pe...
Apr 21 22:10:13 myecs dockerd[21419]: time="2020-04-21T22:10:13.308119401+08:00" level=info msg="[graphdriver] using prior storage driver: overlay2"
Apr 21 22:10:13 myecs dockerd[21419]: time="2020-04-21T22:10:13.335533645+08:00" level=info msg="Graph migration to content-addressability took 0.00 seconds"
Apr 21 22:10:13 myecs dockerd[21419]: time="2020-04-21T22:10:13.336893667+08:00" level=info msg="Loading containers: start."
Apr 21 22:10:13 myecs dockerd[21419]: time="2020-04-21T22:10:13.533743541+08:00" level=info msg="Default bridge (docker0) is assigned with an IP addres...P address"
Apr 21 22:10:13 myecs dockerd[21419]: time="2020-04-21T22:10:13.584586725+08:00" level=info msg="Loading containers: done."
Apr 21 22:10:13 myecs dockerd[21419]: time="2020-04-21T22:10:13.614190810+08:00" level=info msg="Docker daemon" commit=d14af54 graphdriver(s)=overlay2 ...on=18.09.4
Apr 21 22:10:13 myecs dockerd[21419]: time="2020-04-21T22:10:13.614297021+08:00" level=info msg="Daemon has completed initialization"
Apr 21 22:10:13 myecs dockerd[21419]: time="2020-04-21T22:10:13.621970279+08:00" level=info msg="API listen on [::]:2376"
Apr 21 22:10:13 myecs dockerd[21419]: time="2020-04-21T22:10:13.622030438+08:00" level=info msg="API listen on /var/run/docker.sock"
Apr 21 22:10:13 myecs systemd[1]: Started Docker Application Container Engine.
Hint: Some lines were ellipsized, use -l to show in full.
[root@localhost certs]#
客户端连接
首先需要将 CA 证书以及 client 证书拷贝到客户端机器上
使用命令行连接
[root@localhost docker_certs]# docker --tlsverify --tlscacert=ca.pem --tlscert=client-cert.pem --tlskey=client-key.pem -H=52.99.139.56:2376 version
Client:
Version: 18.09.4
API version: 1.39
Go version: go1.10.8
Git commit: d14af54266
Built: Wed Mar 27 18:34:51 2019
OS/Arch: linux/amd64
Experimental: false
Server: Docker Engine - Community
Engine:
Version: 18.09.4
API version: 1.39 (minimum version 1.12)
Go version: go1.10.8
Git commit: d14af54
Built: Wed Mar 27 18:04:46 2019
OS/Arch: linux/amd64
Experimental: false
[root@localhost docker_certs]#
注意:当使用证书连接docker时,即使是普通用户执行,也不需要使用 sudo,这也就意味着任何用户使用证书连接docker都能发送任何指令,甚至获取 root 权限,所以要将证书相关文件妥善保管
设置客户端默认使用证书连接
在用户家目录下创建 .docker
目录
mkdir -pv $HOME/.docker
将 CA 证书以及 client 证书拷贝到 .docker
目录下
cp -a {ca,client-cert,client-key}.pem $HOME/.docker
配置环境变量
export DOCKER_HOST=tcp://52.99.139.56:2376 DOCKER_TLS_VERIFY=1
验证
[root@localhost docker_certs]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
[root@localhost docker_certs]#
如果想把证书文件放在其他位置,比如 /docker/certs
目录,则需要配置 DOCKER_CERT_PATH
环境变量,比如:
export DOCKER_CERT_PATH=/docker/certs/
docker --tlsverify ps
使用 docker-py 连接
import docker
tls_config = docker.tls.TLSConfig(
client_cert=('/root/certs/docker_certs/client-cert.pem', '/root/certs/docker_certs/client-key.pem'),
ca_cert='/root/certs/docker_certs/ca.pem',
verify='/root/certs/docker_certs/ca.pem'
)
client = docker.DockerClient(base_url='tcp://52.99.139.56:2376', tls=tls_config)
print(client.version())
正常会输出docker版本信息:
{'Platform': {'Name': 'Docker Engine - Community'}, 'Components': [{'Name': 'Engine', 'Version': '18.09.4', 'Details': {'ApiVersion': '1.39', 'Arch': 'amd64', 'BuildTime': '2019-03-27T18:04:46.000000000+00:00', 'Experimental': 'false', 'GitCommit': 'd14af54', 'GoVersion': 'go1.10.8', 'KernelVersion': '3.10.0-1062.18.1.el7.x86_64', 'MinAPIVersion': '1.12', 'Os': 'linux'}}], 'Version': '18.09.4', 'ApiVersion': '1.39', 'MinAPIVersion': '1.12', 'GitCommit': 'd14af54', 'GoVersion': 'go1.10.8', 'Os': 'linux', 'Arch': 'amd64', 'KernelVersion': '3.10.0-1062.18.1.el7.x86_64', 'BuildTime': '2019-03-27T18:04:46.000000000+00:00'}
使用 curl 连接
curl https://52.99.139.56:2376/images/json --cert ./client-cert.pem --key client-key.pem --cacert ca.pem
注意:指定 client-cert.pem
的时候,不能直接使用相对路径,会报错:curl: (58) NSS: client certificate not found: client-cert.pem
,
必须使用 ./client-cert.pem
指定
使用 -vvv
排查:
[root@localhost docker_certs]# curl https://52.99.139.56:2376/images/json --cert client-cert.pem --key client-key.pem --cacert ca.pem -vvv
* About to connect() to 127.0.0.1 port 12345 (#0)
* Trying 127.0.0.1...
* Connected to 127.0.0.1 (127.0.0.1) port 12345 (#0)
* Initializing NSS with certpath: sql:/etc/pki/nssdb
* CAfile: ca.pem
CApath: none
* warning: certificate file name "client-cert.pem" handled as nickname; please use "./client-cert.pem" to force file name
* NSS: client certificate not found: client-cert.pem
* NSS error -12271 (SSL_ERROR_BAD_CERT_ALERT)
* SSL peer cannot verify your certificate.
* Closing connection 0
curl: (58) NSS: client certificate not found: client-cert.pem