本文根據官方資料實踐而成,同時對於本人操作時遇到的問題加以整理總結.通過本文,你應該能夠實現通過自定義證書實現對遠程Docker主機的安全訪問和控制.尊重勞動成果,未經本人允許不得轉載.
文章目錄
一 Docker服務端,客戶端和CA證書
默認情況下,Docker通過非聯網的Unix套接字運行。它還可以選擇使用HTTP套接字進行通信。
這裏有三個角色,Docker服務端和Docker客戶端和CA簽名的證書
- Docker服務端:對應運行Docker守護程序的主機
- 通過開放端口(一般爲2376)支持遠程連接
- 通過指定tlsverify標誌並將Docker的tlscacert標誌指向可信的CA證書來啓用TLS以實現安全訪問(僅允許由該CA簽名的證書進行身份驗證的客戶端連接)
- Docker客戶端:即默認情況下的Docker主機
- 當使用證書連接時,僅能連接到具有該CA簽名的證書的服務器
- CA簽名的證書
- 作用:服務端和客戶端證書都只對應一份信任列表,信任列表裏是服務端的信息(如ip或域名等等),服務端持有服務端證書,僅接受持有客戶端證書的主機訪問(這裏可以再加上其他的限制,詳情見下文)
注意:
-
Docker服務端也可以不開啓TLS驗證,不過這樣子很不安全,生產環境下應當儘量避免.如果只是試驗性的,可以指定關閉TLS驗證,但只對特定主機開放,方法見後文
/etc/docker/daemon.json
配置相關 -
如果Docker服務端沒有開啓TLS驗證,則Docker客戶端不需要使用證書連接.但如果客戶端不使用證書連接開啓了TLS驗證的服務端,則會報錯,如下:
Get http://遠程主機ip:2376/v1.38/version: net/http: HTTP/1.x transport connection broken: malformed HTTP response "\x15\x03\x01\x00\x02\x02".
* Are you trying to connect to a TLS-enabled daemon without TLS?
- 如果Docker客戶端連接時使用的證書內不含目的主機的信息,則會提示對方主機不在證書信任列表內,訪問失敗
二 使用OpenSSL創建CA和服務端密鑰key
注意:將以下示例中的所有$HOST
實例替換爲Docker守護程序主機的域名(DNS name)(由於我沒有配域名,所以直接用主機ip代替了,有影響的地方我會再做提醒)
以下步驟在Docker服務端進行:
步驟1:生成CA私鑰ca-key.pem
- 說明:
ca-key.pem是一個臨時文件,最後可以刪除。 - 指令:
openssl genrsa -aes256 -out ca-key.pem 4096
例子如下,需要設置密碼並驗證
$ 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:
Verifying - Enter pass phrase for ca-key.pem:
步驟2:使用CA私鑰生成自簽名CA證書ca.pem
- 說明:生成證書時,通過-days 365設置證書的有效期。單位爲天,默認情況下爲30天。有了CA證書後,就可以創建服務器密鑰和證書籤名請求(CSR)了
- 指令:
openssl req -new -x509 -days 365 -key ca-key.pem -sha256 -out ca.pem
- 例子如下,需要驗證密碼並輸入信息,注意"Common Name"設置爲服務器所在主機的域名(我只填了Country Name 爲CN,Province Name爲Guangdong,後面直接回車也沒關係,反正也只是自己用的證書)
$ 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]:
State or Province Name (full name) [Some-State]:Queensland
Locality Name (eg, city) []:Brisbane
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Docker Inc
Organizational Unit Name (eg, section) []:Sales
Common Name (e.g. server FQDN or YOUR name) []:$HOST
Email Address []:[email protected]
步驟3:生成服務器私鑰server-key.pem和證書籤名請求server-csr
- 說明:
CSR:Certificate Signing Request,證書籤名請求,server-csr是一個臨時文件,生成server-cert.pem以後,可以刪除。
這裏分兩小步- 第一步生成服務器私鑰server-key.pem
- 第二步使用服務器私鑰另加CN信息生成證書籤名請求server-csr.pem
- 如下(注意這裏的CN信息對應的是服務器所在主機域名,如果沒有的話也沒關係,可以通過下一步的
extfile.cnf
配置IP地址連接):
$ openssl genrsa -out server-key.pem 4096
Generating RSA private key, 4096 bit long modulus
.....................................................................++
.................................................................................................++
e is 65537 (0x10001)
$ openssl req -subj "/CN=$HOST" -sha256 -new -key server-key.pem -out server.csr
步驟4:編寫extfile.cnf(重要)
-
說明:這個文件用於指定下一步生成簽名證書的一些屬性配置,這裏我們主要用到兩個屬性,如果要其他要求(如限制指定ip範圍的客戶端才能連接)的可以看OpenSSL x509v3_config文檔
- subjectAltName
主題備選名稱,是有點像上一步生成server.csr時所用的選項-subj "/CN=$HOST"
的東西,這個更像一個說明補充,這裏可以填信任的DNS域名和主機IP等等,因爲沒有域名,所以我只填了Docker服務器的主機IP
另外,需要特別注意,這裏對應生成的是一份信任列表,這裏所說的信任是對服務端的信任,所以填的是服務端的信息
(如域名,IP),我之前看到有文章說這裏的列表是客戶端的列表,只有在列表中的客戶端才能訪問服務器,這種說法是錯誤,在使用證書連接到服務器時,會報錯說服務器IP不在信任列表中( 如遠程主機ip爲ip3,證書的信任列表爲ip1和ip2時,若使用該證書訪問遠程主機,則會報錯x509: certificate is valid for ip1, ip2, not ip3
) - extendedKeyUsage
擴展密鑰用法,此擴展包含一個用法列表,用於指示證書公鑰可用於的目的
- subjectAltName
-
如下
1 將Docker守護程序密鑰的擴展使用屬性設置爲僅用於服務器身份驗證
2 將域名(DNS Name)$HOST和IP爲10.10.10.20(私有地址,用於局域網登錄)和127.0.0.1(本地地址,用於本機登錄)和公網ip(用於遠程登錄)
注意:
事實上只配一個公網IP就行了,其他的視實際需求而定,本機的話一般不那麼麻煩,通過docker本身的配置即可使用本機控制,這裏配127.0.0.1主要是爲了測試是否能連通,如果只配了私有地址則只有本局域網可訪問,安全性更高
echo extendedKeyUsage = serverAuth >> extfile.cnf
echo subjectAltName = DNS:$HOST,IP:10.10.10.20,IP:127.0.0.1,IP:公網ip >> extfile.cnf
步驟5:使用CA證書生成服務器簽名證書server-cert.pem
注意,上面總共生成了兩大模塊的文件和一個extfile.cnf配置文件,這三部分之間是彼此獨立的,到了這一步,也是最後一步才真正做了整合.
這裏要用到的有,(1)CA私鑰ca-key.pem和CA簽名文件ca.pem,(2)證書籤名請求server-csr(3)配置文件extfile.cnf
指令如下:
openssl x509 -req -days 365 -sha256 -in server.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem -extfile extfile.cnf
例:
$ 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=your.host.com
Getting CA Private Key
Enter pass phrase for ca-key.pem:
至此,Docker服務端密鑰創建完畢
三 利用CA創建客戶端密鑰key
注意:生成密鑰需要使用CA私鑰和簽名文件,爲簡化流程,避免CA文件在服務端和客戶端之間的傳輸,以下步驟仍在Docker服務端進行
創建客戶端密鑰的過程和服務端類似,CA相關已經創建好了,extfile.cnf配置文件也簡單很多,具體如下
步驟1:生成客戶端私鑰key.pem和證書籤名請求client-csr
如下
$ openssl genrsa -out key.pem 4096
Generating RSA private key, 4096 bit long modulus
.........................................................++
................++
e is 65537 (0x10001)
$ openssl req -subj '/CN=client' -new -key key.pem -out client.csr
步驟2:編寫擴展配置文件extfile.cnf
注意,如果是在剛纔創建服務器私鑰的文件夾下,應該還有原來的extfile.cnf文件,爲避免覆寫,可以先執行重命名
mv extfile.cnf extfile.cnf.old
創建擴展配置文件並使密鑰適用於客戶端身份驗證的指令如下:
echo extendedKeyUsage = clientAuth >> extfile.cnf
步驟3:生成簽名文件cert.pem
如下
$ openssl x509 -req -days 365 -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:
四 修改文件權限
步驟1:刪除兩個證書籤名請求文件
rm -v client.csr server.csr
步驟2:修改密鑰文件權限爲只由所有者讀取
chmod -v 0400 ca-key.pem key.pem server-key.pem
步驟3:修改證書文件權限爲只讀
chmod -v 0444 ca.pem server-cert.pem cert.pem
五 啓動Docker守護進程
啓動Docker守護進程有兩種方法,直接用帶參指令或者修改daemon.json配置文件,另外,還有一種方式使用systemctl修改docker.service文件,這種不推薦,這裏不作介紹.需要注意的是,不管是哪一種方法,只要對同一屬性做了配置,都會導致衝突而啓動失敗.所以建議只使用一種.
注意,監聽unix:///var/run/docker.sock
是爲了實現本機docker直接控制,監聽tcp://0.0.0.0:2376
表示監聽2376端口所有連接,又這裏開啓了TLS驗證,則會根據我們給定的TLS文件去做驗證
服務端需要的TLS文件有CA證書ca.pem,服務端證書server-cert.pem,服務端密鑰server-key.pem
下面的兩種方法的介紹
1 指令啓動
dockerd --tlsverify=true \
--tlscacert=/opt/docker-ssl/ca.pem \
--tlscert=/opt/docker-ssl/server-cert.pem \
--tlskey=/opt/docker-ssl/server-key.pem \
--host tcp://0.0.0.0:2376 \
--host unix:///var/run/docker.sock
2 daemon.json配置啓動
配置/etc/docker/daemon.json
文件如下,注意,鏡像地址與本文無關,可不配置
{
"tlsverify": true,
"tlscacert": "/opt/docker-ssl/ca.pem",
"tlscert": "/opt/docker-ssl/server-cert.pem",
"tlskey": "/opt/docker-ssl/server-key.pem",
"hosts": ["tcp://0.0.0.0:2376","unix:///var/run/docker.sock"],
"registry-mirrors": ["https://5ehijrnq.mirror.aliyuncs.com"]
}
然後通過systemctl正常啓動,指令如下
systemctl restart docker
3 啓動失敗的處理辦法
如果在修改 /etc/docker/daemon.json
後遇到啓動失敗的問題,如
Job for docker.service failed because the control process exited with error code. See "systemctl status docker.service" and "journalctl -xe" for details.
首先檢查 json 文件的編寫有沒有錯誤,比方說是不是加多了一個 “ , ”符號,如果確定json沒有錯,那通常是json文件的啓動項和dockerd指令方法啓動時附帶的參數衝突了,這個時候建議不要使用dockerd指令啓動,如果你只是簡單地使用 systemctl start docker
啓動,那這個時候通常只剩下一種可能性--------systemd的docker.service
文件裏面附帶了與你daemon.json
文件重複的配置項。這個時候處理方法如下:
1 打開配置文件
vim /usr/lib/systemd/system/docker.service
2 屏蔽重複配置項
如下圖,systemctl啓動默認附帶了參數 -H 用於指定監聽,和 json文件裏的 hosts 配置項重複,導致啓動失敗,這裏我將其 換行並添加屏蔽符號
,也可直接刪去。
3 重新加載配置
systemctl daemon-reload
4 重新啓動
systemctl restart docker
六 驗證遠程控制
客戶端需要的TLS文件有CA證書ca.pem,客戶端證書cert.pem,客戶端密鑰key.pem
連接的指令格式如下,以docker version爲例,其中$HOST爲遠程主機ip
docker --tlsverify --tlscacert=ca.pem --tlscert=cert.pem --tlskey=key.pem -H=$HOST:2376 version
結果如下圖:
將version換成其他即可如在本機一樣控制遠程docker
七 參考
https://docs.docker.com/engine/security/https/
https://www.openssl.org/docs/manmaster/man5/x509v3_config.html
https://docs.docker.com/config/daemon/
https://docs.docker.com/engine/reference/commandline/dockerd/
https://docs.docker.com/install/linux/linux-postinstall/