Skip to content

[ci] Add GPG signing for DEB packages#5145

Closed
PlatCore wants to merge 11 commits into
masterfrom
PlatCore/5109-add-deb-gpg-signing
Closed

[ci] Add GPG signing for DEB packages#5145
PlatCore wants to merge 11 commits into
masterfrom
PlatCore/5109-add-deb-gpg-signing

Conversation

@PlatCore
Copy link
Copy Markdown
Contributor

@PlatCore PlatCore commented Mar 29, 2026

Why this should be merged

DEB packages for avalanchego are currently built unsigned, while RPM packages already have full GPG signing via nfpm. Unsigned packages cannot be verified by users, leaving a gap in the supply chain security posture. This PR adds GPG signing to DEB packages using the RPM approach as a blueprint, and also adds the subnet-evm DEB package for parity.

How this works

Mirrors the existing RPM signing pipeline (build-rpm-release.yml + build-rpm.sh) for DEB packages:

  • nfpm-based packaging replaces the manual dpkg-deb --build process — uses nfpm configs for both avalanchego and subnet-evm
  • Post-build signing with dpkg-sig — nfpm builds the unsigned .deb, then dpkg-sig --sign signs it. This is necessary because nfpm's Go openpgp inline signatures are incompatible with dpkg-sig --verify
  • Unified workflow (build-deb-release.yml) with matrix strategy for amd64 + arm64, replacing the two separate unsigned workflows
  • GPG key handling: reuses secrets.RPM_GPG_PRIVATE_KEY for release builds; generates ephemeral keys for PR smoke tests
  • Validation: validate-deb.sh runs in a fresh ubuntu:22.04 container — imports public key, runs dpkg-sig --verify, installs packages, and smoke-tests --version output
  • Taskfile integration: test-build-debs task orchestrates build + validation end-to-end
  • envsubst preprocessing: nfpm doesn't expand ${VAR} in top-level config fields (changelog, signature), so build scripts preprocess configs with envsubst before calling nfpm

