mirror of
https://github.com/XRPLF/rippled.git
synced 2026-08-22 23:00:55 +00:00
build: Sign RPM packages (#8046)
This commit is contained in:
4
.github/scripts/strategy-matrix/linux.json
vendored
4
.github/scripts/strategy-matrix/linux.json
vendored
@@ -92,7 +92,7 @@
|
||||
"build_type": ["Release"],
|
||||
"arch": ["amd64"],
|
||||
"minimal": false,
|
||||
"image": "ghcr.io/xrplf/xrpld/packaging-debian:sha-028ccea"
|
||||
"image": "ghcr.io/xrplf/xrpld/packaging-debian:sha-a6983f8"
|
||||
}
|
||||
],
|
||||
|
||||
@@ -102,7 +102,7 @@
|
||||
"build_type": ["Release"],
|
||||
"arch": ["amd64"],
|
||||
"minimal": false,
|
||||
"image": "ghcr.io/xrplf/xrpld/packaging-rhel:sha-028ccea"
|
||||
"image": "ghcr.io/xrplf/xrpld/packaging-rhel:sha-a6983f8"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
1
.github/workflows/on-tag.yml
vendored
1
.github/workflows/on-tag.yml
vendored
@@ -49,3 +49,4 @@ jobs:
|
||||
secrets:
|
||||
remote_username: ${{ secrets.NEXUS_REMOTE_USERNAME }}
|
||||
remote_password: ${{ secrets.NEXUS_REMOTE_PASSWORD }}
|
||||
signing_key: ${{ secrets.NEXUS_PACKAGES_PRIVATE_KEY }}
|
||||
|
||||
1
.github/workflows/on-trigger.yml
vendored
1
.github/workflows/on-trigger.yml
vendored
@@ -120,3 +120,4 @@ jobs:
|
||||
secrets:
|
||||
remote_username: ${{ secrets.NEXUS_REMOTE_USERNAME }}
|
||||
remote_password: ${{ secrets.NEXUS_REMOTE_PASSWORD }}
|
||||
signing_key: ${{ secrets.NEXUS_PACKAGES_PRIVATE_KEY }}
|
||||
|
||||
11
.github/workflows/reusable-package.yml
vendored
11
.github/workflows/reusable-package.yml
vendored
@@ -29,6 +29,9 @@ on:
|
||||
remote_password:
|
||||
description: "The password or token for that Nexus account."
|
||||
required: false
|
||||
signing_key:
|
||||
description: "Armoured PGP private key used to sign the RPMs. Required when publishing."
|
||||
required: false
|
||||
|
||||
defaults:
|
||||
run:
|
||||
@@ -98,6 +101,14 @@ jobs:
|
||||
PKG_CHANNEL: ${{ steps.release_info.outputs.channel }}
|
||||
run: ./package/build_pkg.sh
|
||||
|
||||
# Before the upload, so the artifact and the published package are the
|
||||
# same bytes. DEBs are not signed, so the key is never set on that job.
|
||||
- name: Sign RPM
|
||||
if: ${{ inputs.publish && matrix.distro == 'rhel' }}
|
||||
env:
|
||||
PKG_SIGNING_KEY: ${{ secrets.signing_key }}
|
||||
run: ./package/sign_rpm.sh "${BUILD_DIR}"
|
||||
|
||||
- name: Upload package artifact
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
|
||||
@@ -9,6 +9,7 @@ a build configured with `-Dvalidator_keys=ON`.
|
||||
```
|
||||
package/
|
||||
build_pkg.sh Staging and build script (called by the CMake `package` target and CI)
|
||||
sign_rpm.sh Signs the built RPMs (called by CI when publishing)
|
||||
publish_pkg.sh Uploads built packages to the XRPLF Nexus repositories (called by CI)
|
||||
rpm/
|
||||
xrpld.spec RPM spec
|
||||
@@ -32,7 +33,7 @@ package manager (`apt-get` -> deb, `dnf`/`yum` -> rpm).
|
||||
|
||||
| Package type | Image (`package_configs.<distro>[].image` in `linux.json`) | Tools required |
|
||||
| ------------ | ---------------------------------------------------------- | --------------------------------------------------- |
|
||||
| RPM | `ghcr.io/xrplf/xrpld/packaging-rhel:sha-<sha>` | `rpmbuild` |
|
||||
| RPM | `ghcr.io/xrplf/xrpld/packaging-rhel:sha-<sha>` | `rpmbuild`, `rpmsign` |
|
||||
| DEB | `ghcr.io/xrplf/xrpld/packaging-debian:sha-<sha>` | `dpkg-buildpackage`, debhelper with compat level 13 |
|
||||
|
||||
To print the full packaging matrix (artifact names and images) for the current
|
||||
@@ -152,11 +153,14 @@ any `XRPLF` repository, `on-pr.yml` never. Both authenticate with the
|
||||
`NEXUS_REMOTE_USERNAME` / `NEXUS_REMOTE_PASSWORD` secrets already used for the
|
||||
Conan remote.
|
||||
|
||||
Nexus owns the repository metadata; nothing here signs or indexes anything. Worth
|
||||
knowing:
|
||||
Nexus owns the repository metadata; nothing here indexes anything. Worth knowing:
|
||||
|
||||
- Each apt-hosted repository needs a distribution and a PGP signing keypair
|
||||
configured in Nexus, which rejects one created without a keypair.
|
||||
configured in Nexus, which rejects one created without a keypair. Nexus signs
|
||||
the apt metadata with it, never the packages.
|
||||
- Hosted yum repositories cannot be signed by Nexus at all, so `sign_rpm.sh`
|
||||
signs the RPMs before they are uploaded, and rpm clients verify with
|
||||
`gpgcheck=1` rather than `repo_gpgcheck=1`.
|
||||
- yum metadata is rebuilt asynchronously, so a successful publish is not
|
||||
immediately installable.
|
||||
- Each job uploads only what it built, and uploads are not transactional, so a
|
||||
@@ -212,8 +216,12 @@ fail early.
|
||||
Flags are for explicit invocation; environment variables are intended for
|
||||
CMake/CI integration. The CI workflow and the CMake `package` target both invoke
|
||||
`build_pkg.sh` with no flags; CMake supplies `SRC_DIR`, `BUILD_DIR`, and
|
||||
`PKG_RELEASE` via env, while CI supplies `BUILD_DIR` and `PKG_RELEASE` via env
|
||||
and lets the script use defaults for the rest.
|
||||
`PKG_RELEASE` via env, while CI supplies `BUILD_DIR`, `PKG_RELEASE` and
|
||||
`PKG_CHANNEL` via env and lets the script use defaults for the rest.
|
||||
|
||||
Signing is not part of this script. `sign_rpm.sh` does it in a separate CI step
|
||||
that only runs when publishing, so a published RPM is always signed and a local
|
||||
build never needs a key.
|
||||
|
||||
It resolves `SRC_DIR` and `BUILD_DIR` to absolute paths, then calls
|
||||
`stage_common()` to copy the `xrpld` and `validator-keys` binaries, config files,
|
||||
|
||||
65
package/sign_rpm.sh
Executable file
65
package/sign_rpm.sh
Executable file
@@ -0,0 +1,65 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Sign the RPMs built by build_pkg.sh. Nexus cannot sign hosted yum metadata, so
|
||||
# the packages carry the signature themselves and rpm clients verify them with
|
||||
# gpgcheck=1.
|
||||
#
|
||||
# Usage: sign_rpm.sh [package-dir]
|
||||
#
|
||||
# package-dir searched recursively for *.rpm ('build' by default)
|
||||
#
|
||||
# PKG_SIGNING_KEY must hold an armoured PGP private key. It has no flag, to keep
|
||||
# the key out of the process list.
|
||||
#
|
||||
# There is no DEB equivalent: apt trusts the repository metadata, which Nexus
|
||||
# signs, rather than the packages themselves.
|
||||
|
||||
pkg_dir="${1:-build}"
|
||||
|
||||
mapfile -d '' rpms < <(find "${pkg_dir}" -type f -name '*.rpm' -print0)
|
||||
|
||||
# Signing nothing would otherwise look like a successful signing.
|
||||
if [[ ${#rpms[@]} -eq 0 ]]; then
|
||||
echo "sign_rpm.sh: no RPMs found in ${pkg_dir}." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
: "${PKG_SIGNING_KEY:?is required}"
|
||||
|
||||
# Global, and expanded by the trap when it fires: the keyring holds an
|
||||
# unencrypted private key, so it must go even if signing fails.
|
||||
signing_home="$(mktemp -d)"
|
||||
trap 'rm -rf "${signing_home}"' EXIT
|
||||
export GNUPGHOME="${signing_home}"
|
||||
|
||||
printf '%s' "${PKG_SIGNING_KEY}" | gpg --batch --quiet --import
|
||||
|
||||
# Exactly one secret key, so that picking the first below is not a guess between
|
||||
# several.
|
||||
secrets="$(gpg --list-secret-keys --with-colons | grep -c '^sec:' || true)"
|
||||
if [[ "${secrets}" -ne 1 ]]; then
|
||||
echo "sign_rpm.sh: PKG_SIGNING_KEY must hold exactly one secret key, found ${secrets}." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
key="$(gpg --list-secret-keys --with-colons | awk -F: '/^fpr:/ { print $10; exit }')"
|
||||
echo "Signing ${#rpms[@]} RPM(s) with ${key}."
|
||||
|
||||
# Loopback pinentry: the key is unattended, so there is no tty to prompt on.
|
||||
rpmsign \
|
||||
--define "_gpg_name ${key}" \
|
||||
--define "_gpg_sign_cmd_extra_args --pinentry-mode loopback --batch --yes" \
|
||||
--addsign "${rpms[@]}"
|
||||
|
||||
# rpmsign can exit 0 having attached nothing, and an unsigned package is only
|
||||
# rejected later, on the installing machine. Both header tags are checked
|
||||
# because an RSA signature lands in RSAHEADER and a DSA or EdDSA one in
|
||||
# DSAHEADER.
|
||||
for pkg in "${rpms[@]}"; do
|
||||
signature="$(rpm --query --queryformat '%{RSAHEADER:pgpsig}%{DSAHEADER:pgpsig}' --package "${pkg}")"
|
||||
if [[ "${signature}" == "(none)(none)" ]]; then
|
||||
echo "sign_rpm.sh: ${pkg} is unsigned after rpmsign." >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
Reference in New Issue
Block a user