2
0
mirror of https://github.com/xcat2/xcat-core.git synced 2026-08-27 17:16:40 +00:00

feat(genesis): wire signed extension bundles

This commit is contained in:
Vinícius Ferrão
2026-08-20 23:03:49 -03:00
parent 56040cb4f0
commit 50c3b00bac
3 changed files with 125 additions and 2 deletions
+105
View File
@@ -0,0 +1,105 @@
#!/bin/bash
set -euo pipefail
fail() {
printf 'Genesis extension export: %s\n' "$*" >&2
exit 1
}
if (($# != 6)); then
printf '%s\n' \
'Usage: export-extension ARCHITECTURE EXTENSION DEPLOY_DIRECTORY PRIVATE_KEY PUBLIC_KEY OUTPUT_DIRECTORY' >&2
exit 2
fi
architecture=$1
extension=$2
deploy_dir=${3%/}
private_key=$4
public_key=$5
output_dir=${6%/}
script_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
case "$architecture" in
x86|x86_64|ppc64|ppc64le|armv7hf|aarch64|riscv64) ;;
*) fail "unsupported architecture: $architecture" ;;
esac
[[ $extension =~ ^[a-z0-9][a-z0-9._-]{0,63}$ ]] \
|| fail "invalid extension name: $extension"
[[ -d $deploy_dir && ! -L $deploy_dir ]] \
|| fail "invalid deploy directory: $deploy_dir"
[[ -f $private_key && ! -L $private_key ]] \
|| fail "invalid private key: $private_key"
[[ -f $public_key && ! -L $public_key ]] \
|| fail "invalid public key: $public_key"
[[ ! -e $output_dir && ! -L $output_dir ]] \
|| fail "output already exists: $output_dir"
machine=xcat-genesis-${architecture//_/-}
machine_dir=$deploy_dir/images/$machine
stem=$extension-$machine
resolve_artifact() {
local path=$1 resolved
resolved=$(readlink -f -- "$path")
[[ -f $resolved && ! -L $resolved ]] || fail "missing artifact: $path"
printf '%s\n' "$resolved"
}
image=$(resolve_artifact "$machine_dir/$stem.squashfs-zst")
manifest=$(resolve_artifact "$machine_dir/$stem.manifest.json")
key_id=$(python3 - "$manifest" <<'PY'
import json
import re
import sys
with open(sys.argv[1], encoding="utf-8") as source:
value = json.load(source).get("key_id")
if not isinstance(value, str) or not re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9._-]{0,63}", value):
raise SystemExit(1)
print(value)
PY
) || fail 'extension manifest has an invalid key ID'
parent=$(dirname -- "$output_dir")
name=$(basename -- "$output_dir")
install -d -m 0755 "$parent"
staging=$(mktemp -d "$parent/.${name}.XXXXXX")
private_public=$(mktemp "$parent/.${name}.private-public.XXXXXX")
provided_public=$(mktemp "$parent/.${name}.provided-public.XXXXXX")
trap 'rm -rf -- "$staging"; rm -f -- "$private_public" "$provided_public"' EXIT
openssl pkey -in "$private_key" -pubout -outform DER \
-out "$private_public" 2>/dev/null \
|| fail 'private key is not a valid signing key'
openssl pkey -pubin -in "$public_key" -outform DER \
-out "$provided_public" 2>/dev/null \
|| fail 'public key is not valid'
cmp -s -- "$private_public" "$provided_public" \
|| fail 'public key does not match the private key'
install -d -m 0755 "$staging/extensions" "$staging/extension-keys"
install -m 0644 "$image" "$staging/extensions/$stem.squashfs-zst"
install -m 0644 "$manifest" "$staging/extensions/$stem.manifest.json"
"$script_dir/scripts/sign-extension" \
"$staging/extensions/$stem.manifest.json" "$private_key" \
"$staging/extensions/$stem.sig" >/dev/null
install -m 0644 "$public_key" "$staging/extension-keys/$key_id.pem"
openssl pkeyutl -verify -pubin -inkey "$public_key" -rawin \
-in "$staging/extensions/$stem.manifest.json" \
-sigfile "$staging/extensions/$stem.sig" >/dev/null 2>&1 \
|| fail 'generated extension signature did not verify'
(
cd "$staging"
find extension-keys extensions -type f -print \
| LC_ALL=C sort \
| xargs sha256sum -- >SHA256SUMS
sha256sum -c SHA256SUMS >/dev/null
)
mv -- "$staging" "$output_dir"
rm -f -- "$private_public" "$provided_public"
trap - EXIT
printf 'Exported Genesis extension %s to %s\n' "$extension" "$output_dir"
@@ -167,7 +167,7 @@ load_all() {
((${#manifests[@]} > 0)) || fail "no extension manifests found in: $directory"
for manifest in "${manifests[@]}"; do
stem=${manifest%.manifest.json}
install_extension "$manifest" "$stem.raw" "$stem.sig"
install_extension "$manifest" "$stem.squashfs-zst" "$stem.sig"
done
publish_status READY 'Genesis extensions loaded'
}
@@ -13,6 +13,7 @@ RDEPENDS:${PN} = "bash coreutils jq openssl-bin systemd xcat-genesis-init"
SYSTEMD_SERVICE:${PN} = "xcat-genesis-extensions.service"
SYSTEMD_AUTO_ENABLE = "enable"
XCAT_GENESIS_EXTENSION_BUNDLE ??= ""
do_install() {
install -d ${D}${libexecdir}/xcat
@@ -23,10 +24,27 @@ do_install() {
install -m 0644 ${UNPACKDIR}/xcat-genesis-extensions.service \
${D}${systemd_system_unitdir}/xcat-genesis-extensions.service
install -d ${D}${datadir}/xcat/genesis/extension-keys
install -d ${D}${datadir}/xcat/genesis/extension-keys \
${D}${localstatedir}/lib/xcat/genesis/extensions
if [ -n "${XCAT_GENESIS_EXTENSION_BUNDLE}" ]; then
case "${XCAT_GENESIS_EXTENSION_BUNDLE}" in
*[!A-Za-z0-9._-]*|.|..) bbfatal "Invalid Genesis extension bundle name" ;;
esac
bundle=${UNPACKDIR}/${XCAT_GENESIS_EXTENSION_BUNDLE}
test -d "$bundle/extension-keys" \
|| bbfatal "Genesis extension bundle has no trust store"
test -d "$bundle/extensions" \
|| bbfatal "Genesis extension bundle has no extensions"
cp -R --no-preserve=ownership "$bundle/extension-keys/." \
${D}${datadir}/xcat/genesis/extension-keys/
cp -R --no-preserve=ownership "$bundle/extensions/." \
${D}${localstatedir}/lib/xcat/genesis/extensions/
fi
}
FILES:${PN} = "${libexecdir}/xcat/genesis-sysext \
${systemd_system_unitdir}/xcat-genesis-extensions.service \
${datadir}/xcat/genesis/extension-keys \
${localstatedir}/lib/xcat/genesis/extensions \
"