Analysis and configuration verification of TLS encrypted communication for Docker security

Keywords: Linux Docker OpenSSL Nginx vim

The basic concept of TLS

TLS (Transport Layer Security), TLS is a protocol built on the TCP protocol of the transmission layer, serving the application Layer. Its predecessor is SSL (Secure Socket Layer), which realizes the function of encrypting the message of the application layer and then delivering it to TCP for transmission.

TLS protocol has three characteristics: confidentiality (data is encrypted transmission, preventing third-party sniffing), data integrity (based on MAC verification mechanism), two-way authentication support (to avoid identity being impersonated)

In Docker, TLS encryption is established to prevent link hijacking, session hijacking and other problems from causing Docker communication to be conducted by an intermediary. Both ends of c/s should communicate through encryption.

Diagram TLS establishment process

Process diagram of TLS C/S connection establishment (complete i.e. bidirectional verification process)

In general, it is sending hello packets, client sending authentication and request authentication, corresponding response authentication, then corresponding negotiation verification, generating information, and finally both sides start to prepare encrypted communication. Both sides use client key to encrypt the communication content through symmetric encryption algorithm, and then conduct two-way communication. With the end of communication, either side can disconnect the ssl connection information.

Deployment configuration process

Basic environment required

Two servers, one server and one client, are deployed and installed with docker CE

Configuration process

1. Preparation: on the server node

#Create working directory folder
[root@localhost ~]# mkdir /tls
[root@localhost ~]# cd /tls/
#Set host name
[root@localhost tls]# hostnamectl set-hostname server
[root@localhost tls]# su
[root@server tls]# vim /etc/hosts
127.0.0.1   server
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
#Conduct the test
[root@server tls]# ping server
PING server (127.0.0.1) 56(84) bytes of data.
64 bytes from server (127.0.0.1): icmp_seq=1 ttl=64 time=0.033 ms
64 bytes from server (127.0.0.1): icmp_seq=2 ttl=64 time=0.029 ms
64 bytes from server (127.0.0.1): icmp_seq=3 ttl=64 time=0.031 ms
64 bytes from server (127.0.0.1): icmp_seq=4 ttl=64 time=0.084 ms
^C
--- server ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3012ms
rtt min/avg/max/mdev = 0.029/0.044/0.084/0.023 ms

2. Create ca key certificate and ca certificate

#Create ca key certificate
[root@server tls]# openssl genrsa -aes256 -out ca-key.pem 4096
Generating RSA private key, 4096 bit long modulus
...........................................................................++
.................................++
e is 65537 (0x10001)
#You need to set your own password
Enter pass phrase for ca-key.pem:
Verifying - Enter pass phrase for ca-key.pem:

#Create ca certificate
[root@server tls]# openssl req -new -x509 -days 1000 -key ca-key.pem -sha256 -subj "/CN=*" -out ca.pem
#Now you need to enter the password you just set interactively
Enter pass phrase for ca-key.pem:

3. Server private key certificate and generate signing private key

#Create server private key certificate
[root@server tls]# openssl genrsa -out server-key.pem 4096
Generating RSA private key, 4096 bit long modulus
...........................................++
...........................................................................................++
e is 65537 (0x10001)

#Using the ca certificate created above and the server private key to generate the signature private key
[root@server tls]# openssl req -subj "/CN=*" -sha256 -new -key server-key.pem -out server.csr#Sign with the ca certificate and private key created above

#View the corresponding certificate
[root@server tls]# ls
ca-key.pem  ca.pem  server.csr  server-key.pem

#Sign
[root@server tls]# openssl x509 -req -days 1000 -sha256 -in server.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem
Signature ok
subject=/CN=*
Getting CA Private Key
#Input password
Enter pass phrase for ca-key.pem:
[root@server tls]# ls
ca-key.pem  ca.pem  ca.srl  server-cert.pem  server.csr  server-key.pem

4. Generate client key and sign client

#Generate client key
[root@server tls]# openssl genrsa -out key.pem 4096
Generating RSA private key, 4096 bit long modulus
.........................................................................................................++
...............................................++
e is 65537 (0x10001)
#Sign client
[root@server tls]# openssl req -subj "/CN=client" -new -key key.pem -out client.csr

5. Create a profile to enhance the key

#Enhanced key file creation
[root@server tls]# echo extendedKeyUsage=clientAuth > extfile.cnf
#Signing certificate
[root@server tls]# openssl x509 -req -days 1000 -sha256 -in client.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out cert.pem -extfile extfile.cnf
Signature ok
subject=/CN=client
Getting CA Private Key
Enter pass phrase for ca-key.pem:

6. Remove redundant files and set up the docker service

