Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

REL-553 - GPG Signing Example #12

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
188 changes: 188 additions & 0 deletions devops/examples/sign_checksum_upload.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
name: Build, sign, checksum artifacts
on:
workflow_dispatch:

jobs:
build:
outputs: # Update accordingly to your package types
version: ${{ steps.artifacts-list.outputs.version }}
rpm-version: ${{ steps.artifacts-list.outputs.rpm-version }}
artifacts: ${{ steps.artifacts-list.outputs.artifacts }}
rpm-artifacts: ${{ steps.artifacts-list.outputs.rpm-artifacts }}
deb-artifacts: ${{ steps.artifacts-list.outputs.deb-artifacts }}
zip-artifacts: ${{ steps.artifacts-list.outputs.zip-artifacts }}
pkg-artifacts: ${{ steps.artifacts-list.outputs.pkg-artifacts }}
sha-artifacts: ${{ steps.artifacts-list.outputs.sha-artifacts }}
asc-artifacts: ${{ steps.artifacts-list.outputs.asc-artifacts }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master

- name: Build rpm & deb # update this accrodingly to your *.rpm & *.deb generating steps
run: |
mkdir -p artifacts
cp packages/*.rpm packages/*.deb artifacts/

- name: Build pkg # update this accrodingly to your *.pkg generating steps
run: |
cp packages/*.pkg artifacts/

- name: Build zip # update this accrodingly to your *.zip generating steps
run: |
cp packages/*.zip artifacts/

- name: Upload Artifacts # upload artifacts so that they can be downloaded later to sign
uses: actions/upload-artifact@v4
with:
name: demo-artifacts # artifact name
path: ~/work/my-repo/my-repo/artifacts/* # path to artifacts: ~/work/<repo-name>/<repo-name>/.../<artifacts>

- name: Artifacts listing
id: artifacts-list
run: |
VER=$(cat VERSION.md)
echo version=${VER} >> $GITHUB_OUTPUT

# Sometime, the rpm version need to be changed
RPM_VER=$(echo ${VER} | sed 's/-/_/g')
echo rpm-verion=${RPM_VER} >> $GITHUB_OUTPUT

# Update as needed to artifacts that relevant to your product
ARTIFACTS="productName-linux-all-${VER}.deb productName-linux-noarch-${VER}.rpm productName-linux-amd64-${VER}.zip productName-linux-arm64-${VER}.zip productName-macos-${VER}.pkg productName-macos-amd64-${VER}.zip productName-macos-arm64-${VER}.zip productName-windows-amd64-${VER}.zip productName-windows-arm64-${VER}.zip"
echo "artifacts=${ARTIFACTS}" >> $GITHUB_OUTPUT
RPM_ARTIFACTS=$(echo "${ARTIFACTS}" | tr ' ' '\n' | grep '\.rpm$' | tr '\n' ' ')
echo "rpm-artifacts=${RPM_ARTIFACTS}" >> $GITHUB_OUTPUT

DEB_ARTIFACTS=$(echo "${ARTIFACTS}" | tr ' ' '\n' | grep '\.deb$' | tr '\n' ' ')
echo "deb-artifacts=${DEB_ARTIFACTS}" >> $GITHUB_OUTPUT

ZIP_ARTIFACTS=$(echo "${ARTIFACTS}" | tr ' ' '\n' | grep '\.zip$' | tr '\n' ' ')
echo "zip-artifacts=${ZIP_ARTIFACTS}" >> $GITHUB_OUTPUT

PKG_ARTIFACTS=$(echo "${ARTIFACTS}" | tr ' ' '\n' | grep '\.pkg$' | tr '\n' ' ')
echo "pkg-artifacts=${PKG_ARTIFACTS}" >> $GITHUB_OUTPUT

SHA256_FILES=$(for pkg in ${ARTIFACTS}; do echo "${pkg}.sha256"; done | tr '\n' ' ')
echo "sha-artifacts=${SHA256_FILES}" >> $GITHUB_OUTPUT

ASC_FILES=$(for pkg in ${ARTIFACTS} ${SHA256_FILES}; do
if [[ ! "${pkg}" =~ \.rpm$ && ! "${pkg}" =~ \.deb$ ]]; then
echo "${pkg}.asc"
fi
done | tr '\n' ' ')
echo "asc-artifacts=${ASC_FILES}" >> $GITHUB_OUTPUT

sign:
needs: build
runs-on: ubuntu-latest
env:
KEY_ID: 'aerospike-inc' # use "Aerospike" for original key, "aerospike-inc" for new key

steps:
- name: "Git checkout"
uses: actions/checkout@master
with:
fetch-depth: 0

- name: "Download Artifacts" # download built, unsigned artifacts to sign
uses: actions/download-artifact@v4
with:
name: demo-artifacts # your artifact name

- name: Set env # key id to use for signing
run: |
if [[ $KEY_ID == 'Aerospike' ]]; then
echo "EXT=_ORG" >> $GITHUB_ENV
elif [[ $KEY_ID == 'aerospike-inc' ]]; then
echo "EXT=" >> $GITHUB_ENV
else
echo -e "Invalid key name"
exit 1
fi
echo "KEY_ID=$KEY_ID" >> $GITHUB_ENV

- name: "Set dynamic key info"
run: |
echo "PRIVATE_KEY=GPG_SECRET_KEY${{ env.EXT }}" >> $GITHUB_ENV
echo "PUBLIC_KEY=GPG_PUBLIC_KEY${{ env.EXT }}" >> $GITHUB_ENV
echo "PASS_KEY=GPG_PASS${{ env.EXT }}" >> $GITHUB_ENV

- name: setup GPG
uses: aerospike/shared-workflows/devops/setup-gpg@main # update to the official action as needed
with:
gpg-private-key: ${{ secrets[env.PRIVATE_KEY] }}
gpg-public-key: ${{ secrets[env.PUBLIC_KEY] }}
gpg-key-pass: ${{ secrets[env.PASS_KEY] }}
gpg-key-name: ${{ env.KEY_ID }}

- name: List keys
run: gpg -K

- name: GPG Sign All Files
env:
GPG_TTY: no-tty
GPG_PASSPHRASE: ${{ secrets[env.PASS_KEY] }}
run: |
# GPG sign rpm packages
for file in ${{needs.build.outputs.rpm-artifacts}}; do
rpm --addsign "${file}" > /dev/null 2>&1
OUTPUT=$(rpm --checksig "${file}")
if [[ "$OUTPUT" =~ 'digests signatures OK' ]]; then
echo "Successfully GPG Signing $file"
else
echo "$OUTPUT"
echo "GPG Signing $file has failed."
exit 1
fi
done
# GPG sign deb packages
for file in ${{needs.build.outputs.deb-artifacts}}; do
dpkg-sig --sign builder "${file}" > /dev/null 2>&1
OUTPUT=$(dpkg-sig --verify "${file}")
if [[ "$OUTPUT" =~ 'GOODSIG _gpgbuilder' ]]; then
echo "Successfully GPG Signing $file"
else
echo "$OUTPUT"
echo "GPG Signing $file has failed."
exit 1
fi
done
# GPG sign other packages
for file in ${{needs.build.outputs.zip-artifacts}} ${{needs.build.outputs.pkg-artifacts}}; do
gpg --detach-sign --no-tty --batch --yes --output "${file}.asc" --passphrase "$GPG_PASSPHRASE" "${file}"
gpg --verify "${file}.asc" "${file}" &>/dev/null
RETURN_CODE=$?
if [ ! "$RETURN_CODE" = 0 ]; then
echo "GPG Signing $file has failed."
exit 1
else
echo "Successfully GPG Signing $file"
fi
done

- name: Create Checksums
run: |
# checksum 256 all packages
for pkg in ${{needs.build.outputs.artifacts}}; do
shasum -a 256 $pkg > ${pkg}.sha256
done
# GPG sign checksum files if required
for file in ${{needs.build.outputs.sha-artifacts}}; do
gpg --detach-sign --no-tty --batch --yes --output "${file}.asc" --passphrase "$GPG_PASSPHRASE" "${file}"
gpg --verify "${file}.asc" "${file}" &>/dev/null
RETURN_CODE=$?
if [ ! "$RETURN_CODE" = 0 ]; then
echo "GPG Signing $file has failed."
exit 1
else
echo "Successfully GPG Signing $file"
fi
done

- name: "Upload Artifacts"
uses: actions/upload-artifact@v4
with:
name: demo-artifacts
path: productName-*
overwrite: true

130 changes: 91 additions & 39 deletions devops/setup-gpg/action.yaml
Original file line number Diff line number Diff line change
@@ -1,89 +1,141 @@
name: 'Setup GPG'
description: 'Configures this action to run gpg with a given key and pass'
inputs:
gpg-private-key: # id of input
gpg-private-key:
description: 'GPG private key exported as an ASCII armored version or its base64 encoding'
required: true
gpg-key-pass: # id of input
gpg-key-pass:
description: 'Passphrase of the GPG private key'
required: true
gpg-key-name: # id of input
required: true
default: "Aerospike"
gpg-public-key:
description: 'GPG public key exported as an ASCII armored version or its base64 encoding'
required: true
gpg-key-name:
description: 'GPG key name'
required: true
default: 'aerospike-inc'
gpg-trust-level:
description: 'Set key trust level'
required: false
default: 5
runs:
using: "composite"
steps:
- name: "check if private key is not empty"
- name: "check if private key is empty"
env:
PRIVATE_KEY: ${{ inputs.gpg-private-key }}
if: ${{ env.PRIVATE_KEY == '' }}
GPG_PRIVATE_KEY: ${{ inputs.gpg-private-key }}
if: ${{ env.GPG_PRIVATE_KEY == '' }}
run: |
echo "the gpg-private-key was empty"
echo "The gpg-private-key was empty"
exit 1
shell: bash
- name: "check if key name is not empty"
env:
KEY_NAME: ${{ inputs.gpg-key-name }}
if: ${{ env.KEY_NAME == '' }}
- name: "check if public key is empty"
env:
GPG_PUBLIC_KEY: ${{ inputs.gpg-public-key }}
if: ${{ env.GPG_PUBLIC_KEY == '' }}
run: |
echo "the gpg-key-name was empty"
echo "The gpg-public-key was empty"
exit 1
shell: bash
- name: "check if key pass is not empty"
- name: "check if key name is empty"
env:
KEY_PASS: ${{ inputs.gpg-key-pass }}
if: ${{ env.KEY_PASS == '' }}
GPG_ID: ${{ inputs.gpg-key-name }}
if: ${{ env.GPG_ID == '' }}
run: |
echo "the secret gpg-key-pass was empty"
echo "The gpg-key-name was empty"
exit 1
shell: bash
- name: "check if public key pass is empty"
- name: "check if key pass is empty"
env:
PUBLIC_KEY: ${{ inputs.gpg-public-key }}
if: ${{ env.PUBLIC_KEY == '' }}
GPG_PASS: ${{ inputs.gpg-key-pass }}
if: ${{ env.GPG_PASS == '' }}
run: |
echo "the secret gpg-public-pass was empty"
echo "The secret gpg-key-pass was empty"
exit 1
shell: bash
- name: install tools
run: |
sudo apt-get update && sudo apt-get install ca-certificates gnupg dpkg-dev dpkg-sig rpm -y
sudo apt-get update && sudo apt-get install ca-certificates gnupg dpkg-dev rpm dpkg-sig expect -y
shell: bash
- name: Set up GPG
env:
GPG_PRIVATE_KEY: ${{ inputs.gpg-private-key }}
GPG_KEY_PASS: ${{ inputs.gpg-key-pass }}
GPG_PASS: ${{ inputs.gpg-key-pass }}
GPG_ID: ${{ inputs.gpg-key-name }}
GPG_PUBLIC_KEY: ${{ inputs.gpg-public-key }}
GPG_TRUST_LEVEL: ${{ inputs.gpg-trust-level }}
RPM_MACROS: |
%_signature gpg
%_gpg_path ~/.gnupg
%_gpg_name Aerospike
%__gpg_check_password_cmd /bin/true
%_gpgbin /usr/bin/gpg
%__gpg /usr/bin/gpg
%__gpg_sign_cmd %{__gpg} \
gpg \
--pinentry-mode loopback \
--batch \
--verbose \
--no-armor \
--no-secmem-warning \
--passphrase-file /home/runner/.gnupg/.pass \
--digest-algo sha256 \
-u "%{_gpg_name}" \
-sbo %{__signature_filename} %{__plaintext_filename}
SET_TRUST_LEVEL: |
#!/bin/bash

# Set the key ID and the desired trust level
KEY_ID=$1 # Pass the key ID or fingerprint as an argument
TRUST_LEVEL=$2 # Pass the trust level as an argument (1-5)

# Check if both arguments are provided
if [ -z "$KEY_ID" ] || [ -z "$TRUST_LEVEL" ]; then
echo "Usage: $0 <key-id> <trust-level>"
echo "Trust levels: 1 = I don't trust, 2 = I do NOT trust, 3 = I trust marginally, 4 = I trust fully, 5 = I trust ultimately"
exit 1
fi

# Check if the provided trust level is valid
if [[ "$TRUST_LEVEL" -lt 1 || "$TRUST_LEVEL" -gt 5 ]]; then
echo "Invalid trust level. Trust levels: 1-5"
exit 1
fi

# Use 'expect' to automate gpg trust level interaction
expect << EOF
spawn gpg --edit-key $KEY_ID
expect "gpg>"
send "trust\r"
expect "Your decision?"
send "$TRUST_LEVEL\r"
expect "Do you really want to set this key to ultimate trust? (y/N)"
send "y\r"
expect "gpg>"
send "save\r"
expect eof
EOF
run: |
# Setup gpg
mkdir -p ~/.gnupg
chmod 700 ~/.gnupg
echo "$GPG_PRIVATE_KEY" | gpg --import --batch --yes
echo "$GPG_KEY_PASS"
echo "$GPG_PASS"
echo -e "$SET_TRUST_LEVEL" > ~/set-gpg-trust.sh
chmod a+x ~/set-gpg-trust.sh
~/set-gpg-trust.sh "$GPG_ID" "$GPG_TRUST_LEVEL"

# configure for non-interactive use
export GPG_TTY=no-tty
echo -e "pinentry-mode loopback\nuse-agent" >> ~/.gnupg/gpg.conf
echo -e "allow-loopback-pinentry" >> ~/.gnupg/gpg-agent.conf

# configure rpm's
echo -e "$GPG_KEY_PASS" >> ~/pass
echo -e "%_signature gpg" >> ~/.rpmmacros
echo -e "%_gpg_path ~/.gnupg" >> ~/.rpmmacros
echo -e "%_gpg_name $GPG_ID" >> ~/.rpmmacros
echo -e "%_gpgbin /usr/bin/gpg" >> ~/.rpmmacros
echo -e "%__gpg /usr/bin/gpg" >> ~/.rpmmacros
echo -e "%__gpg_sign_cmd %{__gpg} \\" >> ~/.rpmmacros
echo -e "gpg --no-verbose --batch --no-tty --passphrase-file /home/runner/pass --pinentry-mode loopback \\" >> ~/.rpmmacros
echo -e " %{?_gpg_digest_algo:--digest-algo %{_gpg_digest_algo}} \\" >> ~/.rpmmacros
echo -e " --no-secmem-warning \\" >> ~/.rpmmacros
echo -e " -u '%{_gpg_name}' -sbo %{__signature_filename} %{__plaintext_filename}" >> ~/.rpmmacros

# public key for verification
echo -e "$GPG_PASS" >> ~/.gnupg/.pass
echo -e "$GPG_PUBLIC_KEY" >> ~/.gnupg/.public_key.asc
rpm --import ~/.gnupg/.public_key.asc
echo -e "$RPM_MACROS" > ~/.rpmmacros
#cp setup-gpg/.rpmmacros ~/.rpmmacros
rpm --import ~/.gnupg/.public_key.asc

# reload agent
gpg-connect-agent reloadagent /bye
Expand Down