เปิด Remote Connect ไปที่ Docker Daemon และปลอดภัย EP 2 :: Configuration

จากหลักการ แนวคิดในตอนแรก ​เปิด Remote Connect ไปที่ Docker Daemon และปลอดภัย EP 1 :: Concept ต่อด้วย EP2 นำไปสู่การกำหนดค่าต่าง ๆ บน server และ client เพื่อนำไปใช้งานได้จริง

0. ระบบที่ใช้ในการทดสอบในตอนนี้มี เครื่อง 2 เครื่อง เครื่อง server มีชื่อว่า dockerd.example.com (192.168.254.99) และ เครื่อง client มีชื่อว่า dockerc.example.com (192.168.254.98) โดยที่เครื่อง dockerd.example.com ติดตั้ง docker-ce และ ใช้งานได้เรียบร้อยแล้ว ส่วนเครื่อง dockerc.example.com ติดตั้งเฉพาะ docker-ce -cli

  1. สร้าง คู่ Key Pair สำหรับ Certificate Authority, Server และ Client และ ทำการ Signed Certificate ของ Server และ Client ด้วย CA
host]$ export HOST=dockerd.example.com
###### CA
host]$ openssl genrsa -aes256 -out ca-key.pem 4096
host]$ openssl req -new -x509 -days 365 -key ca-key.pem -sha256 -out ca-cert.pem -subj "/CN=$HOST"
####### SERVER
host]$ openssl genrsa -out server-key.pem 4096
host]$ openssl req -subj "/CN=$HOST" -sha256 -new -key server-key.pem -out server.csr
host]$ echo subjectAltName = DNS:$HOST,IP:192.168.254.99,IP:127.0.0.1 >> extfile.cnf
host]$ echo extendedKeyUsage = serverAuth >> extfile.cnf
host]$ openssl x509 -req -days 365 -sha256 -in server.csr -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem -extfile extfile.cnf
####### Client
host]$ openssl genrsa -out client1-key.pem 4096
host]$ openssl req -subj '/CN=client1' -new -key client1-key.pem -out client1.csr
host]$ echo extendedKeyUsage = clientAuth >> extfile2.cnf
host]$ openssl x509 -req -days 365 -sha256 -in client1.csr -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial -out client1-cert.pem -extfile extfile2.cnf

2. copy key ไปยัง server และ client

###### Server
host]$ ssh root@dockerd.example.com 'mkdir /etc/docker/tls'
host]$ scp ca-cert.pem server-key.pem server-cert.pem root@dockerd.example.com:/etc/docker/tls/

###### Client
host]$ ssh root@dockerc.example.com 'mkdir ~/.docker'
host]$ scp ca-cert.pem client1-key.pem client1-cert.pem root@dockerd.example.com:~/.docker/

3. แก้ไข docker.service ที่ server

### เดิม บรรทัดที่ขึ้นต้นด้วย ExecStart จะมีค่าดังนี้
server]# cat /usr/lib/systemd/system/docker.service |grep ExecStart
ExecStart=/usr/bin/dockerd -H unix://
### แก้ไขเป็น 
server]# cat /usr/lib/systemd/system/docker.service |grep ExecStart
ExecStart=/usr/bin/dockerd -H unix:// -H tcp://0.0.0.0:2376 --tlsverify --tlscacert=/etc/docker/tls/ca-cert.pem --tlscert=/etc/docker/tls/server-cert.pem --tlskey=/etc/docker/tls/server-key.pem
server]# systemctl daemon-reload
server]# systemctl restart docker

4. ทดสอบการใช้งานด้วยคำสั่ง docker version

client]# docker --tlsverify --tlscacert ~/.docker/ca-cert.pem --tlscert ~/.docker/client1-cert.pem --tlskey ~/.docker/client1-key.pem -H dockerd.example.com:2376 version
Client:
Version: 18.09.0
API version: 1.39
Go version: go1.10.4
Git commit: 4d60db4
Built: Wed Nov 7 00:48:22 2018
OS/Arch: linux/amd64
Experimental: false
Server: Docker Engine - Community
Engine:
Version: 18.09.0
API version: 1.39 (minimum version 1.12)
Go version: go1.10.4
Git commit: 4d60db4
Built: Wed Nov 7 00:19:08 2018
OS/Arch: linux/amd64
Experimental: false

