From 7879a0591b0a9509ddc85da4755ead16017d6769 Mon Sep 17 00:00:00 2001 From: Alexey Gladkov Date: Sun, 12 Jan 2025 16:41:33 +0100 Subject: [PATCH] New feature to verify signature New feature adds the ability to add gpg utility and public keys to the image and the ability to verify file signatures at certain pipeline steps. Signed-off-by: Alexey Gladkov --- features/gpg/README.md | 12 ++++++ features/gpg/config.mk | 4 ++ features/gpg/rules.mk | 23 ++++++++++ .../pipeline/data/bin/pipeline-sh-functions | 43 +++++++++++++++++++ .../data/etc/initrd/cmdline.d/pipeline | 1 + features/pipeline/data/lib/pipeline/getimage | 6 ++- features/pipeline/data/lib/pipeline/mountfs | 2 + .../lib/uevent/handlers/pipeline/500-pipeline | 2 +- 8 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 features/gpg/README.md create mode 100644 features/gpg/config.mk create mode 100644 features/gpg/rules.mk 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