refactor: remove the runtime binary binding; every release registration path verifies the CI signature [CI]
ci/woodpecker/push/main-amd64-build Pipeline was successful
ci/woodpecker/push/main-amd64-build Pipeline was successful
The hash a node reports about itself cannot be trusted (a swapped binary can copy the old hash), so the runtime binding only caught files that were replaced by something that did not lie. Provenance stays where it can be verified: the signed release manifest (digest) and the signed SHA256SUMS. - Remove BinaryBindingService, the node_bindings table (schema v15 drops it), BinaryPolicy, the handshake hash/manifest fields and the admin binary column. - Drop the per-platform "binaries" list from the release manifest, the hash scripts and the updater's installed.manifest copy. - ReleaseService.PublishAsync now verifies the release signature, so the admin POST /releases route is no longer an unsigned path; tests sign with a test key. - Docs: section 16 explains why the runtime check is gone.
This commit is contained in:
1 parent
a4f3d1892a
commit
904ea2b0ae
49 files changed
+115
-1143
No files matched your search
@@ -122,8 +122,7 @@ fetch_offer() {
|
||||
cp "$OFFER_MANIFEST" "$WORK/feed.manifest"; cp "$OFFER_SIG" "$WORK/feed.sig"
|
||||
fi
|
||||
fetch_mailbox
|
||||
# 信箱里没有东西,而仓库里的清单没通过(验证失败,或者就是已经装的这一份):留下仓库的那份,让后面照常报出原因,
|
||||
# 而不是悄悄当作没有更新;「已经装的这一份」也要留下,因为要把它交给节点握手时带给 Backend
|
||||
# 信箱里没有东西,而仓库里的清单没通过(验证失败,或者就是已经装的这一份):留下仓库的那份,让后面照常报出原因,而不是悄悄当作没有更新
|
||||
if [ -n "$feed_error" ] && [ ! -s "$OFFER_MANIFEST" ]; then
|
||||
cp "$WORK/feed.manifest" "$OFFER_MANIFEST"; cp "$WORK/feed.sig" "$OFFER_SIG"
|
||||
fi
|
||||
@@ -165,29 +164,6 @@ write_status() { # state available_json error
|
||||
fi
|
||||
}
|
||||
|
||||
# 节点握手时要带给 Backend 的「我是从哪份 CI 签名清单安装的」:Backend 用 CI 公钥验过,才相信清单里列的官方二进制哈希。
|
||||
# 写在节点只读挂载的状态目录里(节点自己改不了),清单在前、签名在后;内容没变就不写(保护路由器的闪存)。
|
||||
write_installed() {
|
||||
if cmp -s "$OFFER_MANIFEST" "$STATE_DIR/$COMPONENT.installed.manifest" 2>/dev/null && cmp -s "$OFFER_SIG" "$STATE_DIR/$COMPONENT.installed.sig" 2>/dev/null; then
|
||||
return 0
|
||||
fi
|
||||
for pair in "installed.manifest:$OFFER_MANIFEST" "installed.sig:$OFFER_SIG"; do
|
||||
name="${pair%%:*}"; src="${pair#*:}"
|
||||
if [ -n "$DATA_VOLUME" ]; then
|
||||
"$ENGINE" run --pull=never --rm -i --user 0 --network none --read-only --cap-drop ALL --cap-add DAC_OVERRIDE --security-opt no-new-privileges \
|
||||
-v "$STATUS_VOLUME:/status" --entrypoint /bin/sh "$HELPER_IMAGE" \
|
||||
-c 'tmp=$(mktemp /status/.installed.XXXXXX); cat > "$tmp"; chmod 644 "$tmp"; mv -f "$tmp" "/status/$1"' sh "$name" < "$src" || return 0
|
||||
else
|
||||
destination="$(mktemp "$STATUS_DIR/.installed.XXXXXX")"
|
||||
cp "$src" "$destination"
|
||||
chmod 644 "$destination"
|
||||
mv -f "$destination" "$STATUS_DIR/$name"
|
||||
fi
|
||||
done
|
||||
cp "$OFFER_MANIFEST" "$STATE_DIR/$COMPONENT.installed.manifest"
|
||||
cp "$OFFER_SIG" "$STATE_DIR/$COMPONENT.installed.sig"
|
||||
}
|
||||
|
||||
# 第一次部署(deploy-node.sh 已经验过签并按 digest 固定)时还没有安装记录:清单的 digest 就是正在跑的这个镜像,就记下来,不当作「有更新」
|
||||
seed_installed() {
|
||||
[ ! -r "$INSTALLED" ] || return 0
|
||||
@@ -283,8 +259,6 @@ do_check() {
|
||||
case "$ERR" in
|
||||
"no offer") write_status up_to_date null "" ;;
|
||||
"stale manifest"*)
|
||||
# 就是已经装的这一份(同一个 seq、同一个 digest):交给节点握手时带给 Backend
|
||||
if [ "$M_SEQ" = "$(installed_seq)" ] && [ "$M_DIGEST" = "$(current_digest)" ]; then write_installed; fi
|
||||
write_status up_to_date null "" ;;
|
||||
*) write_status invalid null "$ERR"; echo "offer rejected: $ERR" >&2 ;;
|
||||
esac
|
||||
@@ -293,7 +267,6 @@ do_check() {
|
||||
adopt_next_key
|
||||
seed_installed
|
||||
if [ "$M_DIGEST" = "$(current_digest)" ]; then
|
||||
write_installed
|
||||
write_status up_to_date null ""
|
||||
return 0
|
||||
fi
|
||||
@@ -339,7 +312,6 @@ do_apply() {
|
||||
fi
|
||||
if [ "$healthy" = true ] && service_ctl is-active --quiet "$UNIT" && [ "$before" = "$after" ]; then
|
||||
printf '%s\n%s\n' "$M_SEQ" "$M_DIGEST" > "$INSTALLED"
|
||||
write_installed
|
||||
write_status up_to_date null ""
|
||||
echo "updated to $M_TAG"
|
||||
return 0
|
||||
|
||||
@@ -111,22 +111,13 @@ expect "first install: wrong signing key is refused" "REFUSED" "$(resolve "$T/c.
|
||||
expect "first install: manifest for another component is refused" "REFUSED" "$(resolve "$T/b.pem" probe "$REPO")"
|
||||
expect "first install: manifest naming another repository is refused" "REFUSED" "$(resolve "$T/b.pem" agent "registry.evil/moshiro-dev/moshiro-agent")"
|
||||
|
||||
# --- 第一次部署:正在跑的镜像就是清单里的这个 digest → 记成已安装(不当作有更新),并把验过的清单交给节点握手时带上
|
||||
# --- 第一次部署:正在跑的镜像就是清单里的这个 digest → 记成已安装(不当作有更新)
|
||||
sed -i '/^FEED=off/d' "$T/conf/agent.conf"
|
||||
rm -f "$T/state/agent.installed" "$T/state/agent.installed.manifest" "$T/state/agent.installed.sig" "$T/data/update-status/installed."* "$T/data/update/offer."*
|
||||
rm -f "$T/state/agent.installed" "$T/data/update/offer."*
|
||||
printf 'IMAGE=%s@%s\n' "$REPO" "$DIGEST" > "$T/env"
|
||||
publish "$T/b.pem" 50
|
||||
expect "first deploy: the running image is already up to date" "up_to_date|" "$(check)"
|
||||
if [ "$(sed -n 2p "$T/state/agent.installed" 2>/dev/null)" = "$DIGEST" ]; then echo "ok first deploy: install record seeded from the running image"; else echo "FAIL first deploy: no install record"; fail=1; fi
|
||||
label="$(cat "$T/feed/$REF_KEY")"
|
||||
if [ "$(cat "$T/data/update-status/installed.manifest" 2>/dev/null | base64 -w0)" = "${label%%|*}" ] && [ "$(cat "$T/data/update-status/installed.sig" 2>/dev/null)" = "${label#*|}" ]; then
|
||||
echo "ok the verified manifest is left for the node to present at its handshake"
|
||||
else
|
||||
echo "FAIL installed.manifest / installed.sig missing or different"; fail=1
|
||||
fi
|
||||
# 老版本的更新器没写过:同一份(seq 相同)下次检查补上
|
||||
rm -f "$T/data/update-status/installed."* "$T/state/agent.installed.manifest" "$T/state/agent.installed.sig"
|
||||
expect "already installed: still up to date" "up_to_date|" "$(check)"
|
||||
if [ -s "$T/data/update-status/installed.manifest" ] && [ -s "$T/data/update-status/installed.sig" ]; then echo "ok a missing copy is written again for the node"; else echo "FAIL the copy was not written again"; fail=1; fi
|
||||
|
||||
exit "$fail"
|
||||
Reference in new issue
Block a user