From 5e3d608761015b1df74e6b2e0a4907f834ee3367 Mon Sep 17 00:00:00 2001 From: ziggie Date: Thu, 12 Feb 2026 18:34:05 +0100 Subject: [PATCH 1/3] ci: add verify-release workflow Add a workflow that triggers when a release is published. It runs verify-install.sh inside the official Docker image to validate signatures and binary hashes. If verification fails, the release is automatically set back to draft. --- .github/workflows/verify-release.yaml | 36 +++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 .github/workflows/verify-release.yaml diff --git a/.github/workflows/verify-release.yaml b/.github/workflows/verify-release.yaml new file mode 100644 index 000000000..a9aaa267d --- /dev/null +++ b/.github/workflows/verify-release.yaml @@ -0,0 +1,36 @@ +name: Verify release + +on: + release: + types: [published] + workflow_dispatch: + inputs: + version: + description: 'Release version tag (e.g. v0.20.1-beta)' + required: true + +permissions: + contents: write + +jobs: + verify-release: + name: Verify release signatures and binaries + runs-on: ubuntu-latest + steps: + - name: Verify release + env: + VERSION: ${{ inputs.version || github.event.release.tag_name }} + run: | + docker run --rm --entrypoint="" \ + lightninglabs/lnd:${VERSION} \ + /verify-install.sh ${VERSION} + + - name: Set release back to draft on failure + if: failure() + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + VERSION: ${{ inputs.version || github.event.release.tag_name }} + run: | + gh release edit ${VERSION} \ + --repo ${{ github.repository }} \ + --draft From b7c4755934bf27ae96fba5ba549df91b3d3a392c Mon Sep 17 00:00:00 2001 From: ziggie Date: Thu, 12 Feb 2026 18:43:21 +0100 Subject: [PATCH 2/3] scripts: log failing signature details in verify-install When gpg --verify fails, include the signature filename, username, and full GPG output in the error message. Previously only a generic "Invalid signature!" was printed, making it hard to identify which signer's signature was invalid. --- scripts/verify-install.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/scripts/verify-install.sh b/scripts/verify-install.sh index a1f84eded..f1b747a71 100755 --- a/scripts/verify-install.sh +++ b/scripts/verify-install.sh @@ -159,7 +159,12 @@ function verify_signatures() { # Run the actual verification. gpg --homedir "$TEMP_DIR" --no-default-keyring --keyring "$KEYRING" --status-fd=1 \ --verify "$TEMP_DIR/$signature" "$TEMP_DIR/$MANIFEST" \ - > "$STATUS_FILE" 2>&1 || { echo "ERROR: Invalid signature!"; exit 1; } + > "$STATUS_FILE" 2>&1 || { + echo "ERROR: Invalid signature $signature from user $USERNAME!" + echo " GPG output:" + cat "$STATUS_FILE" | sed 's/^/ /' + exit 1 + } echo "Verifying $signature of user $USERNAME against key ring $KEYRING" if grep -q "Good signature" "$STATUS_FILE"; then From 99eceba7a5170d410320caf25901180c9f6c2d9c Mon Sep 17 00:00:00 2001 From: ziggie Date: Thu, 12 Feb 2026 23:19:41 +0100 Subject: [PATCH 3/3] scripts: switch verify-install.sh from curl to wget Replace curl with wget for downloading release manifests and signatures in verify-install.sh. wget handles redirects, retries, and error reporting more robustly by default, which avoids silent download failures that caused misleading "Invalid signature!" errors. Also add error checking to all download calls so failures are reported immediately with the URL that failed, and log which signature file and user failed gpg verification. --- Dockerfile | 4 ++-- scripts/verify-install.sh | 22 +++++++++++++++++----- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/Dockerfile b/Dockerfile index a152d9184..9cbe35463 100644 --- a/Dockerfile +++ b/Dockerfile @@ -29,13 +29,13 @@ FROM alpine as final VOLUME /root/.lnd # Add utilities for quality of life and SSL-related reasons. We also require -# curl and gpg for the signature verification script. +# wget and gpg for the signature verification script. RUN apk --no-cache add \ bash \ jq \ ca-certificates \ gnupg \ - curl + wget # Copy the binaries from the builder image. COPY --from=builder /go/bin/lncli /bin/ diff --git a/scripts/verify-install.sh b/scripts/verify-install.sh index f1b747a71..f1c5aac3d 100755 --- a/scripts/verify-install.sh +++ b/scripts/verify-install.sh @@ -95,14 +95,20 @@ function import_keys() { function verify_signatures() { # Download the JSON of the release itself. That'll contain the release ID we # need for the next call. - RELEASE_JSON=$(curl -L -s -H "$HEADER_JSON" "$RELEASE_URL/$VERSION") + RELEASE_JSON=$(wget -q --header="$HEADER_JSON" -O - "$RELEASE_URL/$VERSION") || { + echo "ERROR: Failed to download release JSON from $RELEASE_URL/$VERSION" + exit 1 + } TAG_NAME=$(echo $RELEASE_JSON | jq -r '.tag_name') RELEASE_ID=$(echo $RELEASE_JSON | jq -r '.id') echo "Release $TAG_NAME found with ID $RELEASE_ID" # Now download the asset list and filter by the manifest and the signatures. - ASSETS=$(curl -L -s -H "$HEADER_GH_JSON" "$API_URL/$RELEASE_ID" | jq -c '.assets[]') + ASSETS=$(wget -q --header="$HEADER_GH_JSON" -O - "$API_URL/$RELEASE_ID" | jq -c '.assets[]') || { + echo "ERROR: Failed to download asset list from $API_URL/$RELEASE_ID" + exit 1 + } MANIFEST=$(echo $ASSETS | jq -r "$MANIFEST_SELECTOR") SIGNATURES=$(echo $ASSETS | jq -r "$SIGNATURE_SELECTOR") @@ -116,11 +122,17 @@ function verify_signatures() { # Download the main "manifest-*.txt" and all "manifest-*.sig" files containing # the detached signatures. echo "Downloading $MANIFEST" - curl -L -s -o "$TEMP_DIR/$MANIFEST" "$RELEASE_URL/download/$VERSION/$MANIFEST" + wget -q -O "$TEMP_DIR/$MANIFEST" "$RELEASE_URL/download/$VERSION/$MANIFEST" || { + echo "ERROR: Failed to download $MANIFEST from $RELEASE_URL/download/$VERSION/$MANIFEST" + exit 1 + } for signature in $SIGNATURES; do echo "Downloading $signature" - curl -L -s -o "$TEMP_DIR/$signature" "$RELEASE_URL/download/$VERSION/$signature" + wget -q -O "$TEMP_DIR/$signature" "$RELEASE_URL/download/$VERSION/$signature" || { + echo "ERROR: Failed to download $signature from $RELEASE_URL/download/$VERSION/$signature" + exit 1 + } done echo "" @@ -267,7 +279,7 @@ shift verify_version "$VERSION" # Make sure we have all tools needed for the verification. -check_command curl +check_command wget check_command jq check_command gpg