如何給 Docker 鏡像進(jìn)行安全簽名

幸運(yùn)的是,Docker 通過(guò)一種稱為 Docker 內(nèi)容信任的功能來(lái)實(shí)現(xiàn)信任機(jī)制。
在網(wǎng)絡(luò)系統(tǒng)之間傳輸數(shù)據(jù)時(shí),信任是一個(gè)核心問(wèn)題。特別是,當(dāng)通過(guò)不受信任的互聯(lián)網(wǎng)網(wǎng)絡(luò)進(jìn)行通信時(shí),確保系統(tǒng)操作的所有數(shù)據(jù)的完整性和可信任的發(fā)布者,是至關(guān)重要的。
1. 信任機(jī)制
介紹 DCT 到底是什么,其作用是為了干什么!
Docker Content Trust(DCT)提供了對(duì)從遠(yuǎn)程 Docker 倉(cāng)庫(kù)上傳和下載的鏡像文件,使用數(shù)字簽名的能力,其能夠保證鏡像文件的完整性和發(fā)布者的可信性。DCT 通過(guò) Docker 標(biāo)簽對(duì)鏡像文件是否進(jìn)行簽名進(jìn)行區(qū)分,鏡像發(fā)布者可以自行決定在哪些標(biāo)簽上進(jìn)行簽名。
DCT 實(shí)現(xiàn)的是客戶端的簽名和驗(yàn)證,意味著由 Docker 客戶端執(zhí)行它們。顯然,類似這樣的密碼機(jī)制,對(duì)于確保在互聯(lián)網(wǎng)上拉取和推送的軟件的可信性是非常重要的,其在整個(gè)技術(shù)棧的各個(gè)層次,以及軟件交付流水線的各個(gè)環(huán)節(jié)都在發(fā)揮越來(lái)越重要的作用。

在Docker中進(jìn)行鏡像簽名
2. 鏡像簽名
介紹對(duì)鏡像文件進(jìn)行簽名,秘鑰使用流程和關(guān)系!
鏡像標(biāo)簽的信任是,通過(guò)使用簽名密鑰來(lái)管理的。第一次使用 DCT 來(lái)操作時(shí),將創(chuàng)建密鑰集,由以下類密鑰組成:
- 根密鑰 - root key
- 它用于創(chuàng)建和簽名新的庫(kù)密鑰,因此應(yīng)該被妥善保管
- an offline key that is the root of DCT for an image tag
- 庫(kù)密鑰 - 標(biāo)簽密鑰 - repository key
- 用于對(duì)需要推送到指定鏡像庫(kù)的打標(biāo)簽的鏡像進(jìn)行簽名
- repository or tagging keys that sign tags
- 時(shí)間戳密鑰 - timeStamp key
- 它被保存在遠(yuǎn)程鏡像庫(kù)中,用于一些更加高級(jí)的使用場(chǎng)景以確保時(shí)效性
- server-managed keys such as the timestamp key, which provides freshness security guarantees for your repository

在Docker中進(jìn)行鏡像簽名
下圖描述了各種簽名密鑰及其關(guān)系,但需要注意的是根密鑰的丟失是很難恢復(fù)的,所以應(yīng)該將根密鑰備份到一個(gè)安全的地方。

在Docker中進(jìn)行鏡像簽名
3. 初次使用
使用我們自建的 notary(一個(gè)用于建立內(nèi)容之間信任的平臺(tái))服務(wù)進(jìn)行容器簽名
在 Docker CLI 中,我們可以使用 docker trust 命令對(duì)容器文件進(jìn)行簽名和推送。但是需要注意的是,必須要部署一個(gè)公證服務(wù)器(notary)才可以對(duì)容器鏡像進(jìn)行簽名。
# Notary Server
$ git clone https://github.com/theupdateframework/notary.git
$ docker-compose up -d

