From bdb87aea6b875af3e88a0d43b1ca5884e49423ec Mon Sep 17 00:00:00 2001 From: James Le Cuirot Date: Tue, 3 Jun 2025 14:42:33 +0100 Subject: [PATCH] coreos-base/coreos-sb-keys: Add GPG keys, generation scripts, new README We want to sign and verify kernel load scripts with GRUB, but it only supports GPG signatures for plain text files. To avoid needing to manage another key in Azure Key Vault for official builds, the existing key has been converted, keeping the start and end dates from the existing certificate. For unofficial builds, it is awkward to convert plain PEM files into GPG keys, but there is no need to use the same key in this case anyway, so a new key has been created. Being publicly visible, it has no expiry date. Two new scripts have been added to generate official and unofficial keys and certificates. The README has been rewritten with details on how to use these scripts and what each file is actually for. Signed-off-by: James Le Cuirot --- build_library/sbsign_util.sh | 25 ++++++- .../coreos-base/coreos-sb-keys/README.md | 30 ++++++-- .../coreos-sb-keys-2.0.0.ebuild | 11 ++- .../coreos-sb-keys/files/official/.gitignore | 2 + .../files/official/refresh_keys | 67 ++++++++++++++++++ .../files/official/signing-20250320.gpg | Bin 0 -> 1192 bytes .../files/unofficial/refresh_keys | 56 +++++++++++++++ .../files/unofficial/signing-20250604.gpg | Bin 0 -> 677 bytes .../files/unofficial/signing.gpg | Bin 0 -> 1328 bytes 9 files changed, 182 insertions(+), 9 deletions(-) create mode 100644 sdk_container/src/third_party/coreos-overlay/coreos-base/coreos-sb-keys/files/official/.gitignore create mode 100755 sdk_container/src/third_party/coreos-overlay/coreos-base/coreos-sb-keys/files/official/refresh_keys create mode 100644 sdk_container/src/third_party/coreos-overlay/coreos-base/coreos-sb-keys/files/official/signing-20250320.gpg create mode 100755 sdk_container/src/third_party/coreos-overlay/coreos-base/coreos-sb-keys/files/unofficial/refresh_keys create mode 100644 sdk_container/src/third_party/coreos-overlay/coreos-base/coreos-sb-keys/files/unofficial/signing-20250604.gpg create mode 100644 sdk_container/src/third_party/coreos-overlay/coreos-base/coreos-sb-keys/files/unofficial/signing.gpg diff --git a/build_library/sbsign_util.sh b/build_library/sbsign_util.sh index b8017224d6..6eb82ea14d 100644 --- a/build_library/sbsign_util.sh +++ b/build_library/sbsign_util.sh @@ -10,9 +10,10 @@ else SBSIGN_CERT="/usr/share/sb_keys/official/signing.pem" fi -PKCS11_MODULE_PATH="/usr/$(get_sdk_libdir)/pkcs11/azure-keyvault-pkcs11.so" +PKCS11_MODULE_PATH="$(pkg-config p11-kit-1 --variable p11_module_path)/azure-keyvault-pkcs11.so" PKCS11_ENV=( + AZURE_CORE_COLLECT_TELEMETRY=no AZURE_KEYVAULT_URL="https://flatcar-sb-dev-kv.vault.azure.net/" PKCS11_MODULE_PATH="${PKCS11_MODULE_PATH}" AZURE_KEYVAULT_PKCS11_DEBUG=1 @@ -31,3 +32,25 @@ do_sbsign() { --cert "${SBSIGN_CERT}" \ "${@}" } + +setup_gnupghome() { + export GNUPGHOME + GNUPGHOME=$(mktemp -d) + trap 'gpgconf --kill gpg-agent; rm -r -- "${GNUPGHOME}"' EXIT + + # Unofficial builds simply use a local private key. + [[ ${COREOS_OFFICIAL:-0} -ne 1 ]] && return + + cat < "${GNUPGHOME}"/gpg-agent.conf +scdaemon-program $(type -P gnupg-pkcs11-scd) +EOF + + cat < "${GNUPGHOME}"/gnupg-pkcs11-scd.conf +providers kms +provider-kms-library ${PKCS11_MODULE_PATH} +log-file /dev/null +EOF + + # This fetches the private keys from AKV. + gpg --card-status +} diff --git a/sdk_container/src/third_party/coreos-overlay/coreos-base/coreos-sb-keys/README.md b/sdk_container/src/third_party/coreos-overlay/coreos-base/coreos-sb-keys/README.md index 77f406af6f..0f4a53af23 100644 --- a/sdk_container/src/third_party/coreos-overlay/coreos-base/coreos-sb-keys/README.md +++ b/sdk_container/src/third_party/coreos-overlay/coreos-base/coreos-sb-keys/README.md @@ -1,15 +1,31 @@ ## Keys & Certificates -- DB (Signature Database): The signature database is used to validate signed EFI binaries. -- Shim Certificates: Our set of certificates +### X.509 signature database - DB.key, DB.pem +The signature database is used by the UEFI firmware to validate signed EFI binaries (e.g. the shim). In this case, `DB.key` and `DB.pem` are only used for testing with QEMU. Real deployments use the database provided by the bare metal host or hypervisor. + +### X.509 shim vendor certificates - shim.key, shim-*.pem + +Unofficial builds: `shim-*.pem` are the current and historical self-signed signing certificates used to sign the bootloader and kernel. `shim.key` is the private key for the current certificate. + +Official builds: `shim-*.pem` are the current and historical CA certificates that issue the signing certificates. The private key is only needed to issue new signing certificates so is kept offline. + +### X.509 signing certificate - signing.pem + +Unofficial builds: The current signing certificate is also the current shim vendor certificate above, so there is no separate `signing.pem`. + +Official builds: `signing.pem` is the current signing certificate used to sign the bootloader and kernel. It is copied from Azure Key Vault, where the private key is also stored. + +### GPG signing keys - signing*.gpg + +Unofficial builds: `signing.gpg` is the current private key used to sign the kernel load script. `signing-*.gpg` are the historical public keys used to verify them. They have no expiry date. + +Official builds: `signing.gpg` is the current public key used to sign the kernel load script. `signing-*.gpg` are the historical public keys used to verify them. These keys are created from their respective X.509 signing certificates. As such, the private keys are only stored in Azure Key Vault. The start and end dates of the keys also match the certificates. ## Generation of Keys & Certificates +Unofficial builds: Delete any of `DB.key`, `shim.key`, or `signing.gpg` to force recreation. -Generate the our shim certificates: +Official builds: Delete `signing.gpg` to force recreation. `shim-*.pem` must be updated manually. `signing.pem` is refreshed from Azure Key Vault using the details stored in `build_library/sbsign_util.sh`. Ensure [Azure CLI](https://learn.microsoft.com/cli/azure/install-azure-cli) is in your `PATH`. -``` -openssl genrsa -out "shim.key" 2048 -openssl req -new -x509 -sha256 -subj "/CN=shim/" -key "shim.key" -out "shim.pem" -days 7300 -``` +Run the `refresh_keys` script without any arguments. Bump the coreos-sb-keys package with the changes. diff --git a/sdk_container/src/third_party/coreos-overlay/coreos-base/coreos-sb-keys/coreos-sb-keys-2.0.0.ebuild b/sdk_container/src/third_party/coreos-overlay/coreos-base/coreos-sb-keys/coreos-sb-keys-2.0.0.ebuild index 2a6ffb79d3..6baa9ed3a1 100644 --- a/sdk_container/src/third_party/coreos-overlay/coreos-base/coreos-sb-keys/coreos-sb-keys-2.0.0.ebuild +++ b/sdk_container/src/third_party/coreos-overlay/coreos-base/coreos-sb-keys/coreos-sb-keys-2.0.0.ebuild @@ -56,5 +56,14 @@ src_install() { doins -r unofficial official insinto /usr/share/sb_keys/unofficial - doins "${FILESDIR}"/unofficial/{DB.{key,pem},shim.key} + doins "${FILESDIR}"/unofficial/{DB.{key,pem},shim.key,signing*.gpg} + + insinto /usr/share/sb_keys/official + doins "${FILESDIR}"/official/signing-*.gpg + + # For unofficial builds, we install the GPG private key as signing.gpg to + # sign with. For official builds, we create a symlink to the newest public + # key instead because the private key is in Azure Key Vault. + local FILES=( "${FILESDIR}"/official/signing-*.gpg ) + dosym "${FILES[-1]##*/}" /usr/share/sb_keys/official/signing.gpg } diff --git a/sdk_container/src/third_party/coreos-overlay/coreos-base/coreos-sb-keys/files/official/.gitignore b/sdk_container/src/third_party/coreos-overlay/coreos-base/coreos-sb-keys/files/official/.gitignore new file mode 100644 index 0000000000..64d1ee2030 --- /dev/null +++ b/sdk_container/src/third_party/coreos-overlay/coreos-base/coreos-sb-keys/files/official/.gitignore @@ -0,0 +1,2 @@ +# azure-cli creates this, seemingly due to a bug. +/None/ diff --git a/sdk_container/src/third_party/coreos-overlay/coreos-base/coreos-sb-keys/files/official/refresh_keys b/sdk_container/src/third_party/coreos-overlay/coreos-base/coreos-sb-keys/files/official/refresh_keys new file mode 100755 index 0000000000..5f68c21f47 --- /dev/null +++ b/sdk_container/src/third_party/coreos-overlay/coreos-base/coreos-sb-keys/files/official/refresh_keys @@ -0,0 +1,67 @@ +#!/bin/bash + +set -euo pipefail +cd "${0%/*}" + +COREOS_OFFICIAL=1 +. ../../../../../../../../build_library/sbsign_util.sh +export "${PKCS11_ENV[@]}" + +echo "Fetching ${SBSIGN_KEY} from Azure" +CERT=$(p11-kit export-object --provider "${PKCS11_MODULE_PATH}" "${SBSIGN_KEY};type=cert") + +if [[ -f signing.pem && ${CERT} = "$(< signing.pem)" ]]; then + echo "signing.pem: Unchanged" +else + echo "signing.pem: Updating" + cat > signing.pem <<< "${CERT}" +fi + +CERT=signing.pem + +# Copy the X.509 dates to the GPG key. This isn't required, but it helps to +# identify the key, and not setting an expiry date is bad practise. +START=$(openssl x509 -noout -in "${CERT}" -startdate) +START=$(date -d "${START#*=}" -u +%Y%m%dT%H%M%S) +# +END=$(openssl x509 -noout -in "${CERT}" -enddate) +END=$(date -d "${END#*=}" -u +%Y%m%dT%H%M%S) + +# We also use the start date to name the GPG key. +OUTPUT=signing-${START%%T*}.gpg + +if [[ -f ${OUTPUT} ]]; then + echo "${OUTPUT}: Exists (remove to recreate)" + exit +fi + +setup_gnupghome + +# This isn't a great way to find the matching key from AKV, and it only works +# with RSA keys, but keygrips are internal to GPG, so there isn't much choice. +MODULUS=$(openssl x509 -in "${CERT}" -noout -modulus) +MODULUS=${MODULUS#*=} +# +for KEYGRIP in "${GNUPGHOME}"/private-keys-v1.d/*.key; do + if tr -d "\n " < "${KEYGRIP}" | grep -qF "${MODULUS}"; then + break + fi +done +# +KEYGRIP=${KEYGRIP##*/} +KEYGRIP=${KEYGRIP%.key} + +echo "${OUTPUT}: Creating" +gpg --batch --generate-key <<-EOF +Key-Type: RSA +Key-Grip: ${KEYGRIP} +Key-Usage: sign +Name-Real: Flatcar Secure Boot official +Name-Email: maintainers@flatcar-linux.org +Creation-Date: ${START} +Expire-Date: ${END} +%commit +EOF + +# Only write the public key to a file. The private key remains in AKV. +gpg --export --output "${OUTPUT}" diff --git a/sdk_container/src/third_party/coreos-overlay/coreos-base/coreos-sb-keys/files/official/signing-20250320.gpg b/sdk_container/src/third_party/coreos-overlay/coreos-base/coreos-sb-keys/files/official/signing-20250320.gpg new file mode 100644 index 0000000000000000000000000000000000000000..9e98573fe8185c3a64e0533ac3a416c006dfa8a0 GIT binary patch literal 1192 zcmV;Z1Xuf+0u2Ob+yI6F5CDsS(0G?y0XIJBweEZ_If&@~e&9W)^7~r$G!}&Tq8YND zhM0gH)Lg}P-*NjtJhiGx+0?B31IIj`T66^itI^5tpqNj$X-+?}D10Y!l;BQ%W9mQI zd)|-}r*GpXbt3z3Jb5I?1%8Hrx}-ga9!@ebL1X$mIM4TnLO2vB2cjF{HjHlUrISAI zu81`x!{`|2Yf5HsX=;agym=X;@HFHzBn&!tip8#&YO;=Xuk<*-8f^LV5N3ivF#a)5 zRDvM`wUAqW-42WvE#}VKUgeL_fd9` zDb4ji&Ivqh@_9pdR!3An3^*Me1aimJ6rUU85d@e8BLD2_&y~WXd9|J%w#|RR$c%!S zj(V;Ig<52eH-!KZ0RRECJVtC`bYo$1AX8;yb#i4OLT_($Aa7=7X=7<&Y#=;sVQFr3 zVQFqN6RE^l&YK8XTX1QP)W06`WZ1kbuZH9$hxYx@WI|Mn)# z(l+=0iUk5^+yI6G8v_Li1*Gf%1q%rX2Lb{j0tOWd2?z@U1Qr4V0RkQe0vCV)3JDPQ zCd|?{_x_4Kvk(7Hya&vM5enm=Axj(L5S^knIcKgZlhqw1M4-(XVd;kv`TK&u5YODb z&d_1y^AA5oYJ?hOCO?&rRYL~m+sGQzB8b1CR872k)}@tFkoS^JcLTk>HV8OR5PmUA zP7T#pP_coQ%c-}v(P@TgZ2erzMp&c!$=19|C%Vr4l*e&r$xlOFE7VZRB?Dm-zNy&X zmn1$b9SF`ZqGP~v0E@>B2~FE*AuLs_->{E=55nSpsR+@wJ*pLr?pW(U*XtkG)Beze zXXl1f^#=EAEeidm5zH`tts1EPS7|_{d4UCjXV2-pF_n$7d{(n#ZY)PH;if_Jh7SmS zvBCHP2gPSi)8z@iFKc=JjyLbgvr8|{otXD2h^?1HUJtV*6KgnBvm?>PF&Rq3Azoz> zUpG&XYcqP1KM&7+LOEGA>XU9Am)8|XjE**aR| z(o70sBgwo{unnN;dR4chm644qgs?=+KX_CC`pMU8{&zOv9bE#eJW(xkjvybe)Nr@B|~un4LQ5BFrVon78Y{gO)deAC@)wm z7F=&W?v*Xd*!tBVmAectZEqbjg4=;rYl(UFYg1R+2#rkW@DwQEN33X>Mh5b3kTnVRU0*axH9WZgqGrZ*pfoi2+dr69EVSI~E}XJB4%I!&6Id zEK>C@{%V)S1)q(J1p;V5V3YzI0|g5S2nPZJA_4{#3JC}c0t6NU0|5da2Lcy>0162Z z{%V)S1)q(JA(RLJlH#s-Nq7O05xc$38E?yY79n%gD}` L;8q+IBOgjHbcrt2 literal 0 HcmV?d00001 diff --git a/sdk_container/src/third_party/coreos-overlay/coreos-base/coreos-sb-keys/files/unofficial/signing.gpg b/sdk_container/src/third_party/coreos-overlay/coreos-base/coreos-sb-keys/files/unofficial/signing.gpg new file mode 100644 index 0000000000000000000000000000000000000000..ce520623a4fc3bae0acfe90cb3de52b69e1a4397 GIT binary patch literal 1328 zcmV-01<(4G1DFJ8Kwy*s2ms2?VzcDcfi^ERk)AOeAJ8Z|YIVwVx(S*3SE1iI!SSVz z9ttA(w|Yxc8eO5Pe z?z8o18Mz4Be!T_N)f!=$LQ{bs90cHAhB(h*xc-@&fmZM#A5hgP+XG?oU+bkNVzmyH9Yk5Eh8 zU}T9Pe+g&f28#}a)YTj#4H@#VVx^~9>MT1ruu};=Fp1(6=I_v1f&A{`kPnhEnMYu@?X!d z%Xq9qcrJe4s5Q+_Wxe4J%ihomw4w13{`Q1~2XN&!>N$V{m$Omhr(PJZ1OWIBdLz2x zB!5qP?tI{un=fC(*#{W{nhPakf;RGu@1Md4)B>FUA<-ox$d5pwILbl2zmwJFyxP&B z=w_@(2DLJrDDPBm1+}`j%~)tym1Ob4WT9;IV5#Ji-yyTXmv#PUR^_exTpF}805#U% z>PCb(dsvxD@Y8x{*;tV}m@@rLW*Gy@J9@eZM6hp zV_|Y2Q)Odya%CVwZ*O!UWMy_`Y;SOFWo~pJJZ)iVZggR3Ze?&7*Q%i3wQuQwWYL~?YpN)$J0%$;BlmZ(A1q%rX z2Lb{j0tOWd2?z@U1Qr4V0RkQe0vCV)3JDPYYL~?YpN)$lln4Nl;k!o@pNarX(E6;V`~35kD5lL zP^>OQ?9YE~YzhgY2bM9`IAH mtZ~muLGa*?h~N)Otr2p^J}<$$?tQb%$j+DGRvZ)~A4)He