Docker 守護進程+遠程連接+安全訪問+啓動衝突解決辦法 (完整收藏版)

本文根據官方資料實踐而成,同時對於本人操作時遇到的問題加以整理總結.通過本文,你應該能夠實現通過自定義證書實現對遠程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
      擴展密鑰用法,此擴展包含一個用法列表,用於指示證書公鑰可用於的目的
  • 如下
    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

vim

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

結果如下圖:
docker 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/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章