ci: add scripts/sign-linux-tarball.sh for GPG signing

Adds `scripts/sign-linux-file.sh` which expectes the following variables set:

* `LINUX_GPG_PRIVATE_KEY_FILE` - Path to the GPG export of private key.
* `LINUX_GPG_PRIVATE_KEY_PASS` - Password necessary to use the private key.

Given a file it creates a file with a `.asc` suffix containing the signature:
```
 > wget -q https://status-im-prs.ams3.digitaloceanspaces.com/StatusIm-210809-104514-156806-pr.tar.gz

 > tar xvf StatusIm-210809-104514-156806-pr.tar.gz
StatusIm-210809-104514-156806-pr.AppImage
StatusIm-210809-104514-156806-pr.AppImage.asc

 > gpg --verify StatusIm-210809-104514-156806-pr.AppImage.asc
gpg: assuming signed data in 'StatusIm-210809-104514-156806-pr.AppImage'
gpg: Signature made Mon 09 Aug 2021 12:54:49 PM CEST using RSA key ID E20B4DFD
gpg: Good signature from "Status.im Devel Signing (GPG key for signing Status.im development builds.) <devel@status.im>" [ultimate]
Primary key fingerprint: BBF0 5F92 536B ED19 30A9  FD44 009F B3BF E20B 4DFD
```

Issue: https://github.com/status-im/infra-ci/issues/25
Requires: https://github.com/status-im/status-jenkins-lib/pull/32

Signed-off-by: Jakub Sokołowski <jakub@status.im>
This commit is contained in:
Jakub Sokołowski 2021-08-04 17:41:31 +02:00 committed by Iuri Matias
parent 50b6b59abf
commit 2df6def7f9
8 changed files with 103 additions and 21 deletions

View File

@ -248,12 +248,13 @@ $(APPIMAGE_TOOL):
echo -e "\e[92mFetching:\e[39m appimagetool" echo -e "\e[92mFetching:\e[39m appimagetool"
rm -rf tmp/linux rm -rf tmp/linux
mkdir -p tmp/linux/tools mkdir -p tmp/linux/tools
wget https://github.com/AppImage/AppImageKit/releases/download/continuous/$(_APPIMAGE_TOOL) wget -nv https://github.com/AppImage/AppImageKit/releases/download/continuous/$(_APPIMAGE_TOOL)
mv $(_APPIMAGE_TOOL) tmp/linux/tools/ mv $(_APPIMAGE_TOOL) tmp/linux/tools/
chmod +x $(APPIMAGE_TOOL) chmod +x $(APPIMAGE_TOOL)
STATUS_CLIENT_APPIMAGE ?= pkg/Status.AppImage STATUS_CLIENT_APPIMAGE ?= pkg/Status.AppImage
STATUS_CLIENT_TARBALL ?= pkg/Status.tar.gz STATUS_CLIENT_TARBALL ?= pkg/Status.tar.gz
STATUS_CLIENT_TARBALL_FULL ?= $(shell realpath $(STATUS_CLIENT_TARBALL))
$(STATUS_CLIENT_APPIMAGE): override RESOURCES_LAYOUT := -d:production $(STATUS_CLIENT_APPIMAGE): override RESOURCES_LAYOUT := -d:production
$(STATUS_CLIENT_APPIMAGE): nim_status_client $(APPIMAGE_TOOL) nim-status.desktop $(STATUS_CLIENT_APPIMAGE): nim_status_client $(APPIMAGE_TOOL) nim-status.desktop
@ -288,11 +289,18 @@ $(STATUS_CLIENT_APPIMAGE): nim_status_client $(APPIMAGE_TOOL) nim-status.desktop
mkdir -p pkg mkdir -p pkg
$(APPIMAGE_TOOL) tmp/linux/dist $(STATUS_CLIENT_APPIMAGE) $(APPIMAGE_TOOL) tmp/linux/dist $(STATUS_CLIENT_APPIMAGE)
# if LINUX_GPG_PRIVATE_KEY_FILE is not set then we don't generate a signature
ifdef LINUX_GPG_PRIVATE_KEY_FILE
scripts/sign-linux-file.sh $(STATUS_CLIENT_APPIMAGE)
endif
$(STATUS_CLIENT_TARBALL): $(STATUS_CLIENT_APPIMAGE) $(STATUS_CLIENT_TARBALL): $(STATUS_CLIENT_APPIMAGE)
tar czvf $(STATUS_CLIENT_TARBALL) \ cd $(shell dirname $(STATUS_CLIENT_APPIMAGE)) && \
-C $(shell dirname $(STATUS_CLIENT_APPIMAGE)) \ tar czvf $(STATUS_CLIENT_TARBALL_FULL) --ignore-failed-read \
$(shell basename $(STATUS_CLIENT_APPIMAGE)) $(shell basename $(STATUS_CLIENT_APPIMAGE)){,.asc}
ifdef LINUX_GPG_PRIVATE_KEY_FILE
scripts/sign-linux-file.sh $(STATUS_CLIENT_TARBALL)
endif
DMG_TOOL := node_modules/.bin/create-dmg DMG_TOOL := node_modules/.bin/create-dmg