[root@server tls]# rm -rf ca.srl client.csr extfile.cnf server.csr

[root@server tls]# vim /lib/systemd/system/docker.service
//Save and exit after modifying the lower 14 lines (quasi start mode setting)
14 ExecStart=/usr/bin/dockerd --tlsverify --tlscacert=/tls/ca.pem --tlscert=/tls/server-cert.pem --tlskey=/tls/server-ke    y.pem -H tcp://0.0.0.0:2376 -H unix:///var/run/docker.sock

7. Reload service and restart docker

[root@server tls]# systemctl daemon-reload
[root@server tls]# systemctl restart docker.service 

8. Copy / TLS / ca.pem/tls/cert.pem/tls/key.pem to the client host

[root@server tls]# ls
ca-key.pem  ca.pem  cert.pem  key.pem  server-cert.pem  server-key.pem
[root@server tls]# scp ca.pem cert.pem key.pem root@192.168.0.136:/etc/docker
root@192.168.0.136's password: 
ca.pem                                                                                 100% 1765   894.4KB/s   00:00    
cert.pem                                                                               100% 1696     1.7MB/s   00:00    
key.pem                                                                                100% 3243     2.6MB/s   00:00  

client settings

1. Check whether the three files copied remotely exist

[root@localhost ~]# ls /etc/docker/
ca.pem  cert.pem  daemon.json  key.json  key.pem

2. Environment installation and setup host name and hosts file

[root@localhost ~]# systemctl status docker.service 
● docker.service - Docker Application Container Engine
   Loaded: loaded (/usr/lib/systemd/system/docker.service; disabled; vendor preset: disabled)
   Active: active (running) since IV. 2020-04-30 09:31:18 CST; 3min 20s ago
     Docs: https://docs.docker.com

[root@localhost ~]# hostnamectl set-hostname client
[root@localhost ~]# su
[root@client ~]# vim /etc/hosts
[root@client ~]# cat /etc/hosts
192.168.0.135 server
[root@client ~]# ping server
PING server (192.168.0.135) 56(84) bytes of data.
64 bytes from server (192.168.0.135): icmp_seq=1 ttl=64 time=0.444 ms
64 bytes from server (192.168.0.135): icmp_seq=2 ttl=64 time=0.345 ms
64 bytes from server (192.168.0.135): icmp_seq=3 ttl=64 time=1.09 ms
^C
--- server ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2002ms
rtt min/avg/max/mdev = 0.345/0.628/1.096/0.333 ms

3. Enter the / etc/docker directory to view the docker version on the server side for verification

[root@client docker]# docker --tlsverify --tlscacert=ca.pem --tlscert=cert.pem --tlskey=key.pem -H tcp://server:2376 version
Client: Docker Engine - Community
 Version:           19.03.8
 API version:       1.40
 Go version:        go1.12.17
 Git commit:        afacb8b
 Built:             Wed Mar 11 01:27:04 2020
 OS/Arch:           linux/amd64
 Experimental:      false
#Above is the client, below is the server
Server: Docker Engine - Community
 Engine:
  Version:          19.03.8
  API version:      1.40 (minimum version 1.12)
  Go version:       go1.12.17
  Git commit:       afacb8b
  Built:            Wed Mar 11 01:25:42 2020
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.2.13
  GitCommit:        7ad184331fa3e55e52b890ea95e65ba581ae3429
 runc:
  Version:          1.0.0-rc10
  GitCommit:        dc9208a3303feef5b3839f4323d9beb36df0a9dd
 docker-init:
  Version:          0.18.0
  GitCommit:        fec3683

After TLS encryption communication and configuration of the above docker container is completed, a simple test is carried out below

Final test

Pull an image on the server side and view it on the client side

#Pull a nginx image
[root@server tls]# docker pull nginx
Using default tag: latest
latest: Pulling from library/nginx
54fec2fa59d0: Pull complete 
4ede6f09aefe: Pull complete 
f9dc69acb465: Pull complete 
Digest: sha256:86ae264c3f4acb99b2dee4d0098c40cb8c46dcf9e1148f05d3a51c4df6758c12
Status: Downloaded newer image for nginx:latest
docker.io/library/nginx:latest
[root@server tls]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED                  SIZE
nginx               latest              602e111c06b6        Less than a second ago   127MB

#View image on client
[root@client docker]# docker --tlsverify --tlscacert=ca.pem --tlscert=cert.pem --tlskey=key.pem -H tcp://server:2376 images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
nginx               latest              602e111c06b6        6 days ago          127MB

The test is correct (just look at the image ID), thank you for reading!

Posted by guoxin on Sun, 03 May 2020 09:09:02 -0700