Docker SSH HTTPS

Protect the Remote Docker Server

董欣其 Chi Tung 2021/11/26 17:00:00
1343

前言

上一篇文章討論了如何將 docker engine 開啟遠端控制的功能,此功能常被用在一些輔助管理 docker 容器的應用,如 Portainer。

然而之前也提到此種做法是有風險的,其中最重要的就是原本的 docker socket 會受到 Linux 的權限限制,但暴露到網路上後就沒有這層保護了。

其次,由於使用 HTTP 來傳輸,一些機敏資訊都會以明碼的方式來傳輸。以下使用 docker login 命令搭配 WireShark 抓包讓各位感受一下。

 

在 Terminal 上進行 docker login,看似有隱藏起密碼。

tcp docker login

但在 WireShark 上看得一清二楚。

tcp wireshark package

以上兩個顯然都是需要被改善的問題。

 

這邊將會介紹兩種方式來解決安全性的問題。

 

Using HTTPS

前面提到了使用了 http 導致封包是明碼的,應該可以很直覺地聯想到改用 https 來解決這個問題。

首先要知道的是在配置 https 的時候,會需要在 server 端及 client 都用同一個 CA 去做簽章。這邊會著重在 docker 的配置操作,關於憑證信任鏈的知識就不多做說明。

註:基於安全性考量,以下操作憑證簽署會使用 root 身分,唯 client 端憑證須給對應 user 使用需要修改檔案 owner。

 

建立 CA

由於是自簽的憑證,CA 也由我們自己產生。

首先產出一個私鑰:

openssl genrsa -out ca.key 4096

generate CA key

如果有需要用 pass phrase 加密私鑰的話可以改用以下指令:

openssl genrsa -aes256 -out ca.key 4096

接著使用這把私鑰產生自我簽署的 CA 憑證 (視需求調整 subj 部分):

openssl req -new -x509 -days 365 -sha256 \
  -subj "/C=TW/ST=Taipei/O=TPI/OU=BD4/CN=$HOSTNAME" \
  -key ca.key \
  -out ca.crt

grnerate CA crt

至此,一個私人 CA 就建立完成了。

 

簽署 Docker Server 憑證

先產出屬於 docker server 的私鑰:

openssl genrsa -out server.key 4096

generate server key

用此私鑰產出憑證簽章請求:

openssl req -sha256 -new \
  -subj "/CN=$HOSTNAME" \
  -key server.key \
  -out server.csr

注意:這邊的 CN 必須是你用來連到 docker server 的連線資訊,FQDN 或者 IP。

generate server CSR

產出擴充文件:

cat << EOF > extfile.cnf
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = $HOSTNAME
IP.1 = 192.168.80.17
IP.2 = 127.0.0.1
EOF

generate server v3 ext

接著使用 CA 憑證對此請求進行簽章:

openssl x509 -req -sha256 \
  -CAcreateserial \
  -days 365 \
  -extfile extfile.cnf \
  -in server.csr \
  -CA ca.crt \
  -CAkey ca.key \
  -out server.crt

generate server crt

 

配置 Docker Client 憑證

老樣子依序產出私鑰、簽章請求以及憑證:

openssl genrsa -out client.key 4096

generate client key

openssl req -new \
  -subj "/CN=$HOSTNAME" \
  -key client.key \
  -out client.csr

generate client CSR

產出擴充文件:

cat << EOF > extfile-client.cnf
extendedKeyUsage = clientAuth
EOF

generate client v3 ext

使用 CA 憑證對此請求進行簽章:

openssl x509 -req -sha256 \
  -CAcreateserial \
  -days 365 \
  -extfile extfile-client.cnf \
  -in client.csr \
  -CA ca.crt \
  -CAkey ca.key \
  -out client.crt

generate client crt

將 client 端的私鑰及憑證,連同 server 的憑證複製到 client 端的機器上。

 

啟用 HTTPS 傳輸

