#!/bin/bash # SPDX-License-Identifier: EPL-1.0 ############################################################################## # Copyright (c) 2017 The Linux Foundation and others. # # All rights reserved. This program and the accompanying materials # are made available under the terms of the Eclipse Public License v1.0 # which accompanies this distribution, and is available at # http://www.eclipse.org/legal/epl-v10.html ############################################################################## # Sign artifacts with gpg or sigul # Sigul signing requires the following: # # * sigul is installed # * configuration file for sigul with the full file path to the file as an # environment variable of $SIGUL_CONFIG # * the sigul user (key) as an environment variable of $SIGUL_KEY # * the sigul nss password, null terminated in a file referred to by # $SIGUL_PASSWORD # # If this is being run via JJB and sigul was installed via the scaffolding # macros then all environment variables should already be properly set GPG_BIN="gpg" if hash gpg2 2>/dev/null; then GPG_BIN="gpg2" fi sign() { # Entry point for the deploy command. subcommand=$1; shift case "$subcommand" in dir ) echo "Signing Directory..." sign_dir 'gpg' "$@" exit 0 ;; nexus ) echo "Signing Nexus repo..." sign_nexus "$@" exit 0 ;; sigul ) echo "Signing Directory with Sigul..." sign_dir 'sigul' "$@" exit 0 ;; git-tag ) echo "Signing Git tag with Sigul..." sign_git_tag "$@" exit 0 ;; container ) echo "Signing container with Sigul..." sign_container "$@" exit 0 ;; * ) echo "Invalid command: $subcommand" 1>&2 exit 1 ;; esac } sign_git_tag() { # Signs the specified git tag. # # Parameters: # # : The name of the git tag to be signed. local tag="$1" echo "Signing $tag" sigul --batch -c "$SIGUL_CONFIG" sign-git-tag \ "$SIGUL_KEY" "$tag" < "$SIGUL_PASSWORD" } sign_container() { # Signs the specified Docker container. # # Parameters: # # : Manifest of the container to be signed. # : The container's tag. local manifest="$1" local tag="$2" echo "Signing $manifest:$tag" sigul --batch -c "$SIGUL_CONFIG" sign-container -o "$manifest-$tag.asc" \ "$SIGUL_KEY" "$manifest" "$tag" < "$SIGUL_PASSWORD" } sign_dir() { # GPG signs all of the files in a directory # # Parameters: # # : gpg|sigul # : The directory to find and sign artifacts. # : serial|parallel local signer="$1" local dir="$2" if [ -z "$2" ]; then echo "Missing required arguments." echo "Usage: $0 sign-dir 'gpg|sigul' " exit 1 fi if [ "$signer" == "gpg" ]; then test_gpg_key fi set -e # Fail immediately if any if signing fails mapfile -t files_to_sign < <(find "$dir" -type f ! -name "*.asc" \ ! -name "*.md5" \ ! -name "*.sha1" \ ! -name "_maven.repositories" \ ! -name "_remote.repositories" \ ! -name "*.lastUpdated" \ ! -name "maven-metadata-local.xml" \ ! -name "maven-metadata.xml") if [ "${#files_to_sign[@]}" -eq 0 ]; then echo "ERROR: No files to sign. Quitting..." exit 1 fi local mode="serial" if hash parallel 2>/dev/null; then # Unless you specifically asked for serial mode if [ "$3" != "serial" ]; then local mode="parallel" fi fi if [ "$mode" == "parallel" ]; then echo "Signing in parallel..." case "$signer" in gpg ) # We need to wordsplit ${files_to_sign[*]} so disable quoting check # shellcheck disable=SC2086 parallel --no-notice --jobs 200% --halt now,fail=1 \ "$GPG_BIN --batch -abq {}" ::: ${files_to_sign[*]} ;; sigul ) # We need to wordsplit ${files_to_sign[*]} so disable quoting check # shellcheck disable=SC2086 parallel --no-notice --jobs 200% --retries 3 \ "sigul --batch -c $SIGUL_CONFIG sign-data -a -o {}.asc \ $SIGUL_KEY {} < $SIGUL_PASSWORD" ::: ${files_to_sign[*]} ;; * ) echo "Invalid signer: $signer" 1>&2 exit 1 ;; esac echo "Signed the following files:" printf '%s\n' "${files_to_sign[@]}" else echo "Signing in serial mode..." case "$signer" in gpg ) for f in "${files_to_sign[@]}"; do echo "Signing $f" "$GPG_BIN" --batch -abq "$f" done ;; sigul ) for f in "${files_to_sign[@]}"; do echo "Signing $f" sigul --batch -c "$SIGUL_CONFIG" sign-data -a -o "$f.asc" \ "$SIGUL_KEY" "$f" < "$SIGUL_PASSWORD" done ;; * ) echo "Invalid signer: $signer" 1>&2 exit 1 ;; esac fi set +e } sign_nexus_usage () { echo "Usage: $0 nexus " echo "" echo " nexus_repo_url: The URL to the Nexus repository to be signed." echo " Ex: https://nexus.opendaylight.org/content/repositories/autorelease-1888" echo "" echo "Options:" echo " -d /path/to/store/signatures" echo " -w gpg|sigul" } sign_nexus() { # Fetch and sign Nexus repo using GPG or sigul # # The resultant output of this command is stored in # /tmp/gpg-signatures.XXXXXXXXXX unless the -d parameter is passed to # override. local signer='gpg' while getopts d:m:w:h o; do case "$o" in h) sign_nexus_usage exit 0 ;; d) local sign_dir="$OPTARG" mkdir -p "$sign_dir" ;; m) local sign_mode="$OPTARG" ;; w) if [[ "$OPTARG" =~ ^(gpg|sigul)$ ]] then signer="$OPTARG" else sign_nexus_usage exit 1 fi ;; [?]) sign_nexus_usage exit 1 ;; esac done shift $((OPTIND-1)) local nexus_repo_url="$1" if [ -z "$1" ]; then echo "Missing required arguments." sign_nexus_usage exit 1 fi # Ensure that the repo_url has a trailing slash as wget needs it to work case "$nexus_repo_url" in */) ;; *) nexus_repo_url="$nexus_repo_url/" ;; esac sign_dir=${sign_dir:-$(mktemp -d /tmp/gpg-signatures.XXXXXXXXXX)} cd "$sign_dir" || exit 1 echo "Fetching artifacts from $nexus_repo_url to $sign_dir..." if ! wget -nv --recursive --execute robots=off --no-parent \ --no-host-directories --cut-dirs=3 --level=15 \ --continue --no-remove-listing \ "$nexus_repo_url"; then echo "ERROR: Failed to download artifacts." exit 1 fi echo "Removing files that do not need to be cloned..." mapfile -t remove_files < <(find . -type f -name "index.html" \ -o -name "*.asc" \ -o -name "*.md5" \ -o -name "*.sha1" \ -o -name "_maven.repositories*" \ -o -name "_remote.repositories*" \ -o -name "maven-metadata-local.xml*" \ -o -name "maven-metadata.xml*" \ -o -name "archetype-catalog.xml") for f in "${remove_files[@]}"; do rm "$f" done echo "Signing artifacts..." if [ "$signer" == "gpg" ] then sign_dir 'gpg' "$sign_dir" "$sign_mode" else sign_dir 'sigul' "$sign_dir" "$sign_mode" fi echo "Removing non-signature files..." mapfile -t remove_files < <(find . -type f -not -name '*.asc') for f in "${remove_files[@]}"; do rm "$f" done echo "Printing signatures generated" find . -type f echo "Signing complete. Signatures can be found in $sign_dir" } test_gpg_key() { # Test that our GPG key works by signing a test file. local test_file test_file=$(mktemp) echo "Test Signature" > "$test_file" "$GPG_BIN" --batch -abq "$test_file" if ! "$GPG_BIN" --verify "$test_file".asc "$test_file" > /dev/null 2>&1; then echo "ERROR: Failed to validate signature." rm "$test_file" "$test_file".asc # Cleanup before we exit exit 1 fi rm "$test_file" "$test_file".asc # Cleanup tmp files } # Only run the script if it is being called directly and not sourced. if [[ "${BASH_SOURCE[0]}" == "${0}" ]] then sign "$@" fi