diff --git a/features/gpg/README.md b/features/gpg/README.md new file mode 100644 index 00000000..33db17b1 --- /dev/null +++ b/features/gpg/README.md @@ -0,0 +1,12 @@ +# Feature: gpg + +Feature adds GnuPG (The Universal Crypto Engine) and public keys to the image to +verify image signatures. + +https://www.gnupg.org/software/index.html + +## Parameters + +- **GPG_PUBKEYS** -- List of files with public gpg keys. +- **GPG_PROG** -- The name of the gpg utility. This may be necessary if gpg is + gpg-1.x and not gpg-2.x or higher. diff --git a/features/gpg/config.mk b/features/gpg/config.mk new file mode 100644 index 00000000..99f4b7d7 --- /dev/null +++ b/features/gpg/config.mk @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-3.0-or-later + +GPG_PROG ?= gpg2 +GPG_PUBKEYS ?= diff --git a/features/gpg/rules.mk b/features/gpg/rules.mk new file mode 100644 index 00000000..41c6dca5 --- /dev/null +++ b/features/gpg/rules.mk @@ -0,0 +1,23 @@ +# SPDX-License-Identifier: GPL-3.0-or-later + +ifeq ($(GPG_PROG),) + $(error gpg utility must be specified in the "GPG_PROG" variable.) +endif + +ifeq ($(GPG_PUBKEYS),) + $(error one or more public gpg keys must be specified in the "GPG_PUBKEYS" variable.) +endif + +PUT_FEATURE_PROGS += $(GPG_PROG) + +PHONY += gpg + +gpg: create + @$(VMSG) "Putting gpg keyring ..." + @mkdir -m700 -p -- "$(ROOTDIR)/etc/initrd/gnupg" + @$(GPG_PROG) --quiet --homedir "$(ROOTDIR)/etc/initrd/gnupg" --import $(GPG_PUBKEYS) + @[ -e "$(ROOTDIR)"/bin/gpg ] || \ + ln -s -- "`type -P $(GPG_PROG)`" "$(ROOTDIR)"/bin/gpg + @rm -f -- "$(ROOTDIR)/etc/initrd/gnupg"/*~ + +pack: gpg diff --git a/features/pipeline/data/bin/pipeline-sh-functions b/features/pipeline/data/bin/pipeline-sh-functions index cf152a91..fe37d08a 100644 --- a/features/pipeline/data/bin/pipeline-sh-functions +++ b/features/pipeline/data/bin/pipeline-sh-functions @@ -131,4 +131,47 @@ pipe_failed() [ "$failed" -le "${PIPE_RETRY:-}" ] } +PIPE_RETCODE_STOP=2 +pipe_fatal() +{ + message "$*" + exit $PIPE_RETCODE_STOP +} + +in_comma_list() +{ + local var arg list + + var="$1"; shift + + list=() + readarray -t -d, list < <(printf '%s' "$1") + + for arg in "${list[@]}"; do + [ "$var" != "$arg" ] || return 0 + done + return 1 +} + +pipe_gpg_verify() +{ + local stepname signfile datafile gpg err + + stepname="$1"; shift + signfile="$1"; shift + datafile="$1"; shift + + in_comma_list "$stepname" "${PIPE_VERIFY_SIGN-}" || + return 0 + + gpg="$(type -P gpg)" || + pipe_fatal "gpg utility detected." + + [ -f "$signfile" ] || + pipe_fatal "unable to verify the signature because the signature file could not be found: $signfile" + + err="$("$gpg" --verify --homedir /etc/initrd/gnupg "$signfile" "$datafile")" || + pipe_fatal "$err" +} + fi # __pipeline_sh_functions diff --git a/features/pipeline/data/etc/initrd/cmdline.d/pipeline b/features/pipeline/data/etc/initrd/cmdline.d/pipeline index 86c7cbca..44e48ae7 100644 --- a/features/pipeline/data/etc/initrd/cmdline.d/pipeline +++ b/features/pipeline/data/etc/initrd/cmdline.d/pipeline @@ -1,5 +1,6 @@ register_parameter string PIPELINE register_parameter number PIPE_RETRY +register_parameter string PIPE_VERIFY_SIGN register_array string PING register_array string GETIMAGE register_array string MOUNTFS diff --git a/features/pipeline/data/lib/pipeline/getimage b/features/pipeline/data/lib/pipeline/getimage index 83745f08..2b6390fc 100755 --- a/features/pipeline/data/lib/pipeline/getimage +++ b/features/pipeline/data/lib/pipeline/getimage @@ -14,7 +14,11 @@ if [ -n "${url##file://*}" ]; then sleep 3 done else - cp -f -- "${url#file://}" "$datadir/image" + target="${url#file://}" + + pipe_gpg_verify "getimage" "$target.asc" "$target" + + cp -f -- "$target" "$datadir/image" fi modprobe -q 'devname:loop-control' ||: run mount -o ro,loop "$datadir/image" "$destdir" diff --git a/features/pipeline/data/lib/pipeline/mountfs b/features/pipeline/data/lib/pipeline/mountfs index 82597166..d59fe908 100755 --- a/features/pipeline/data/lib/pipeline/mountfs +++ b/features/pipeline/data/lib/pipeline/mountfs @@ -14,6 +14,8 @@ opts="$(get_parameter MOUNTFS_OPTS)" if [ ! -c "$target" ] && [ ! -b "$target" ]; then modprobe -q 'devname:loop-control' ||: opts="${opts:+$opts,}ro,loop" + + pipe_gpg_verify "mountfs" "$target.asc" "$target" fi run mount ${opts:+-o $opts} "$target" "$destdir" diff --git a/features/pipeline/data/lib/uevent/handlers/pipeline/500-pipeline b/features/pipeline/data/lib/uevent/handlers/pipeline/500-pipeline index 18fe11ec..f04ec435 100755 --- a/features/pipeline/data/lib/uevent/handlers/pipeline/500-pipeline +++ b/features/pipeline/data/lib/uevent/handlers/pipeline/500-pipeline @@ -73,7 +73,7 @@ handler_step() "$exe" || rc=$? if [ "$rc" -ne 0 ]; then - if [ "$rc" -eq 2 ]; then + if [ "$rc" -eq "$PIPE_RETCODE_STOP" ]; then message "step #$pipenum: $name: triggered pipeline stop." return 0 fi