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 <jlecuirot@microsoft.com>
This commit is contained in:
James Le Cuirot 2025-06-03 14:42:33 +01:00
parent 010962f6d6
commit bdb87aea6b
No known key found for this signature in database
GPG Key ID: 1226415D00DD3137
9 changed files with 182 additions and 9 deletions

View File

@ -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 <<EOF > "${GNUPGHOME}"/gpg-agent.conf
scdaemon-program $(type -P gnupg-pkcs11-scd)
EOF
cat <<EOF > "${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
}

View File

@ -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.

View File

@ -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
}

View File

@ -0,0 +1,2 @@
# azure-cli creates this, seemingly due to a bug.
/None/

View File

@ -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}"

View File

@ -0,0 +1,56 @@
#!/bin/bash
set -euo pipefail
cd "${0%/*}"
COREOS_OFFICIAL=0
. ../../../../../../../../build_library/sbsign_util.sh
BITS=2048
DAYS=7300
DATE=$(date +%Y%m%d)
if [[ -f DB.key ]]; then
echo "DB.key: Exists (remove to recreate)"
else
echo "DB.key: Creating"
openssl genrsa -out DB.key "${BITS}"
echo "DB.pem: Creating"
openssl req -new -x509 -sha256 -days "${DAYS}" -subj "/CN=CoreOS test DB/" -key DB.key -out DB.pem
fi
if [[ -f shim.key ]]; then
echo "shim.key: Exists (remove to recreate)"
else
echo "shim.key: Creating"
openssl genrsa -out shim.key "${BITS}"
echo "shim-${DATE}.pem: Creating"
openssl req -new -x509 -sha256 -days "${DAYS}" -subj "/CN=shim/" -key shim.key -out shim-"${DATE}".pem
fi
if [[ -f signing.gpg ]]; then
echo "signing.gpg: Exists (remove to recreate)"
exit
fi
setup_gnupghome
echo "signing.gpg: Creating"
gpg --batch --generate-key <<-EOF
%no-protection
Key-Type: RSA
Key-Length: ${BITS}
Key-Usage: sign
Name-Real: Flatcar Secure Boot development
Name-Email: maintainers@flatcar-linux.org
Expire-Date: 0
%commit
EOF
# Write the private key to a file for signing.
gpg --export-secret-keys --output signing.gpg
# Write the public key to a file for verification.
echo "signing-${DATE}.gpg: Creating"
rm -f signing-"${DATE}".gpg
gpg --export --output signing-"${DATE}".gpg