cosign 签名镜像(simple sign),签名上传至 harbor,harbor 如何存储签名,手动模拟 cosign 签名过程,镜像签名与 harbor 特性(如复制规则、垃圾清理、tag保留)的适配性
cosign & harbor 实验 install cosign
UTM virtual host | Linux kali 6.0.0-kali3-arm64 #1 SMP Debian 6.0.7-1kali1 (2022-11-07) aarch64 GNU/Linux
wget https://github.com/sigstore/cosign/releases/download/v2.2.2/cosign-linux-arm64 install -m 777 cosign-linux-arm64 /usr/local/bin/cosign rm cosign-linux-arm64cosign version
about harbor installed harbor v2.5.5, server at Internal Network https://10.120.1.233:8443
Labs 签名镜像并上传签名 cosign generate-key-pair docker pull 10.120.1.233:8443/zyydebug/test:dev docker push 10.120.1.233:8443/zyydebug/test:dev
cosign sign -key cosign.key 10.120.1.233:8443/zyydebug/test:dev
证书链上选中终端证书上一层的证书,导出,保存为 ca.crt,传到主机上
mkdir -p '/etc/docker/certs.d/10.120.1.233:8443' install -m 644 ca.crt '/etc/docker/certs.d/10.120.1.233:8443/ca.crt' ls -R /etc/docker/certs.dcosign sign -key cosign.key 10.120.1.233:8443/zyydebug/test:dev systemctl daemon-reload; systemctl restart docker cosign sign -key cosign.key 10.120.1.233:8443/zyydebug/test:dev cosign sign --help cosign sign -key cosign.key --allow-insecure-registry=true 10.120.1.233:8443/zyydebug/test:dev
注:至此,注意到 -key 会给 warning,下文改用 --key
经查询文档,确认 cosign 支持从 tag 来引用镜像,但是签名是签在 digest 上的。如果 tag 指向的是一个多架构镜像,则实际签名的 digest 并不是任何一个子镜像,而是签在这个 manifest 上。下图即在页面上删除了之前上传的签名,然后给多架构镜像签名的结果
签名文件实际上是以 blob 的形式存储在 storage 层,并以 tag 的形式附加到镜像,所以镜像的 sign 可以作为 image-artifact 的一部分被一起迁移。详见 Cosign Signature Spec 。
如果对 87aa8 这个 degist 多次重复签名,签名会保存在 _manifest/tags/sha256-87aa8... .sig中
以 666fc95 为例,其对应的 blob/sha256/66/…/data 的内容是一个 json 文本。
注意到 layers[*].annotations[dev.sigstore.cosign/bundle]:Payload.logIndex,这个就是 tlog 的序列号,签名时 tlog 将被传到公网。
$ cosign sign \ --key cosign.key \ --allow-insecure-registry=true \ --tlog-upload=false \ --upload=true \ 10.120.1.233:8443/zyydebug/test@sha256:87aa817e17903f32769d257e980555f6382c6ac75964929c654b04d1c0826243 Enter password for private key: Pushing signature to: 10.120.1.233:8443/zyydebug/test
参数
解释
默认值
–key
签名用私钥|格式:密钥文件路径
–allow-insecure-registry
disable repository TLS verify
false
–tlog-upload
上传 tlog(需要访问公网,或自定的tlog server)
true
–upload
是否推送镜像签名到远程
true
–output-payload
签名算法的输入|格式:文件路径
–output-signature
签名算法的输出|格式:文件路径
验证签名(不上传) IMAGE=10.120.1.233:8443/zyydebug/test@sha256:87aa817e17903f32769d257e980555f6382c6ac75964929c654b04d1c0826243 cosign generate-key-pair cosign sign --key=cosign.key \ --tlog-upload=false \ --upload=false \ --output-signature=signature.dat \ --output-payload=payload.dat $IMAGE
┌──(root㉿kali)-[~/lab_cosign] └─# cat signature.dat MEQCIEMEcEbkPx7YihEJBkMZ7vEbwk074j9s409ZN/tbp0jdAiA7NWSXto1QAXo0FYpEE1r+qgNPMcvbL4KDjR/azNA2fg== ┌──(root㉿kali)-[~/lab_cosign] └─# cat payload.dat | jq { "critical" : { "identity" : { "docker-reference" : "10.120.1.233:8443/zyydebug/test" }, "image" : { "docker-manifest-digest" : "sha256:87aa817e17903f32769d257e980555f6382c6ac75964929c654b04d1c0826243" }, "type" : "cosign container image signature" }, "optional" : null }
cosign verify \ --key cosign.pub \ --signature=signature.dat \ --payload=payload.dat \ --insecure-ignore-tlog=true $IMAGE [ { "critical" : { "identity" : { "docker-reference" : "10.120.1.233:8443/zyydebug/test" }, "image" : { "docker-manifest-digest" : "sha256:87aa817e17903f32769d257e980555f6382c6ac75964929c654b04d1c0826243" }, "type" : "cosign container image signature" }, "optional" : null } ] cosign verify-blob \ --key cosign.pub \ --signature signature.dat \ --insecure-ignore-tlog=true payload.dat
cosign 从 harbor 下载镜像签名 ┌──(root㉿kali)-[~/lab_cosign/verify] └─# cosign download signature 10.120.1.233:8443/zyydebug/test:dev | jq { "Base64Signature" : "MEUCIQDWrW/mlCSB1OqMSYk8QRYnze4KIYvPE6uQyB66/A7qfwIgCmN2DlLfDJ9VXdfv+HEfJC2gxJSoCeEH4Za4dhGsE2E=" , "Payload" : "eyJjcml0aWNhbCI6eyJpZGVudGl0eSI6eyJkb2NrZXItcmVmZXJlbmNlIjoiMTAuMTIwLjEuMjMzOjg0NDMvenl5ZGVidWcvdGVzdCJ9LCJpbWFnZSI6eyJkb2NrZXItbWFuaWZlc3QtZGlnZXN0Ijoic2hhMjU2Ojg3YWE4MTdlMTc5MDNmMzI3NjlkMjU3ZTk4MDU1NWY2MzgyYzZhYzc1OTY0OTI5YzY1NGIwNGQxYzA4MjYyNDMifSwidHlwZSI6ImNvc2lnbiBjb250YWluZXIgaW1hZ2Ugc2lnbmF0dXJlIn0sIm9wdGlvbmFsIjpudWxsfQ==" , "Cert" : null, "Chain" : null, "Bundle" : null, "RFC3161Timestamp" : null } ┌──(root㉿kali)-[~/lab_cosign/verify] └─# cosign download signature 10.120.1.233:8443/zyydebug/test:dev | jq -r ".Payload" | base64 -d | jq { "critical" : { "identity" : { "docker-reference" : "10.120.1.233:8443/zyydebug/test" }, "image" : { "docker-manifest-digest" : "sha256:87aa817e17903f32769d257e980555f6382c6ac75964929c654b04d1c0826243" }, "type" : "cosign container image signature" }, "optional" : null }
[26/Dec/2023:09:19:28 +0000]:10.10.136.50 - "GET /v2/ HTTP/1.1" 401 76 "-" "cosign/v2.2.2 (linux; arm64) go-containerregistry/v0.17.0" 0.006 0.006 . [26/Dec/2023:09:19:28 +0000]:10.10.136.50 - "GET /service/token?scope=repository%3Azyydebug%2Ftest%3Apull&service=harbor-registry HTTP/1.1" 200 967 "-" "cosign/v2.2.2 (linux; arm64) go-containerregistry/v0.17.0" 0.086 0.086 . [26/Dec/2023:09:19:28 +0000]:10.10.136.50 - "GET /v2/zyydebug/test/manifests/dev HTTP/1.1" 200 1365 "-" "cosign/v2.2.2 (linux; arm64) go-containerregistry/v0.17.0" 0.221 0.221 . [26/Dec/2023:09:19:28 +0000]:10.10.136.50 - "GET /v2/zyydebug/test/manifests/sha256-87aa817e17903f32769d257e980555f6382c6ac75964929c654b04d1c0826243.sig HTTP/1.1" 200 558 "-" "cosign/v2.2.2 (linux; arm64) go-containerregistry/v0.17.0" 0.200 0.201 . [26/Dec/2023:09:19:28 +0000]:10.10.136.50 - "GET /v2/zyydebug/test/blobs/sha256:4549abebd4a503f64d874955e3f80518960bfb1aea6d2e0da4bf4b382ced029f HTTP/1.1" 200 247 "-" "cosign/v2.2.2 (linux; arm64) go-containerregistry/v0.17.0" 0.176 0.176 .
拉取镜像时验证签名 IMAGE=10.120.1.233:8443/zyydebug/test:dev crane manifest 10.120.1.233:8443/zyydebug/test:dev | jq | tee manifest.json image_digest=$(crane manifest 10.120.1.233:8443/zyydebug/test:dev | sha256sum | cut -d' ' -f1) crane manifest 10.120.1.233:8443/zyydebug/test:sha256-87aa817e17903f32769d257e980555f6382c6ac75964929c654b04d1c0826243.sig | jq | tee signature.json { "schemaVersion" : 2, "mediaType" : "application/vnd.oci.image.manifest.v1+json" , "config" : { "mediaType" : "application/vnd.oci.image.config.v1+json" , "size" : 233, "digest" : "sha256:f6febe23ffd09309aea4d4a8422f0214ce10849b00b459be4313eac2d36d4678" }, "layers" : [ { "mediaType" : "application/vnd.dev.cosign.simplesigning.v1+json" , "size" : 247, "digest" : "sha256:4549abebd4a503f64d874955e3f80518960bfb1aea6d2e0da4bf4b382ced029f" , "annotations" : { "dev.cosignproject.cosign/signature" : "MEUCIQDWrW/mlCSB1OqMSYk8QRYnze4KIYvPE6uQyB66/A7qfwIgCmN2DlLfDJ9VXdfv+HEfJC2gxJSoCeEH4Za4dhGsE2E=" } } ] } download_signature=$(crane manifest 10.120.1.233:8443/zyydebug/test:sha256-87aa817e17903f32769d257e980555f6382c6ac75964929c654b04d1c0826243.sig | jq -r '.layers[0].annotations["dev.cosignproject.cosign/signature"]' ) { "critical" : { "identity" : { "docker-reference" : "10.120.1.233:8443/zyydebug/test" }, "image" : { "docker-manifest-digest" : "sha256:87aa817e17903f32769d257e980555f6382c6ac75964929c654b04d1c0826243" }, "type" : "cosign container image signature" }, "optional" : null } payload=$(cosign download signature 10.120.1.233:8443/zyydebug/test:dev | jq -r ".Payload" | base64 -d) echo -n $download_signature > sign.datecho -n $payload > payload.datcosign verify-blob \ --key cosign.pub \ --signature sign.dat \ --insecure-ignore-tlog=true payload.dat
┌──(root㉿kali)-[~/lab_cosign/verify] └─# download_signature=$(crane manifest 10.120.1.233:8443/zyydebug/test:sha256-87aa817e17903f32769d257e980555f6382c6ac75964929c654b04d1c0826243.sig | jq -r '.layers[0].annotations["dev.cosignproject.cosign/signature"]' ) payload=$(cosign download signature 10.120.1.233:8443/zyydebug/test:dev | jq -r ".Payload" | base64 -d) echo -n $download_signature > sign.datecho -n $payload > payload.datcosign verify-blob \ --key cosign.pub \ --signature sign.dat \ --insecure-ignore-tlog=true payload.dat WARNING: Skipping tlog verification is an insecure practice that lacks of transparency and auditability verification for the blob. Verified OK
harbor镜像复制,是否会携带签名一起转移 测试环境 cat > Dockerfile <<'EOF' from scratch add file /file EOF echo "hello world" > filedocker build -t 10.120.1.233:8443/zyydebug/signature:v1 . docker push 10.120.1.233:8443/zyydebug/signature:v1
cosign generate-key-pair cosign sign --key=cosign.key \ --tlog-upload=false \ --upload=true 10.120.1.233:8443/zyydebug/signature:v1
测试结果
测试
签名是否复制
pull-base
✅
push-base
✅
manual-docker-pull-push
❌
manual-crane-cp
❌
manual-skopeo-cp
❌
补充说明:对于未复制签名的情况,把签名一起复制了就好了。例如
crane --insecure cp \ 10.120.1.233:8443/zyydebug/signature:sha256-70263dea0fafa899bfc9aa37fbf3a669e48dd15a277f7eaee821afa61eebf570.sig \ 10.120.1.233:8443/zyydebug-crane/signature:sha256-70263dea0fafa899bfc9aa37fbf3a669e48dd15a277f7eaee821afa61eebf570.sig
harbor GC测试
删签名,不删镜像,签名 blob 会被 GC 么:会
删签名,再删除镜像artifact,镜像 blob 和签名 blob 会被 GC 么:会
不删签名,直接删镜像,镜像 blob 和签名 blob 会被 GC 么:会
Q&A Q: harbor 是仅存储 cosign 签名还是也会尝试解析、验证签名? A: harbor 既没有签名的公钥或私钥,也没相关的证书链,无从解析签名。
考虑到签名在 repository 中的存储形式,不验证它,是非常合理的。
虽然没有具体明确的证据,但是我认为 harbor 不会尝试解析 cosign 签名。
Q: 签名以 tag 的形式链接到 image-artifact,那么它会被 tag 保留策略删除么? 测了,签名不会被 tag 保留策略删除。但是暂时未找到签名tag和普通tag在数据库中或源代码处理上有什么不一样。
参考文档