Key changes:

  • New: avalanchego-deb.yml / subnet-evm-deb.yml nfpm configs (install to /usr/local/bin and /usr/local/lib/avalanchego/plugins/, gzip compression for dpkg-sig compatibility)
  • New: build-deb.sh — builds binary, sets up GPG, packages with nfpm, signs with dpkg-sig
  • New: validate-deb.sh — verifies signature + installs + smoke-tests in fresh Ubuntu container
  • New: build-deb-release.yml — triggers on v* tags, workflow_dispatch, and PRs touching .github/packaging/**
  • Refactor: parameterized NFPM_CHANGELOG and NFPM_SIGNING_KEY paths in RPM nfpm configs via envsubst (non-breaking)
  • Refactor: added gettext to RPM builder Dockerfile for envsubst
  • Removed: old unsigned workflows (build-ubuntu-amd64-release.yml, build-ubuntu-arm64-release.yml, build-deb-pkg.sh, debian/template/control)

How this was tested

  • PR build: workflow triggers on PRs touching .github/packaging/** and runs the full test-build-debs pipeline with ephemeral GPG keys (signature verification + install + smoke test in a fresh Ubuntu container)
  • Tag/release build: workflow_dispatch with a test tag to verify real-key signing and S3 upload
  • RPM builds are unaffected — the nfpm config parameterization is a non-breaking refactor (export added to existing variables)

Need to be documented in RELEASES.md?

No

Replace the old unsigned dpkg-deb workflow with nfpm-based DEB packaging
that includes GPG signing, mirroring the existing RPM signing approach.
Adds subnet-evm DEB package for parity with RPM.
@PlatCore PlatCore requested a review from a team as a code owner March 29, 2026 06:36
nfpm does not expand ${VAR} in top-level fields like changelog and
signature.key_file. Preprocess configs with envsubst before passing
them to nfpm. Add gettext to RPM builder Dockerfile for envsubst.
nfpm defaults to debsign method which produces signatures
incompatible with dpkg-sig --verify. Set method explicitly
to dpkg-sig so signing and verification use the same format.
nfpm's Go openpgp inline signatures are incompatible with
dpkg-sig --verify. Remove nfpm deb.signature config and sign
post-build with dpkg-sig itself so the same tool signs and verifies.
dpkg-sig 0.13.1 (Ubuntu 22.04) predates zstd support in dpkg and
cannot verify signatures on zstd-compressed .deb archives, causing
BADSIG. Switch to gzip for dpkg-sig compatibility.
@PlatCore PlatCore self-assigned this Mar 29, 2026
@PlatCore PlatCore added devinfra ci This focuses on changes to the CI process labels Mar 29, 2026
@PlatCore PlatCore linked an issue Mar 29, 2026 that may be closed by this pull request
1 task
@PlatCore PlatCore requested a review from maru-ava March 29, 2026 07:29
Copy link
Copy Markdown
Contributor

@maru-ava maru-ava left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good start. My original expectation was that this would mainly add the signing already used for the RPM package builds, but it goes further by moving DEB packaging toward the same standard as RPM in both signing and testing.

That broader effort has also introduced a couple of concerns:

  • Noble-specific coverage appears to have been removed. The old flow had separate noble builds/tests, but the new flow publishes to noble paths without building or smoke-testing in a noble environment.
  • The new DEB build/test path is not locally reproducible in the same way as the RPM path, because it still depends on Linux host tooling and validation assumptions. A developer on macOS cannot exercise it locally in the same straightforward way. If the goal is to move DEB toward the RPM model, local reproducibility seems like a reasonable bar.

Would it make sense to split this into two parts? One option would be to first refactor the RPM workflows for reuse, with no intended functional change, and then build the DEB flow on top of that. That would reduce duplication, make the review easier, and help keep the RPM and DEB paths aligned over time.

Comment thread .github/workflows/build-deb-release.yml Outdated
build/deb/DEB-GPG-KEY-avalanchego

- name: Configure AWS credentials
if: github.event_name != 'pull_request' && startsWith(github.ref, 'refs/tags/')
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The github.event_name != 'pull_request' part makes sense, but gating this on startsWith(github.ref, 'refs/tags/') means the upload path will never run on workflow_dispatch, even when a tag is explicitly provided via github.event.inputs.tag. If manual dispatch is only intended to build and validate, that seems fine. But if the intent is for a manually supplied tag to exercise the release/upload path as well, this condition does not do that.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this condition does not do that.

Do you mean it prevents it or it doesn't capture / trigger on it?
I'm not sure that I clearly / fully understand the "manual" scenario when you'd like it to be triggered upon, mb point me to some docs?

env:
PACKAGING_TAG: ${{ env.TAG }}
DEB_GPG_KEY_FILE: ${{ env.GPG_KEY_FILE }}
NFPM_DEB_PASSPHRASE: ${{ secrets.RPM_GPG_PASSPHRASE }}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I may be missing it, but I don’t see the passphrase being used anywhere after NFPM_DEB_PASSPHRASE is passed into the job. If the release key is passphrase-protected, what ensures that key import and dpkg-sig signing work non-interactively in the release path? My concern is that this may work in PR/testing with an unprotected ephemeral key, while the real release key path remains unexercised.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My assumption was that the signing key/s are non-interactive. Good catch! Addressed in the commit 83b1975

"s3://${{ secrets.BUCKET }}/linux/debs/ubuntu/${release}/"
done

- name: Cleanup
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

build-deb.sh copies the private signing key into the workspace at build/gpg/signing-key.asc, but this cleanup step only removes the original temp key file and build/deb. Should build/gpg also be removed here? As written, the private key appears to remain in the workspace after the job completes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like the RPM w/f is lacking this kind of clean-up in the same way.

@maru-ava maru-ava moved this to In Progress 🏗️ in avalanchego Apr 1, 2026
@maru-ava maru-ava moved this from In Progress 🏗️ to Ready 🚦 in avalanchego Apr 1, 2026
PlatCore added 4 commits April 1, 2026 12:20
dpkg-sig delegates to gpg which needs the passphrase from gpg-agent.
Configure allow-preset-passphrase and use gpg-preset-passphrase to
cache the release key passphrase so signing works non-interactively.
Use gpgconf --list-dirs libexecdir for portable path resolution.
build-deb.sh and build-rpm.sh copy the private signing key to
build/gpg/signing-key.asc, but cleanup only removed the temp file
and package output dirs. Add build/gpg to both DEB and RPM cleanup.
The S3 upload was gated on startsWith(github.ref, 'refs/tags/')
which excluded workflow_dispatch (where github.ref is a branch ref).
Allow upload on tag push OR manual dispatch to match old behavior.
The RPM Docker container creates build/gpg/ as root via bind mount.
The host runner cannot remove root-owned files without sudo.
@PlatCore PlatCore moved this from Ready 🚦 to In Progress 🏗️ in avalanchego Apr 3, 2026
PlatCore added 2 commits April 3, 2026 11:38
The old workflows built and tested on both Ubuntu 22.04 and 24.04
separately. Restore noble coverage by running the validation smoke
test in both ubuntu:22.04 and ubuntu:24.04 containers to confirm
glibc forward compatibility of the jammy-built binary.
dpkg-sig was removed from Ubuntu 24.04 (noble) repositories.
Split validation into signature verification (jammy only, where
dpkg-sig exists) and install + smoke test (both jammy and noble).
The signature is embedded in the .deb and does not vary by release.
@PlatCore
Copy link
Copy Markdown
Contributor Author

PlatCore commented Apr 6, 2026

Request for refactoring into two PRs has been addressed, see

@PlatCore PlatCore closed this Apr 6, 2026
@github-project-automation github-project-automation Bot moved this from In Progress 🏗️ to Done 🎉 in avalanchego Apr 6, 2026
@maru-ava
Copy link
Copy Markdown
Contributor

maru-ava commented Apr 6, 2026

I thought I had made it clear that this PR should evolve to be one of the 2 parts asked for. That way the review context gets retained as part of what gets merged rather than orphaned as is now the case.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci This focuses on changes to the CI process devinfra

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

Update deb and rpm builds to use amazon kms for gpg signing

2 participants