在Docker中進(jìn)行鏡像簽名
要簽名一個(gè) Docker 鏡像,需要一個(gè)密鑰對(duì),其可以使用命令在本地生成(默認(rèn)情況下存儲(chǔ)在 ~/.docker/trust/ 中),也可以由來(lái)自證書機(jī)構(gòu)。
# 本地生成
$ docker trust key generate point_me
Generating key for point_me...
Enter passphrase for new point_me key with ID 9deed25:
Repeat passphrase for new point_me key with ID 9deed25:
Successfully generated and loaded private key.
# 使用已有
$ docker trust key load key.pem --name point_me
Loading key from "key.pem"...
Enter passphrase for new point_me key with ID 8ae710e:
Repeat passphrase for new point_me key with ID 8ae710e:
Successfully imported key from key.pem
接下來(lái),我們將公鑰添加到公證服務(wù)器上。如果是第一次執(zhí)行的話,需要輸入一些相關(guān)信息,才可以使用。
$ docker trust signer add --key cert.pem point_me registry.example.com/admin/demo
Adding signer "point_me" to registry.example.com/admin/demo...
Enter passphrase for new repository key with ID 10b5e94:
最后,我們將使用私鑰對(duì)特定鏡像文件的標(biāo)簽進(jìn)行簽名,并將其推到倉(cāng)庫(kù)中去。
# 簽名
$ docker trust sign registry.example.com/admin/demo:1
Signing and pushing trust data for local image registry.example.com/admin/demo:1, may overwrite remote trust data
The push refers to repository [registry.example.com/admin/demo]
7bff100f35cb: Pushed
1: digest: sha256:3d2e482b82608d153a374df3357c0291589a61cc194ec4a9ca2381073a17f58e size: 528
Signing and pushing trust metadata
Enter passphrase for signer key with ID 8ae710e:
Successfully signed registry.example.com/admin/demo:1
# 啟用
$ export DOCKER_CONTENT_TRUST=1
# 推送
$ docker push registry.example.com/admin/demo:1
The push refers to repository [registry.example.com/admin/demo:1]
7bff100f35cb: Pushed
1: digest: sha256:3d2e482b82608d153a374df3357c0291589a61cc194ec4a9ca2381073a17f58e size: 528
Signing and pushing trust metadata
Enter passphrase for signer key with ID 8ae710e:
Successfully signed registry.example.com/admin/demo:1
# 查看
$ docker trust inspect --pretty registry.example.com/admin/demo:1
# 刪除遠(yuǎn)程服務(wù)器對(duì)該鏡像的信任
$ docker trust revoke registry.example.com/admin/demo:1
Enter passphrase for signer key with ID 8ae710e:
Successfully deleted signature for registry.example.com/admin/demo:1
Docker 客戶端默認(rèn)禁用鏡像簽名,如果希望啟用,則將 DOCKER_CONTENT_TRUST 環(huán)境變量設(shè)置為 1 即可。這將阻止用戶使用帶有非簽名的圖像文件,對(duì)應(yīng)對(duì)應(yīng)的客戶端命令,比如 push、build、create、pull 和 run。
# 拉取失敗
$ docker pull registry.example.com/user/image:1
Error: remote trust data does not exist for registry.example.com/user/image: registry.example.com does not have trust data for registry.example.com/user/image
# 拉取成功
$ docker pull registry.example.com/user/image@sha256:d149ab53f8718e987c3a3024bb8aa0e2caadf6c0328f1d9d850b2a2a67f2819a
sha256:ee7491c9c31db1ffb7673d91e9fac5d6354a89d0e97408567e09df069a1687c1: Pulling from user/image
ff3a5c916c92: Pull complete
a59a168caba3: Pull complete
Digest: sha256:ee7491c9c31db1ffb7673d91e9fac5d6354a89d0e97408567e09df069a1687c1
Status: Downloaded newer image for registry.example.com/user/image@sha256:ee7491c9c31db1ffb7673d91e9fac5d6354a89d0e97408567e09df069a1687c1
4. 使用示例
直接基于 hub.docker 倉(cāng)庫(kù)服務(wù)進(jìn)行操作!
下面通過(guò)一個(gè)簡(jiǎn)單的配置 DCT 的實(shí)戰(zhàn)例子予以闡述。我們需要一個(gè) Docker 客戶端和一個(gè)用來(lái)推送鏡像的庫(kù),Docker Hub 上的鏡像庫(kù)即可。
# 啟用特性
$ export DOCKER_CONTENT_TRUST
# 登錄hub.docker倉(cāng)庫(kù)
$ docker login
# 對(duì)鏡像打標(biāo)簽并推送到目標(biāo)鏡像庫(kù)
$ docker image tag alpine:latest escape/dockerbook:v1
# 推送打了新標(biāo)簽的鏡像
# 在簽名時(shí)會(huì)創(chuàng)建兩個(gè)密鑰,根密鑰和庫(kù)密鑰
$ docker image push nigelpoulton/dockerbook:v1
# 在拉取鏡像時(shí)使用如下命令來(lái)覆蓋DCT設(shè)置
$ docker image pull --disable-content-trust nigelpoulton/dockerbook:unsigned
# 嘗試運(yùn)行未簽名的鏡像容器
$ docker container run -d --rm nigelpoulton/dockerbook:unsigned
docker: No trust data for unsigned.
5. 參考鏈接
- Content trust in Docker
- Deploy Notary Server with Compose
- 容器鏡像簽名
























