Docker 安全与监控

TLS 加密配置

Docker Daemon 监听 Socket 有三种方式,分别是 unix、tcp 和 fd。其中,监听 fd 文件是使用 system 的系统专用。

在默认配置中,Docker Remote API 通过 Socket 监听来自本地的连接,监听地址位于 unix:///var/run/docker.sock。可以通过 curl 发送一个简单的请求来测试:

[root@server4 .docker]$ curl --unix-socket /var/run/docker.sock http://localhost/info | python -mjson.tool
{
    "Architecture": "x86_64",
    "BridgeNfIp6tables": false,
    "BridgeNfIptables": false,
    "CPUSet": true,
    "CPUShares": true,
    "CgroupDriver": "cgroupfs",

可以通过 -H 选项来修改监听方式:

[root@server1 ~]$ dockerd -H 192.168.2.234:5999 -H unix:///var/run/docker.sock &

客户端可以通过 docker -H 指定服务端地址来操作服务端。也可以设置 Docker_HOST 变量固定服务器地址:

[root@server4 ~]$ docker -H tcp://192.168.2.234:5999 pull ubuntu

在 Docker 中规定 2375 作为非加密端口,2376 作为加密端口。如果没有启用 TLS,则服务端与客户端的通信没有认证和加密,任何客户端口可以向服务端发送命令。

使用 TLS 通信需要在服务端和客户端中加入 TLS 相关的配置。首先创建 RSA 私钥 ca.key

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

使用 RSA 私钥创建 CA 证书 ca.pem。参数 -x509 是生成自签名 CA 证书,参数 -days 365 设置证书有效期:

[root@server1 ~]$ openssl req -new -x509 -days 365 -key ca.key -sha256 -out ca.pem
Enter pass phrase for ca.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) [XX]:CN
State or Province Name (full name) []:BeiJing
Locality Name (eg, city) [Default City]:BeiJing
Organization Name (eg, company) [Default Company Ltd]:server1
Organizational Unit Name (eg, section) []:Dev
Common Name (eg, your name or your server's hostname) []:server1
Email Address []:root@server1.com
[root@server1 ~]$ scp ca.* root@192.168.2.241:/root
root@192.168.2.241's password: 
ca.key                                                 100% 3326     2.7MB/s   00:00    
ca.pem                                                 100% 2094     2.4MB/s   00:00    
ca.srl                                                 100%   17    24.3KB/s   00:00

创建服务器私钥和 CSR。其中 server.key 是服务器私钥的文件名,server.csr 是服务器 CSR 文件名:

[root@server1 ~]$ openssl genrsa -out server.key 4096
Generating RSA private key, 4096 bit long modulus
................++
............................................................................................................................................................................................................................................++
e is 65537 (0x10001)
[root@server1 ~]$ openssl req -subj "/CN=server1" -sha256 -new -key server.key -out server.csr

使用 CA 证书创建服务器证书文件,可以限制连接客户端 IP:

[root@server1 ~]$ echo subjectAltName = IP:192.168.2.234,IP:192.168.2.241,IP:127.0.0.1 > allowip.cnf
[root@server1 ~]$ openssl x509 -req -days 365 -sha256 -in server.csr -CA ca.pem -CAkey ca.key -CAcreateserial -out server.pem -extfile allowip.cnf
Signature ok
subject=/CN=server1
Getting CA Private Key
Enter pass phrase for ca.key:

创建客户端私钥和 CSR:

[root@server4 ~]$ openssl genrsa -out client.key 4096
Generating RSA private key, 4096 bit long modulus
................................................................................................................................................................................................................................................++
...............................................................................................................++
e is 65537 (0x10001)
[root@server4 ~]$ openssl req -subj '/CN=server4' -new -key client.key -out client.csr

使用 CA 证书创建客户端证书文件,需要加入 extendedKeyUsage 选项:

[root@server4 ~]$ echo extendedKeyUsage = clientAuth > client.cnf
[root@server4 ~]$ openssl x509 -req -days 365 -sha256 -in client.csr -CA ca.pem -CAkey ca.key -CAcreateserial -out client.pem -extfile client.cnf 
Signature ok
subject=/CN=server4
Getting CA Private Key
Enter pass phrase for ca.key:

创建 PEM 文件之后可以删除 CSR 文件。修改证书权限并移动到 ~/.docker/ 目录下:

[root@server4 ~]$ rm -f client.cnf client.csr 
[root@server4 ~]$ chmod -v 0400 *.key
mode of ‘ca.key’ changed from 0644 (rw-r--r--) to 0244 (r--r--r--)
mode of ‘client.key’ changed from 0644 (rw-r--r--) to 0244 (r--r--r--)
[root@server4 ~]$ chmod -v 0444 *.pem
mode of ‘ca.pem’ changed from 0644 (rw-r--r--) to 0444 (r--r--r--)
mode of ‘client.pem’ changed from 0644 (rw-r--r--) to 0444 (r--r--r--)
[root@server4 ~]$ mkdir .docker
[root@server4 ~]$ cp ca.pem client.* .docker/
[root@server1 ~]$ rm -f server.csr allowip.cnf 
[root@server1 ~]$ chmod -v 0400 *.key
mode of ‘ca.key’ changed from 0644 (rw-r--r--) to 0244 (r--r--r--)
mode of ‘server.key’ changed from 0644 (rw-r--r--) to 0244 (r--r--r--)
[root@server1 ~]$ chmod -v 0444 *.pem
mode of ‘ca.pem’ changed from 0644 (rw-r--r--) to 0444 (r--r--r--)
mode of ‘server.pem’ changed from 0644 (rw-r--r--) to 0444 (r--r--r--)
[root@server1 ~]$ mkdir .docker
[root@server1 ~]$ cp ca.pem server.* .docker/

修改服务端配置文件并重启:

[root@server1 ~]$ vi /etc/docker/daemon.json
{
    "tlsverify": true,
    "tlscacert": "/root/.docker/ca.pem",
    "tlscert": "/root/.docker/server.pem",
    "tlskey": "/root/.docker/server.key"
}
[root@server1 ~]$ systemctl restart docker

修改客户端配置文件并重启:

[root@server4 ~]$ vi /etc/docker/daemon.json
{
    "tlsverify": true,
    "tlscacert": "/root/.docker/ca.pem",
    "tlscert": "/root/.docker/client.pem",
    "tlskey": "/root/.docker/client.key"
}
[root@server4 ~]$ systemctl restart docker

客户端测试连接:

[root@server4 ~]$ cd /root/.docker/
[root@server4 ~]$ docker --tlsverify --tlscacert=ca.pem --tlscert=client.pem --tlskey=client.key -H tcp://192.168.2.234:5999 ps -a
CONTAINER ID   IMAGE           COMMAND           CREATED       STATUS                      PORTS     NAMES
7198e1cdf396   pytest/volume   "/bin/sleep 60"   5 hours ago   Exited (0) 5 hours ago                clever_aryabhata

Docker Bench

Docker Bench 是一个开源项目,该项目按照互联网安全中心(CIS,Center for Internet Security)对于 Docker 的安全规范进行一系列环境检查。

可以利用 Docker 镜像快速进行检查:

[root@server4 ~]$ docker run -it --net host --pid host --userns host --cap-add audit_control -e DOCKER_CONTENT_TRUST=$DOCKER_CONTENT_TRUST -v /var/lib:/bar/lib -v /var/run/docker.sock:/var/run/docker.sock -v /usr/lib/systemd:/usr/lib/systemd -v /etc:/etc --label docker_bench_security docker/docker-bench-security

Sysdig

Sysdig 是一个轻量级的系统监控工具,原生支持容器。可以使用容器来运行:

[root@server4 ~]$ docker run -it --rm --name=sysdig --privileged --volume=/var/run/docker.sock:/host/var/run/docker.sock --volume=/dev/:/host/dev/ --volume=/proc/:/host/proc/:ro --volume=/boot/:/host/boot/:ro --volume=/lib/modules/:/host/lib/modules/:ro --volume=/usr/:/host/usr/:ro sysdig/sysdig

启动后进入容器执行 csysdig 命令:

root@213496097e95:/# csysdig
   PID   PPID     CPU USER          TH     VIRT      RES    FILE     NET Command
  1250      1    0.50 root          16       1G      40M       0    0.00 /usr/bin/contain
  5459   5298    0.50 root           2     362M      15M       0    0.00 csysdig
  1500      1    0.00 root           1      88M       2M       0    0.00 /usr/libexec/pos
  1506   1500    0.00                1      88M       4M       0    0.00 pickup -l -t uni
  4624   4511    0.00 www-data       1     222M       9M       0    0.00 apache2 -DFOREGR
   954      1    0.00                2      65M       3M       0    0.00 /usr/bin/dbus-da
  4475   1477    0.00 root           7     806M      10M       0    0.00 /usr/bin/docker-
   947      1    0.00 root           3     267M       5M       0    0.00 /usr/bin/vmtools
  5254   5233    0.00 root          11       1G      39M      4K    0.00 docker run -it -
  4291   4272    0.00                8       1G      74M       0    0.00 mysqld
  1036      1    0.00 root           1     108M     860K       0    0.00 /sbin/agetty --n
  1507   1500    0.00                1      88M       4M       0    0.00 qmgr -l -t unix
   948      1    0.00                7     598M      14M       0    0.00 /usr/lib/polkit-
F1Help  F2Views F4FilterF5Echo  F6Dig   F7LegendF8ActionsF9Sort  F12Spe       8/43(18.6%)

Weave Scope

Weave Scope 可以将 Docker 容器分布生成一张地图,看起来很直观:

[root@server4 ~]$ sudo curl -L https://github.com/weaveworks/scope/releases/download/latest_release/scope -o /usr/local/bin/scope
[root@server4 ~]$ chmod a+x /usr/local/bin/scope 
[root@server4 ~]$ scope launch
Scope probe started
Weave Scope is listening at the following URL(s):
  * http://192.168.2.204:4040/
  * http://172.18.0.1:4040/

使用 scope launch 将以容器方式启动 Weave Scope,之后通过 http://192.168.2.204:4040/ 在浏览器访问。

如果需要监控多台主机,在 scope launch 后面加上对应主机的 IP 地址,并在每台主机上运行:

[root@server4 ~]$ scope launch 192.168.2.204 192.168.2.205 192.168.2.206

加入基本账号验证:

[root@server4 ~]$ scope launch -app.basicAuth -app.basicAuth.password 123456 -app.basicAuth.username user -probe.basicAuth -probe.basicAuth.password 123456 -probe.basicAuth.username user

ELK

ELK 套件是三个软件的合称:Elasticsearch(搜索引擎)、Logstash(日志分析)和 Kibana(可视化)。

下面采用最小部署。其中,5601 是 Kibana web 接口,9200 是 Elasticsearch JSON 接口,5044 是 Logstash 日志接口:

[root@server4 ~]$ docker run -d -p 5601:5601 -p 9200:9200 -p 5044:5044 -it --name elk sebp/elk

如果启动报错 “vm.max_map_count [65530] is too low”,需要修改 /etc/sysctl.conf 文件:

[root@server4 ~]$ vi /etc/sysctl.conf
vm.max_map_count=262144
[root@server4 ~]$ sysctl -p
net.ipv4.ip_forward = 1
vm.max_map_count = 262144

启动之后通过 http://192.168.2.204:5601/ 来访问。由于目前没有任何日志,所以还需要使用 Filebeat 来配合,它能将指定路径下的日志文件转发给 ELK:

[root@server4 ~]$ curl -L -O https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-7.3.0-x86_64.rpm
[root@server4 ~]$ rpm -vi filebeat-7.3.0-x86_64.rpm

之后修改 Filebeat 配置文件:

[root@server4 ~]$ vi /etc/filebeat/filebeat.yml 
#=========================== Filebeat inputs =============================
- type: log
  enabled: true
  paths:
    - /var/lib/docker/containers/*/*.log
    - /var/log/*.log
#-------------------------- Elasticsearch output ------------------------------
output.elasticsearch:
  # Array of hosts to connect to.
  hosts: ["localhost:9200"]
[root@server4 ~]$ filebeat setup -e

启动 Filebeat 后,监控的日志会发送给 Elasticsearch:

[root@server4 ~]$ systemctl start filebeat.service

访问 http://192.168.2.204:5601,进入 Kibana 的 Discover 页面,即可查看日志。