Skip to content

Commit dc0919d

Browse files
feat: sign coder binaries with the release key using GPG (#18774)
### Description This PR introduces GPG signing for all Coder *slim-binaries*. Detached signatures will allow users to verify the integrity and authenticity of the binaries they download. ### Changes * `scripts/sign_with_gpg.sh`: New script to sign a given binary using GPG. It imports the release key, signs the binary, and verifies the signature. * `scripts/build_go.sh`: Updated to call `sign_with_gpg.sh` when the `CODER_SIGN_GPG` environment variable is set to 1. * `.github/workflows/release.yaml`: The` CODER_SIGN_GPG` environment variable is now set to 1 during the release build, enabling GPG signing for all release binaries. * `.github/workflows/ci.yaml`: The `CODER_SIGN_GPG` environment variable is now set to 1 during the CI build, enabling GPG signing for all CI binaries. * `Makefile`: Detached signatures are moved to the `/site/out/bin/ `directory
1 parent 3c2f3d6 commit dc0919d

File tree

6 files changed

+82
-19
lines changed

6 files changed

+82
-19
lines changed

.github/workflows/ci.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1137,6 +1137,8 @@ jobs:
11371137
# do (see above).
11381138
CODER_SIGN_WINDOWS: "1"
11391139
CODER_WINDOWS_RESOURCES: "1"
1140+
CODER_SIGN_GPG: "1"
1141+
CODER_GPG_RELEASE_KEY_BASE64: ${{ secrets.GPG_RELEASE_KEY_BASE64 }}
11401142
EV_KEY: ${{ secrets.EV_KEY }}
11411143
EV_KEYSTORE: ${{ secrets.EV_KEYSTORE }}
11421144
EV_TSA_URL: ${{ secrets.EV_TSA_URL }}

.github/workflows/release.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,8 @@ jobs:
323323
env:
324324
CODER_SIGN_WINDOWS: "1"
325325
CODER_SIGN_DARWIN: "1"
326+
CODER_SIGN_GPG: "1"
327+
CODER_GPG_RELEASE_KEY_BASE64: ${{ secrets.GPG_RELEASE_KEY_BASE64 }}
326328
CODER_WINDOWS_RESOURCES: "1"
327329
AC_CERTIFICATE_FILE: /tmp/apple_cert.p12
328330
AC_CERTIFICATE_PASSWORD_FILE: /tmp/apple_cert_password.txt

Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,10 @@ $(CODER_ALL_BINARIES): go.mod go.sum \
252252
fi
253253

254254
cp "$@" "./site/out/bin/coder-$$os-$$arch$$dot_ext"
255+
256+
if [[ "$${CODER_SIGN_GPG:-0}" == "1" ]]; then
257+
cp "$@.asc" "./site/out/bin/coder-$$os-$$arch$$dot_ext.asc"
258+
fi
255259
fi
256260

257261
# This task builds Coder Desktop dylibs

scripts/build_go.sh

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@
2020
# binary will be signed using ./sign_darwin.sh. Read that file for more details
2121
# on the requirements.
2222
#
23+
# If the --sign-gpg parameter is specified, the output binary will be signed using ./sign_with_gpg.sh.
24+
# Read that file for more details on the requirements.
25+
#
2326
# If the --agpl parameter is specified, builds only the AGPL-licensed code (no
2427
# Coder enterprise features).
2528
#
@@ -41,6 +44,7 @@ slim="${CODER_SLIM_BUILD:-0}"
4144
agpl="${CODER_BUILD_AGPL:-0}"
4245
sign_darwin="${CODER_SIGN_DARWIN:-0}"
4346
sign_windows="${CODER_SIGN_WINDOWS:-0}"
47+
sign_gpg="${CODER_SIGN_GPG:-0}"
4448
boringcrypto=${CODER_BUILD_BORINGCRYPTO:-0}
4549
dylib=0
4650
windows_resources="${CODER_WINDOWS_RESOURCES:-0}"
@@ -85,6 +89,10 @@ while true; do
8589
sign_windows=1
8690
shift
8791
;;
92+
--sign-gpg)
93+
sign_gpg=1
94+
shift
95+
;;
8896
--boringcrypto)
8997
boringcrypto=1
9098
shift
@@ -319,4 +327,9 @@ if [[ "$sign_windows" == 1 ]] && [[ "$os" == "windows" ]]; then
319327
execrelative ./sign_windows.sh "$output_path" 1>&2
320328
fi
321329