Client 端

先由 client 端開始配置,不然下 docker 指令時必須要帶一長串憑證參數,很麻煩。

使用 docker context 配置給 client 使用 (憑證路徑自行修改):

docker context create chi-tools-https \
  --docker "host=tcp://chi-tools:2375,ca=/etc/docker/ssl/ca.crt,cert=/etc/docker/ssl/client.crt,key=/etc/docker/ssl/client.key" \
  --description "Connect to chi-tools through https"

docker context create https

切換至配置好的 context:

docker context use chi-tools-https
docker context ls

docker context use https

 

Server 端

注意:一旦啟用 TLS Verify,就無法使用其他方式對 server 連線,包括 ssh。

 

修改 docker.service 中服務的啟動指令:

## Modify /usr/lib/systemd/system/docker.service

[Service]

ExecStart=/usr/bin/dockerd --tlsverify --tlscacert=/etc/docker/ssl/ca.crt --tlscert=/etc/docker/ssl/server.crt --tlskey=/etc/docker/ssl/server.key -H=0.0.0.0:2375

套用設定後重啟 docker:

sudo systemctl daemon-reload
sudo systemctl restart docker
systemctl status docker

restart docker

 

檢驗成果

首先在 client 端下 docker version 應該要可以看到 client 及 server 的版本資訊。

docker version

接著抓一下封包檢查。

https docker info

可以明顯看出有經過 TLS 加密囉~

 

Using SSH

另一種方式是改走 ssh 的方式。

首先要在 client 端配置 ssh key,並將其在 server 端信任。

ssh-keygen
ssh-copy-id chi@chi-tools

注意:這邊 ssh-copy-id 步驟的連線對象即為 docker server 的使用者以及主機名,分別有以下兩個注意事項:

    1. 這邊的"使用者"必須存在於 docker server 中,並且備使用 docker.sock 的權限,比如說身為 root 或有包含在 docker 群組中。

    2. "主機名"要求要能在 client 端能解析出 ip,比如說由 dns 解析,或是配置 /etc/hosts

接著建立 docker context。

docker context create chi-tools-ssh \
  --docker host=ssh://chi@chi-tools \
  --description "Connect to chi-tools through ssh"

切換至剛剛新增的 context。

docker context use chi-tools-ssh
docker context ls

docker ls

接著再嘗試著抓包驗證一下效果吧~

ssh docker info

可以明顯看出封包都變成 SSH 加密封包了,大功告成。

 

結語

最後來比較一下這兩種保護方式的差異吧~

 

SSH 連線

可以明顯看出配置的步驟比 https 方式容易許多,只要在 client 端產出 ssh key 後讓 server 端信任即可。

而且由於是使用了 server 端上 os 的 user 來呼叫 `docker.socket`,在管理上可以沿用 Linux 的 user 管理方式。

不過正因為如此,使用此種方式會對管理者的 OS 知識要求比較高一些,才能夠更安全的進行管理。

 

HTTPS 連線

雖然配置的過程非常繁瑣,但其實仔細檢視流程的話,server 端的配置只需要做一次即可。

剩下的部分就是當有新的 client 端使用者,只要向管理員申請憑證簽章,剩下的就是在 client 端使用憑證進行連線。過程中基本無須對 server 的 OS 進行操作。

而且還有一個好處,就是可以藉由憑證的有效時間對 client 進行管理。 (當然我也知道一般可能會想簽個 100 年比較省事)

 

比較

綜上所述,個人認為當團隊人員比較少的時候使用 ssh 進行連線會比較省事。但如果 client 的人數比較多的話,也許使用 https 的方式會比較容易追蹤管理。

最後,文章一開始提到的容器管理服務,目前大多數是以 https 的方式進行配置,由於 https 的保護無法與其他連線相容,所以如果有預期要使用的話,就別無選擇只能用 https 囉。

董欣其 Chi Tung