4.2. 轮转证书转换日志签名者密钥
您可以使用分片功能冻结日志树,主动轮转证书转换(CT)日志签名程序密钥,并使用新的签名程序密钥创建新日志树。此流程指导您完成旧的 CT 日志签名程序密钥,并使用 Red Hat Trusted Artifact Signer (RHTAS)的新 signer 密钥替换它。使旧的 CT 日志签名器密钥过期仍允许您验证旧密钥签名的工件。
先决条件
- 安装在 Red Hat OpenShift Container Platform 上运行的 RHTAS operator。
- 正在运行的 Securesign 实例。
-
安装了
oc
、openssl
和cosign
二进制文件的工作站。
流程
将
tuftool
二进制文件从 OpenShift 集群下载到您的工作站。重要tuftool
二进制文件仅适用于 Linux 操作系统。- 在主页中,单击 ? 图标,单击 Command line tools,转至 tuftool 下载部分,然后点击您的平台的链接。
在工作站上打开一个终端,解压缩二进制
.gz
文件,并设置 execute 位:Example
$ gunzip tuftool-amd64.gz $ chmod +x tuftool-amd64
Copy to clipboardCopied$ gunzip tuftool-amd64.gz $ chmod +x tuftool-amd64
将二进制文件移到
$PATH
环境中的位置:Example
$ sudo mv tuftool-amd64 /usr/local/bin/tuftool
Copy to clipboardCopied$ sudo mv tuftool-amd64 /usr/local/bin/tuftool
从命令行登录到 OpenShift:
语法
oc login --token=TOKEN --server=SERVER_URL_AND_PORT
Copy to clipboardCopiedoc login --token=TOKEN --server=SERVER_URL_AND_PORT
Example
$ oc login --token=sha256~ZvFDBvoIYAbVECixS4-WmkN4RfnNd8Neh3y1WuiFPXC --server=https://example.com:6443
Copy to clipboardCopied$ oc login --token=sha256~ZvFDBvoIYAbVECixS4-WmkN4RfnNd8Neh3y1WuiFPXC --server=https://example.com:6443
注意您可以从 OpenShift Web 控制台找到用于命令行的登录令牌和 URL。登录 OpenShift Web 控制台。单击您的用户名,然后单击 Copy login command。如果被要求,再次提供您的用户名和密码,然后单击 Display Token 以查看该命令。
切换到 RHTAS 项目:
Example
$ oc project trusted-artifact-signer
Copy to clipboardCopied$ oc project trusted-artifact-signer
备份当前的 CT 日志配置和密钥:
Example
$ export SERVER_CONFIG_NAME=$(oc get ctlog -o jsonpath='{.items[0].status.serverConfigRef.name}') $ oc get secret $SERVER_CONFIG_NAME -o jsonpath="{.data.config}" | base64 --decode > config.txtpb $ oc get secret $SERVER_CONFIG_NAME -o jsonpath="{.data.fulcio-0}" | base64 --decode > fulcio-0.pem $ oc get secret $SERVER_CONFIG_NAME -o jsonpath="{.data.private}" | base64 --decode > private.pem $ oc get secret $SERVER_CONFIG_NAME -o jsonpath="{.data.public}" | base64 --decode > public.pem
Copy to clipboardCopied$ export SERVER_CONFIG_NAME=$(oc get ctlog -o jsonpath='{.items[0].status.serverConfigRef.name}') $ oc get secret $SERVER_CONFIG_NAME -o jsonpath="{.data.config}" | base64 --decode > config.txtpb $ oc get secret $SERVER_CONFIG_NAME -o jsonpath="{.data.fulcio-0}" | base64 --decode > fulcio-0.pem $ oc get secret $SERVER_CONFIG_NAME -o jsonpath="{.data.private}" | base64 --decode > private.pem $ oc get secret $SERVER_CONFIG_NAME -o jsonpath="{.data.public}" | base64 --decode > public.pem
捕获当前树标识符:
Example
$ export OLD_TREE_ID=$(oc get ctlog -o jsonpath='{.items[0].status.treeID}')
Copy to clipboardCopied$ export OLD_TREE_ID=$(oc get ctlog -o jsonpath='{.items[0].status.treeID}')
将日志树设置为
DRAINING
状态:Example
$ oc run --image registry.redhat.io/rhtas/updatetree-rhel9:1.1.0 --restart=Never --attach=true --rm=true -q -- updatetree --admin_server=trillian-logserver:8091 --tree_id=${OLD_TREE_ID} --tree_state=DRAINING
Copy to clipboardCopied$ oc run --image registry.redhat.io/rhtas/updatetree-rhel9:1.1.0 --restart=Never --attach=true --rm=true -q -- updatetree --admin_server=trillian-logserver:8091 --tree_id=${OLD_TREE_ID} --tree_state=DRAINING
在排空时,树形日志不接受任何新条目。观察并等待队列为空。
重要您必须等待队列为空,然后继续下一步。如果在排空时保留仍然集成,则在此过程中冻结日志树可能会导致日志路径超过最大合并延迟(MMD)。
队列完全排空后,冻结日志:
Example
$ oc run --image registry.redhat.io/rhtas/updatetree-rhel9:1.1.0 --restart=Never --attach=true --rm=true -q -- updatetree --admin_server=trillian-logserver:8091 --tree_id=${OLD_TREE_ID} --tree_state=FROZEN
Copy to clipboardCopied$ oc run --image registry.redhat.io/rhtas/updatetree-rhel9:1.1.0 --restart=Never --attach=true --rm=true -q -- updatetree --admin_server=trillian-logserver:8091 --tree_id=${OLD_TREE_ID} --tree_state=FROZEN
创建新的 Merkle 树,并捕获新的树标识符:
Example
$ export NEW_TREE_ID=$(kubectl run createtree --image registry.redhat.io/rhtas/createtree-rhel9:1.1.0 --restart=Never --attach=true --rm=true -q -- -logtostderr=false --admin_server=trillian-logserver:8091 --display_name=ctlog-tree)
Copy to clipboardCopied$ export NEW_TREE_ID=$(kubectl run createtree --image registry.redhat.io/rhtas/createtree-rhel9:1.1.0 --restart=Never --attach=true --rm=true -q -- -logtostderr=false --admin_server=trillian-logserver:8091 --display_name=ctlog-tree)
生成新证书,以及新的公钥和私钥:
Example
$ openssl ecparam -genkey -name prime256v1 -noout -out new-ctlog.pem $ openssl ec -in new-ctlog.pem -pubout -out new-ctlog-public.pem $ openssl ec -in new-ctlog.pem -out new-ctlog.pass.pem -des3 -passout pass:"CHANGE_ME"
Copy to clipboardCopied$ openssl ecparam -genkey -name prime256v1 -noout -out new-ctlog.pem $ openssl ec -in new-ctlog.pem -pubout -out new-ctlog-public.pem $ openssl ec -in new-ctlog.pem -out new-ctlog.pass.pem -des3 -passout pass:"CHANGE_ME"
将 CHANGE_ME 替换为新密码。
重要证书和密钥必须具有唯一的文件名。
更新 CT 日志配置。
-
打开
config.txtpb
文件进行编辑。 对于 frozen 日志,将
not_after_limit
字段添加到 frozen 日志条目中,将前缀值重命名为唯一名称,并使用ctfe-keys/private-0
替换到私钥的旧路径:Example
... log_configs:{ # frozen log config:{ log_id:2066075212146181968 prefix:"trusted-artifact-signer-0" roots_pem_file:"/ctfe-keys/fulcio-0" private_key:{[type.googleapis.com/keyspb.PEMKeyFile]:{path:"/ctfe-keys/private-0" password:"Example123"}} public_key:{der:"0Y0\x13\x06\x07*\x86H\xce=\x02\x01\x06\x08*\x86H\xce=\x03\x01\x07\x03B\x00\x04)'.\xffUJ\xe2s)\xefR\x8a\xfcO\xdcewȶy\xa7\x9d<\x13\xb0\x1c\x99\x96\xe4'\xe3v\x07:\xc8I+\x08J\x9d\x8a\xed\x06\xe4\xaeI:q\x98\xf4\xbc<o4VD\x0cr\xf9\x9c\xecxT\x84"} not_after_limit:{seconds:1728056285 nanos:012111000} ext_key_usages:"CodeSigning" log_backend_name:"trillian" }
Copy to clipboardCopied... log_configs:{ # frozen log config:{ log_id:2066075212146181968 prefix:"trusted-artifact-signer-0" roots_pem_file:"/ctfe-keys/fulcio-0" private_key:{[type.googleapis.com/keyspb.PEMKeyFile]:{path:"/ctfe-keys/private-0" password:"Example123"}} public_key:{der:"0Y0\x13\x06\x07*\x86H\xce=\x02\x01\x06\x08*\x86H\xce=\x03\x01\x07\x03B\x00\x04)'.\xffUJ\xe2s)\xefR\x8a\xfcO\xdcewȶy\xa7\x9d<\x13\xb0\x1c\x99\x96\xe4'\xe3v\x07:\xc8I+\x08J\x9d\x8a\xed\x06\xe4\xaeI:q\x98\xf4\xbc<o4VD\x0cr\xf9\x9c\xecxT\x84"} not_after_limit:{seconds:1728056285 nanos:012111000} ext_key_usages:"CodeSigning" log_backend_name:"trillian" }
注意您可以运行以下命令来获取秒和纳秒的当前时间值:
date +%s
,以及date +%N
。重要not_after_limit
字段仅定义 frozen 日志的时间戳范围的末尾。除此刻外的证书不再接受在此日志中包括的证书。-
复制并粘贴 frozen 日志
配置
块,将其附加到配置文件中以创建新条目。 更改新
config
块中的以下行:将log_id
设置为新的树标识符,将前缀
改为trusted-artifact-signer
,将private_key
路径更改为ctfe-keys/private
,删除public_key
行,并将not_after_limit
更改为not_after_start
并设置时间戳范围:Example
... log_configs:{ # frozen log ... # new active log config:{ log_id: NEW_TREE_ID prefix:"trusted-artifact-signer" roots_pem_file:"/ctfe-keys/fulcio-0" private_key:{[type.googleapis.com/keyspb.PEMKeyFile]:{path:"ctfe-keys/private" password:"CHANGE_ME"}} ext_key_usages:"CodeSigning" not_after_start:{seconds:1713201754 nanos:155663000} log_backend_name:"trillian" }
Copy to clipboardCopied... log_configs:{ # frozen log ... # new active log config:{ log_id: NEW_TREE_ID prefix:"trusted-artifact-signer" roots_pem_file:"/ctfe-keys/fulcio-0" private_key:{[type.googleapis.com/keyspb.PEMKeyFile]:{path:"ctfe-keys/private" password:"CHANGE_ME"}} ext_key_usages:"CodeSigning" not_after_start:{seconds:1713201754 nanos:155663000} log_backend_name:"trillian" }
添加 NEW_TREE_ID,并将 CHANGE_ME 替换为新私钥密码。此处的密码必须与用于生成新私钥和公钥的密码匹配。
重要not_after_start
字段定义时间戳范围的开头。这意味着日志此时将开始接受证书。
-
打开
创建新 secret 资源:
Example
$ oc create secret generic ctlog-config \ --from-file=config=config.txtpb \ --from-file=private=new-ctlog.pass.pem \ --from-file=public=new-ctlog-public.pem \ --from-file=fulcio-0=fulcio-0.pem \ --from-file=private-0=private.pem \ --from-file=public-0=public.pem \ --from-literal=password=CHANGE_ME
Copy to clipboardCopied$ oc create secret generic ctlog-config \ --from-file=config=config.txtpb \ --from-file=private=new-ctlog.pass.pem \ --from-file=public=new-ctlog-public.pem \ --from-file=fulcio-0=fulcio-0.pem \ --from-file=private-0=private.pem \ --from-file=public-0=public.pem \ --from-literal=password=CHANGE_ME
使用新私钥密码替换 CHANGE_ME。
配置更新框架(TUF)服务,以使用新的 CT 日志公钥。
设置 shell 环境:
Example
$ export WORK="${HOME}/trustroot-example" $ export ROOT="${WORK}/root/root.json" $ export KEYDIR="${WORK}/keys" $ export INPUT="${WORK}/input" $ export TUF_REPO="${WORK}/tuf-repo" $ export TUF_SERVER_POD="$(oc get pod --selector=app.kubernetes.io/component=tuf --no-headers -o custom-columns=":metadata.name")"
Copy to clipboardCopied$ export WORK="${HOME}/trustroot-example" $ export ROOT="${WORK}/root/root.json" $ export KEYDIR="${WORK}/keys" $ export INPUT="${WORK}/input" $ export TUF_REPO="${WORK}/tuf-repo" $ export TUF_SERVER_POD="$(oc get pod --selector=app.kubernetes.io/component=tuf --no-headers -o custom-columns=":metadata.name")"
创建临时 TUF 目录结构:
Example
$ mkdir -p "${WORK}/root/" "${KEYDIR}" "${INPUT}" "${TUF_REPO}"
Copy to clipboardCopied$ mkdir -p "${WORK}/root/" "${KEYDIR}" "${INPUT}" "${TUF_REPO}"
将 TUF 内容下载到临时 TUF 目录结构中:
Example
$ oc extract --to "${KEYDIR}/" secret/tuf-root-keys $ oc cp "${TUF_SERVER_POD}:/var/www/html" "${TUF_REPO}" $ cp "${TUF_REPO}/root.json" "${ROOT}"
Copy to clipboardCopied$ oc extract --to "${KEYDIR}/" secret/tuf-root-keys $ oc cp "${TUF_SERVER_POD}:/var/www/html" "${TUF_REPO}" $ cp "${TUF_REPO}/root.json" "${ROOT}"
查找活跃的 CT 日志公钥文件名。在本地 TUF 存储库中打开最新的目标文件,例如
1.targets.json
。在此目标文件中,您将找到活跃的 CT 日志公钥文件名,例如ctfe.pub
。使用这个活跃 CT 日志公钥文件名设置环境变量:Example
$ export ACTIVE_CTFE_NAME=ctfe.pub
Copy to clipboardCopied$ export ACTIVE_CTFE_NAME=ctfe.pub
从 OpenShift 中提取活跃的 CT 日志公钥:
Example
$ oc get secret $(oc get ctlog securesign-sample -o jsonpath='{.status.publicKeyRef.name}') -o jsonpath='{.data.public}' | base64 -d > $ACTIVE_CTFE_NAME
Copy to clipboardCopied$ oc get secret $(oc get ctlog securesign-sample -o jsonpath='{.status.publicKeyRef.name}') -o jsonpath='{.data.public}' | base64 -d > $ACTIVE_CTFE_NAME
使旧的 CT 日志签名者密钥过期:
Example
$ tuftool rhtas \ --root "${ROOT}" \ --key "${KEYDIR}/snapshot.pem" \ --key "${KEYDIR}/targets.pem" \ --key "${KEYDIR}/timestamp.pem" \ --set-ctlog-target "$ACTIVE_CTFE_NAME" \ --ctlog-uri "https://ctlog.rhtas" \ --ctlog-status "Expired" \ --outdir "${TUF_REPO}" \ --metadata-url "file://${TUF_REPO}"
Copy to clipboardCopied$ tuftool rhtas \ --root "${ROOT}" \ --key "${KEYDIR}/snapshot.pem" \ --key "${KEYDIR}/targets.pem" \ --key "${KEYDIR}/timestamp.pem" \ --set-ctlog-target "$ACTIVE_CTFE_NAME" \ --ctlog-uri "https://ctlog.rhtas" \ --ctlog-status "Expired" \ --outdir "${TUF_REPO}" \ --metadata-url "file://${TUF_REPO}"
添加新的 CT 日志签名器密钥:
Example
$ tuftool rhtas \ --root "${ROOT}" \ --key "${KEYDIR}/snapshot.pem" \ --key "${KEYDIR}/targets.pem" \ --key "${KEYDIR}/timestamp.pem" \ --set-ctlog-target "new-ctlog-public.pem" \ --ctlog-uri "https://ctlog.rhtas" \ --outdir "${TUF_REPO}" \ --metadata-url "file://${TUF_REPO}"
Copy to clipboardCopied$ tuftool rhtas \ --root "${ROOT}" \ --key "${KEYDIR}/snapshot.pem" \ --key "${KEYDIR}/targets.pem" \ --key "${KEYDIR}/timestamp.pem" \ --set-ctlog-target "new-ctlog-public.pem" \ --ctlog-uri "https://ctlog.rhtas" \ --outdir "${TUF_REPO}" \ --metadata-url "file://${TUF_REPO}"
将这些更改上传到 TUF 服务器:
Example
$ oc rsync "${TUF_REPO}/" "${TUF_SERVER_POD}:/var/www/html"
Copy to clipboardCopied$ oc rsync "${TUF_REPO}/" "${TUF_SERVER_POD}:/var/www/html"
删除工作目录:
Example
$ rm -r $WORK
Copy to clipboardCopied$ rm -r $WORK
使用新树标识符更新 Securesign CT 日志配置:
Example
$ read -r -d '' SECURESIGN_PATCH <<EOF [ { "op": "replace", "path": "/spec/ctlog/serverConfigRef", "value": {"name": "ctlog-config"} }, { "op": "replace", "path": "/spec/ctlog/treeID", "value": $NEW_TREE_ID }, { "op": "replace", "path": "/spec/ctlog/privateKeyRef", "value": {"name": "ctlog-config", "key": "private"} }, { "op": "replace", "path": "/spec/ctlog/privateKeyPasswordRef", "value": {"name": "ctlog-config", "key": "password"} }, { "op": "replace", "path": "/spec/ctlog/publicKeyRef", "value": {"name": "ctlog-config", "key": "public"} } ] EOF
Copy to clipboardCopied$ read -r -d '' SECURESIGN_PATCH <<EOF [ { "op": "replace", "path": "/spec/ctlog/serverConfigRef", "value": {"name": "ctlog-config"} }, { "op": "replace", "path": "/spec/ctlog/treeID", "value": $NEW_TREE_ID }, { "op": "replace", "path": "/spec/ctlog/privateKeyRef", "value": {"name": "ctlog-config", "key": "private"} }, { "op": "replace", "path": "/spec/ctlog/privateKeyPasswordRef", "value": {"name": "ctlog-config", "key": "password"} }, { "op": "replace", "path": "/spec/ctlog/publicKeyRef", "value": {"name": "ctlog-config", "key": "public"} } ] EOF
对 Securesign 实例进行补丁:
Example
$ oc patch Securesign securesign-sample --type='json' -p="$SECURESIGN_PATCH"
Copy to clipboardCopied$ oc patch Securesign securesign-sample --type='json' -p="$SECURESIGN_PATCH"
等待 CT 日志服务器重新部署:
Example
$ oc wait pod -l app.kubernetes.io/name=ctlog --for=condition=Ready
Copy to clipboardCopied$ oc wait pod -l app.kubernetes.io/name=ctlog --for=condition=Ready
使用更新的 TUF 配置更新
cosign
配置:Example
$ cosign initialize --mirror=$TUF_URL --root=$TUF_URL/root.json
Copy to clipboardCopied$ cosign initialize --mirror=$TUF_URL --root=$TUF_URL/root.json
现在,您已准备好使用新的 CT 日志签名器密钥签名并验证您的工件。