330+
# Platform agnostic signing
331+
if [[ "$sign_gpg" == 1 ]]; then
332+
execrelative ./sign_with_gpg.sh "$output_path" 1>&2
333+
fi
334+
322335
echo "$output_path"

scripts/release/publish.sh

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -129,26 +129,9 @@ if [[ "$dry_run" == 0 ]] && [[ "${CODER_GPG_RELEASE_KEY_BASE64:-}" != "" ]]; the
129129
log "--- Signing checksums file"
130130
log
131131

132-
# Import the GPG key.
133-
old_gnupg_home="${GNUPGHOME:-}"
134-
gnupg_home_temp="$(mktemp -d)"
135-
export GNUPGHOME="$gnupg_home_temp"
136-
echo "$CODER_GPG_RELEASE_KEY_BASE64" | base64 -d | gpg --import 1>&2
137-
138-
# Sign the checksums file. This generates a file in the same directory and
139-
# with the same name as the checksums file but ending in ".asc".
140-
#
141-
# We pipe `true` into `gpg` so that it never tries to be interactive (i.e.
142-
# ask for a passphrase). The key we import above is not password protected.
143-
true | gpg --detach-sign --armor "${temp_dir}/${checksum_file}" 1>&2
144-
145-
rm -rf "$gnupg_home_temp"
146-
unset GNUPGHOME
147-
if [[ "$old_gnupg_home" != "" ]]; then
148-
export GNUPGHOME="$old_gnupg_home"
149-
fi
150-
132+
execrelative ../sign_with_gpg.sh "${temp_dir}/${checksum_file}"
151133
signed_checksum_path="${temp_dir}/${checksum_file}.asc"
134+
152135
if [[ ! -e "$signed_checksum_path" ]]; then
153136
log "Signed checksum file not found: ${signed_checksum_path}"
154137
log

scripts/sign_with_gpg.sh

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
#!/usr/bin/env bash
2+
3+
# This script signs a given binary using GPG.
4+
# It expects the binary to be signed as the first argument.
5+
#
6+
# Usage: ./sign_with_gpg.sh path/to/binary
7+
#
8+
# On success, the input file will be signed using the GPG key and the signature output file will moved to /site/out/bin/ (happens in the Makefile)
9+
#
10+
# Depends on the GPG utility. Requires the following environment variables to be set:
11+
# - $CODER_GPG_RELEASE_KEY_BASE64: The base64 encoded private key to use.
12+
13+
set -euo pipefail
14+
# shellcheck source=scripts/lib.sh
15+
source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
16+
17+
requiredenvs CODER_GPG_RELEASE_KEY_BASE64
18+
19+
FILE_TO_SIGN="$1"
20+
21+
if [[ -z "$FILE_TO_SIGN" ]]; then
22+
error "Usage: $0 <file_to_sign>"
23+
fi
24+
25+
if [[ ! -f "$FILE_TO_SIGN" ]]; then
26+
error "File not found: $FILE_TO_SIGN"
27+
fi
28+
29+
# Import the GPG key.
30+
old_gnupg_home="${GNUPGHOME:-}"
31+
gnupg_home_temp="$(mktemp -d)"
32+
export GNUPGHOME="$gnupg_home_temp"
33+
34+
# Ensure GPG uses the temporary directory
35+
echo "$CODER_GPG_RELEASE_KEY_BASE64" | base64 -d | gpg --homedir "$gnupg_home_temp" --import 1>&2
36+
37+
# Sign the binary. This generates a file in the same directory and
38+
# with the same name as the binary but ending in ".asc".
39+
#
40+
# We pipe `true` into `gpg` so that it never tries to be interactive (i.e.
41+
# ask for a passphrase). The key we import above is not password protected.
42+
true | gpg --homedir "$gnupg_home_temp" --detach-sign --armor "$FILE_TO_SIGN" 1>&2
43+
44+
# Verify the signature and capture the exit status
45+
gpg --homedir "$gnupg_home_temp" --verify "${FILE_TO_SIGN}.asc" "$FILE_TO_SIGN" 1>&2
46+
verification_result=$?
47+
48+
# Clean up the temporary GPG home
49+
rm -rf "$gnupg_home_temp"
50+
unset GNUPGHOME
51+
if [[ "$old_gnupg_home" != "" ]]; then
52+
export GNUPGHOME="$old_gnupg_home"
53+
fi
54+
55+
if [[ $verification_result -eq 0 ]]; then
56+
echo "${FILE_TO_SIGN}.asc"
57+
else
58+
error "Signature verification failed!"
59+
fi

0 commit comments

Comments
 (0)