View File

@ -14,8 +14,13 @@ RUN export DEBIAN_FRONTEND=noninteractive \
&& sudo apt install -yq software-properties-common \ && sudo apt install -yq software-properties-common \
&& sudo add-apt-repository -y ppa:git-core/ppa \ && sudo add-apt-repository -y ppa:git-core/ppa \
&& sudo apt update -yq \ && sudo apt update -yq \
&& sudo apt purge -yq gnupg \
&& sudo apt install -yq --fix-missing \ && sudo apt install -yq --fix-missing \
build-essential cmake git s3cmd libpcre3-dev libnss3 libxcomposite1 libxtst6 jq gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly gstreamer1.0-libav gstreamer1.0-tools gstreamer1.0-alsa gstreamer1.0-pulseaudio build-essential cmake jq git s3cmd gnupg2 \
libpcre3-dev libnss3 libxcomposite1 libxtst6 \
gstreamer1.0-plugins-good gstreamer1.0-plugins-bad \
gstreamer1.0-plugins-ugly gstreamer1.0-libav \
gstreamer1.0-tools gstreamer1.0-alsa gstreamer1.0-pulseaudio
# Installing Golang # Installing Golang
RUN GOLANG_SHA256="aed845e4185a0b2a3c3d5e1d0a35491702c55889192bb9c30e67a3de6849c067" \ RUN GOLANG_SHA256="aed845e4185a0b2a3c3d5e1d0a35491702c55889192bb9c30e67a3de6849c067" \
@ -40,5 +45,5 @@ USER jenkins
ENV HOME="/home/jenkins" ENV HOME="/home/jenkins"
LABEL maintainer="jakub@status.im" LABEL maintainer="jakub@status.im"
LABEL source="https://github.com/status-im/nim-status-client" LABEL source="https://github.com/status-im/status-desktop"
LABEL description="Build image for the Status Desktop client written in Nim." LABEL description="Build image for the Status Desktop client written in Nim."

View File