ทดสอบเพิ่มเติม

  1. หลังจาก เพิ่มความปลอดภัยให้กับ docker daemon เรียบร้อย ถ้าขอเชื่อมต่อไปด้วย http สิ่งที่เกิดขึ้นคือ docker daemon จะไม่ตอบอะไรกับมา
client]# curl -v http://dockerd.example.com:2376/v1.39/version
* About to connect() to dockerd.example.com port 2376 (#0)
* Trying 192.168.254.99...
* Connected to dockerd.example.com (192.168.254.99) port 2376 (#0)
> GET /v1.39/version HTTP/1.1
> User-Agent: curl/7.29.0
> Host: dockerd.example.com:2376
> Accept: */*
>
* Connection #0 to host dockerd.example.com left intact

2. ถ้าขอเชื่อมต่อด้วย https จะพบข้อความว่า Peer’s Certificate issuer is not recognized.

client]# curl -v https://dockerd.example.com:2376/v1.39/version
* About to connect() to dockerd.example.com port 2376 (#0)
* Trying 192.168.254.99...
* Connected to dockerd.example.com (192.168.254.99) port 2376 (#0)
* Initializing NSS with certpath: sql:/etc/pki/nssdb
* CAfile: /etc/pki/tls/certs/ca-bundle.crt
CApath: none
* Server certificate:
* subject: CN=dockerd.example.com
* start date: Nov 18 06:22:57 2018 GMT
* expire date: Nov 18 06:22:57 2019 GMT
* common name: dockerd.example.com
* issuer: CN=docker.example.com,OU=MyHome,O=MyOrganization,L=Bangkok,ST=Bangkok,C=TH
* NSS error -8179 (SEC_ERROR_UNKNOWN_ISSUER)
* Peer's Certificate issuer is not recognized.
* Closing connection 0
curl: (60) Peer's Certificate issuer is not recognized.
More details here: http://curl.haxx.se/docs/sslcerts.html
curl performs SSL certificate verification by default, using a "bundle"
of Certificate Authority (CA) public keys (CA certs). If the default
bundle file isn't adequate, you can specify an alternate file
using the --cacert option.
If this HTTPS server uses a certificate signed by a CA represented in
the bundle, the certificate verification probably failed due to a
problem with the certificate (it might be expired, or the name might
not match the domain name in the URL).
If you'd like to turn off curl's verification of the certificate, use
the -k (or --insecure) option.

3. ถ้าต้องการทดสอบด้วยคำสั่ง curl

client]# curl --cacert ~/.docker/ca-cert.pem --cert ~/.docker/client1-cert.pem --key ~/.docker/client1-key.pem -s https://dockerd.example.com:2376/v1.39/version | python -m json.tool
{
"ApiVersion": "1.39",
"Arch": "amd64",
"BuildTime": "2018-11-07T00:19:08.000000000+00:00",
"Components": [
{
"Details": {
"ApiVersion": "1.39",
"Arch": "amd64",
"BuildTime": "2018-11-07T00:19:08.000000000+00:00",
"Experimental": "false",
"GitCommit": "4d60db4",
"GoVersion": "go1.10.4",
"KernelVersion": "3.10.0-862.14.4.el7.x86_64",
"MinAPIVersion": "1.12",
"Os": "linux"
},
"Name": "Engine",
"Version": "18.09.0"
}
],
"GitCommit": "4d60db4",
"GoVersion": "go1.10.4",
"KernelVersion": "3.10.0-862.14.4.el7.x86_64",
"MinAPIVersion": "1.12",
"Os": "linux",
"Platform": {
"Name": "Docker Engine - Community"
},
"Version": "18.09.0"
}
#MWriter