@ -1,4 +1,4 @@
library 'status-jenkins-lib@v1.2.20' library 'status-jenkins-lib@ci-linux-signing'
pipeline { pipeline {
agent { label 'linux' } agent { label 'linux' }

View File

@ -1,10 +1,10 @@
library 'status-jenkins-lib@v1.2.20' library 'status-jenkins-lib@ci-linux-signing'
pipeline { pipeline {
agent { agent {
docker { docker {
label 'linux' label 'linux'
image 'statusteam/nim-status-client-build:1.0.2' image 'statusteam/nim-status-client-build:1.0.3'
/* allows jenkins use cat and mounts '/dev/fuse' for linuxdeployqt */ /* allows jenkins use cat and mounts '/dev/fuse' for linuxdeployqt */
args '--entrypoint="" --cap-add SYS_ADMIN --security-opt apparmor:unconfined --device /dev/fuse' args '--entrypoint="" --cap-add SYS_ADMIN --security-opt apparmor:unconfined --device /dev/fuse'
} }
@ -64,14 +64,9 @@ pipeline {
} }
stage('Package') { stage('Package') {
steps { steps { script {
withCredentials([string( linux.bundle('tgz-linux')
credentialsId: utils.getInfuraTokenCred(), } }
variable: 'INFURA_TOKEN'
)]) {
sh 'make tgz-linux'
}
}
} }
stage('Parallel Upload') { stage('Parallel Upload') {
@ -84,7 +79,7 @@ pipeline {
} }
stage('Archive') { stage('Archive') {
steps { script { steps { script {
archiveArtifacts(env.STATUS_CLIENT_TARBALL) archiveArtifacts("${env.STATUS_CLIENT_TARBALL}*")
} } } }
} }
} }

View File

@ -1,4 +1,4 @@
library 'status-jenkins-lib@v1.2.20' library 'status-jenkins-lib@ci-linux-signing'
pipeline { pipeline {
agent { agent {

View File

@ -1,4 +1,4 @@
library 'status-jenkins-lib@v1.2.20' library 'status-jenkins-lib@ci-linux-signing'
pipeline { pipeline {
agent { label 'windows' } agent { label 'windows' }

72
scripts/sign-linux-file.sh Executable file
View File

@ -0,0 +1,72 @@
#!/usr/bin/env bash
set -eof pipefail
# Checks -----------------------------------------------------------------------
if [[ $(uname) != 'Linux' ]]; then
echo 'This only works on Linux.' >&2
exit 1
fi
if [[ $# -lt 1 ]]; then
echo 'sign-linux-tarball.sh <file_to_sign>' >&2
exit 1
fi
if [[ -z "${LINUX_GPG_PRIVATE_KEY_FILE}" ]]; then
echo "Unable to import GPG key file if LINUX_GPG_PRIVATE_KEY_FILE is not set!" >&2
exit 1
fi
if [[ -z "${LINUX_GPG_PRIVATE_KEY_PASS}" ]]; then
echo "Unable to import GPG key file if LINUX_GPG_PRIVATE_KEY_PASS is not set!" >&2
exit 1
fi
if [[ ! -f "${LINUX_GPG_PRIVATE_KEY_FILE}" ]]; then
echo "No such file exists: ${LINUX_GPG_PRIVATE_KEY_FILE}" >&2
exit 1
fi
# Signing ----------------------------------------------------------------------
function clean_up {
STATUS=$?
if [[ "${STATUS}" -ne 0 ]]; then
echo -e "\n###### ERROR: See above for details."
fi
set +e
echo -e "\n### Removing Temporary Keyring..."
rm -frv "${GNUPGHOME}"
exit $STATUS
}
# First and only argument is the file to create signature for
TARGET="${1}"
# Use a temporary GPG home and for the keyring.
export GNUPGHOME=$(mktemp -d $HOME/.gnupg.tmp.XXXXXX)
# Remove the GPG home along with the keyring regardless of how script exits.
trap clean_up EXIT
# Fix for 'gpg: signing failed: Inappropriate ioctl for device' in Docker
echo 'allow-loopback-pinentry' > "${GNUPGHOME}/gpg-agent.conf"
echo 'pinentry-mode loopback' > "${GNUPGHOME}/gpg.conf"
# Import the GPG key file into the temporary keyring.
echo -e "\n### Importing GPG private key..."
gpg2 --batch --yes --passphrase-fd 0 \
--import "${LINUX_GPG_PRIVATE_KEY_FILE}" \
<<< "${LINUX_GPG_PRIVATE_KEY_PASS}"
# Trust all immported keys ultimately.
gpg2 --list-secret-keys --with-colons \
| awk -F: '/fpr/{printf "%s:6:\n", $10}' \
| gpg2 --import-ownertrust --batch
echo -e "\n### Signing target..."
gpg2 --batch --yes --passphrase-fd 0 --verbose \
--armor --detach-sign "${TARGET}" \
<<< "${LINUX_GPG_PRIVATE_KEY_PASS}"
echo -e "\n### Verifying signature..."
gpg2 --batch --verify "${TARGET}.asc" "${TARGET}"
echo -e "\n### DONE"

View File

@ -17,7 +17,9 @@ CODESIGN_OPTS_EXTRA=("${@}")
function clean_up { function clean_up {
STATUS=$? STATUS=$?
[[ $? -eq 0 ]] || echo -e "\n###### ERROR: See above for details." if [[ "${STATUS}" -eq 0 ]]; then
echo -e "\n###### ERROR: See above for details."
fi
set +e set +e
echo -e "\n###### Cleaning up..." echo -e "\n###### Cleaning up..."