From 23a0d5b8b29a156467f929297584daed2f013c41 Mon Sep 17 00:00:00 2001 From: Dominic Cleal Date: Thu, 11 Feb 2016 12:10:13 +0000 Subject: [PATCH 1/2] Sign apt's Release file with multiple GPG keys The GPG config option and associated command arguments now accept multiple GPG keys, which are all used to sign the Release file. This makes it easier to roll keys, similar to the Debian archives which are signed with multiple release keys. --- bin/freight-cache | 17 +++++++++++++---- bin/freight-init | 17 ++++++++++++----- etc/freight.conf.example | 6 +++++- lib/freight/apt.sh | 25 +++++++++++++++---------- man/man1/freight-cache.1 | 4 ++-- man/man1/freight-cache.1.ronn | 2 +- man/man1/freight-init.1 | 4 ++-- man/man1/freight-init.1.ronn | 2 +- man/man5/freight.5 | 4 ++-- man/man5/freight.5.ronn | 2 +- 10 files changed, 54 insertions(+), 29 deletions(-) diff --git a/bin/freight-cache b/bin/freight-cache index c6e8fcc..6a6fd37 100755 --- a/bin/freight-cache +++ b/bin/freight-cache @@ -6,7 +6,7 @@ #/ Usage: freight cache [-k] [-g ] [-p ] [-c ] [-v] [-h] [/][...] #/ -k, --keep keep unreferenced versions of packages -#/ -g , --gpg= GPG key to use +#/ -g , --gpg= GPG key to use, may be given multiple times #/ -p , #/ --passphrase-file= path to file containing the passphrase of the GPG key #/ -c , --conf= config file to parse @@ -23,9 +23,18 @@ while [ "$#" -gt 0 ] do case "$1" in -k|--keep) KEEP=1 shift;; - -g|--gpg) GPG="$2" shift 2;; - -g*) GPG="$(echo "$1" | cut -c"3-")" shift;; - --gpg=*) GPG="$(echo "$1" | cut -c"7-")" shift;; + -g|--gpg) + [ -z "$GPG" ] && GPG=() + GPG+=("$2") + shift 2;; + -g*) + [ -z "$GPG" ] && GPG=() + GPG+=("$(echo "$1" | cut -c"3-")") + shift;; + --gpg=*) + [ -z "$GPG" ] && GPG=() + GPG+=("$(echo "$1" | cut -c"7-")") + shift;; -p|--passphrase-file) GPG_PASSPHRASE_FILE="$2" shift 2;; -p*) GPG_PASSPHRASE_FILE="$(echo "$1" | cut -c"3-")" shift;; --passphrase-file=*) GPG_PASSPHRASE_FILE="$(echo "$1" | cut -c"19-")" shift;; diff --git a/bin/freight-init b/bin/freight-init index a8671c5..597c477 100755 --- a/bin/freight-init +++ b/bin/freight-init @@ -17,6 +17,7 @@ set -e CONF="etc/freight.conf" +GPG=() usage() { grep "^#/" "$0" | cut -c"4-" >&2 @@ -25,9 +26,15 @@ usage() { while [ "$#" -gt 0 ] do case "$1" in - -g|--gpg) GPG="$2" shift 2;; - -g*) GPG="$(echo "$1" | cut -c"3-")" shift;; - --gpg=*) GPG="$(echo "$1" | cut -c"7-")" shift;; + -g|--gpg) + GPG+=("$2") + shift 2;; + -g*) + GPG+=("$(echo "$1" | cut -c"3-")") + shift;; + --gpg=*) + GPG+=("$(echo "$1" | cut -c"7-")") + shift;; -c|--conf) CONF="$2" shift 2;; -c*) CONF="$(echo "$1" | cut -c"3-")" shift;; --conf=*) CONF="$(echo "$1" | cut -c"8-")" shift;; @@ -50,7 +57,7 @@ do esac done DIRNAME="$(cd "${1:-"."}" && pwd)" -[ -z "$GPG" -o -z "$DIRNAME" ] && usage 1 +[ ${#GPG[@]} -eq 0 -o -z "$DIRNAME" ] && usage 1 # The default value for VARLIB and VARCACHE lies within DIRNAME but otherwise # follows the FHS style. @@ -67,7 +74,7 @@ mkdir -p "$(dirname "$CONF")" cat >"$CONF" <>"$CONF" [ "$ORIGIN" ] && echo "ORIGIN=\"$ORIGIN\"" >>"$CONF" diff --git a/etc/freight.conf.example b/etc/freight.conf.example index 8e8fa7c..446a16f 100644 --- a/etc/freight.conf.example +++ b/etc/freight.conf.example @@ -13,10 +13,14 @@ LABEL="Freight" # time (off). CACHE="off" -# GPG key to use to sign repositories. This is required by the `apt` +# GPG key(s) to use to sign repositories. This is required by the `apt` # repository provider. Use `gpg --gen-key` (see `gpg`(1) for more # details) to generate a key and put its email address here. +# +# Multiple addresses can be given in an array to sign the repository with +# them all. GPG="example@example.com" +# GPG=("example@example.com" "another@example.com") # Whether to follow symbolic links in `$VARLIB` to produce extra components # in the cache directory (on) or not (off). diff --git a/lib/freight/apt.sh b/lib/freight/apt.sh index ad6040c..2abde86 100644 --- a/lib/freight/apt.sh +++ b/lib/freight/apt.sh @@ -181,25 +181,30 @@ EOF } >"$DISTCACHE/Release" - # Sign the top-level `Release` file with `gpg`. - gpg -abs$([ "$TTY" ] || echo " --no-tty") --use-agent -u"$GPG" \ - $([ "$GPG_PASSPHRASE_FILE" ] && echo " --batch --passphrase-fd 1 --passphrase-file $GPG_PASSPHRASE_FILE") \ - -o"$DISTCACHE/Release.gpg" "$DISTCACHE/Release" || { - cat <> $DISTCACHE/Release.gpg + rm -f $TMP/release_last_signature.gpg + done # Generate `pubkey.gpg` containing the plaintext public key and # `keyring.gpg` containing a complete GPG keyring containing only - # the appropriate public key. `keyring.gpg` is appropriate for + # the appropriate public keys. `keyring.gpg` is appropriate for # copying directly to `/etc/apt/trusted.gpg.d`. mkdir -m700 -p "$TMP/gpg" - gpg -q --export -a "$GPG" | + gpg -q --export -a ${GPG[*]} | tee "$VARCACHE/pubkey.gpg" | gpg -q --homedir "$TMP/gpg" --import chmod 644 "$TMP/gpg/pubring.gpg" diff --git a/man/man1/freight-cache.1 b/man/man1/freight-cache.1 index 51c4713..412308b 100644 --- a/man/man1/freight-cache.1 +++ b/man/man1/freight-cache.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "FREIGHT\-CACHE" "1" "January 2014" "" "Freight" +.TH "FREIGHT\-CACHE" "1" "February 2016" "" "Freight" . .SH "NAME" \fBfreight\-cache\fR \- (re)builds package repositories @@ -29,7 +29,7 @@ Keep unreferenced versions of packages\. This is different than keeping multiple . .TP \fB\-g\fR \fIemail\fR, \fB\-\-gpg=\fR\fIemail\fR -Use an alternate GPG key\. +Use an alternate GPG key\. May be given multiple times\. . .TP \fB\-p\fR \fIpassphrase file\fR, \fB\-\-passphrase\-file=\fR\fIpassphrase file\fR diff --git a/man/man1/freight-cache.1.ronn b/man/man1/freight-cache.1.ronn index 99d3f8c..ec994d8 100644 --- a/man/man1/freight-cache.1.ronn +++ b/man/man1/freight-cache.1.ronn @@ -20,7 +20,7 @@ From version 0.0.8 onwards, distros in an APT repository no longer share the con * `-k`, `--keep`: Keep unreferenced versions of packages. This is different than keeping multiple versions of a package in the repository, which is supported without any special options. * `-g` _email_, `--gpg=`_email_: - Use an alternate GPG key. + Use an alternate GPG key. May be given multiple times. * `-p` _passphrase file_, `--passphrase-file=`_passphrase file_: Use an alternate file containing the GPG key passphrase. This file should obviously be protected and only readable by the user running Freight. * `-c` _conf_, `--conf=`_conf_: diff --git a/man/man1/freight-init.1 b/man/man1/freight-init.1 index 6a36afd..9dfe102 100644 --- a/man/man1/freight-init.1 +++ b/man/man1/freight-init.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "FREIGHT\-INIT" "1" "January 2014" "" "Freight" +.TH "FREIGHT\-INIT" "1" "February 2016" "" "Freight" . .SH "NAME" \fBfreight\-init\fR \- initialize a Freight directory @@ -22,7 +22,7 @@ Configuration is stored in \fB_dirname_/\.freight\.conf\fR\. . .TP \fB\-g\fR \fIgpg\fR, \fB\-\-gpg=\fR\fIgpg\fR` -GPG key\. +GPG key\. May be given multiple times\. . .TP \fB\-l\fR \fIvarlib\fR, \fB\-\-varlib=\fR\fIvarlib\fB_\fR\fR diff --git a/man/man1/freight-init.1.ronn b/man/man1/freight-init.1.ronn index c010f2a..210d0e4 100644 --- a/man/man1/freight-init.1.ronn +++ b/man/man1/freight-init.1.ronn @@ -16,7 +16,7 @@ Configuration is stored in `_dirname_/.freight.conf`. ## OPTIONS * `-g` _gpg_, `--gpg=`_gpg_`: - GPG key. + GPG key. May be given multiple times. * `-l` _varlib_, `--varlib=`_varlib`_: VARLIB directory to use. Defaults to `_dirname_/lib` * `--varcache=`_varcache`_: diff --git a/man/man5/freight.5 b/man/man5/freight.5 index 874db71..8e6f753 100644 --- a/man/man5/freight.5 +++ b/man/man5/freight.5 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "FREIGHT" "5" "January 2014" "" "Freight" +.TH "FREIGHT" "5" "February 2016" "" "Freight" . .SH "NAME" \fBfreight\fR \- Freight configuration @@ -37,7 +37,7 @@ The \fBLabel\fR field in the Debian archive\. . .TP \fBGPG\fR -The GPG key to use\. This value must be set either in a configuration file or by using the \fB\-g\fR option to \fBfreight\-cache\fR(1)\. +The GPG key(s) to use\. This value must be set either in a configuration file or by using the \fB\-g\fR option to \fBfreight\-cache\fR(1)\. Multiple keys can be given to sign the repository with more signatures\. . .TP \fBGPG_PASSPHRASE_FILE\fR diff --git a/man/man5/freight.5.ronn b/man/man5/freight.5.ronn index de75aa1..4a84c05 100644 --- a/man/man5/freight.5.ronn +++ b/man/man5/freight.5.ronn @@ -20,7 +20,7 @@ The Freight configuration is a `source`d shell script that defines a few importa * `CACHE`: _on_ to cache package control files or _off_ to read them from the packages on each `freight-cache`(1) run. * `GPG`: - The GPG key to use. This value must be set either in a configuration file or by using the `-g` option to `freight-cache`(1). + The GPG key(s) to use. This value must be set either in a configuration file or by using the `-g` option to `freight-cache`(1). Multiple keys can be given to sign the repository with more signatures. * `GPG_PASSPHRASE_FILE`: Pathname of a file containing the GPGP private key's passphrase. This sets the `--passphrase-fd` and `--passphrase-file` options to `gpg`(1). The passphrase file can be set either in a configuration file or by using the `-p` option to `freight-cache`(1). * `SYMLINKS`: From 1183f6d7a348aa9e5eb65bf026da5d4429e9034d Mon Sep 17 00:00:00 2001 From: Dominic Cleal Date: Sun, 6 Mar 2016 15:21:07 +0000 Subject: [PATCH 2/2] Replace bash-only GPG UID array with string Using a space-separated string of GPG UIDs is compatible with non-bash, POSIX compatible shells such as dash. --- bin/freight-cache | 21 +++++++++++++++------ bin/freight-init | 24 ++++++++++++++++++------ etc/freight.conf.example | 5 ++--- lib/freight/apt.sh | 4 ++-- 4 files changed, 37 insertions(+), 17 deletions(-) diff --git a/bin/freight-cache b/bin/freight-cache index 6a6fd37..b207917 100755 --- a/bin/freight-cache +++ b/bin/freight-cache @@ -24,16 +24,25 @@ do case "$1" in -k|--keep) KEEP=1 shift;; -g|--gpg) - [ -z "$GPG" ] && GPG=() - GPG+=("$2") + if [ -z "$GPG" ]; then + GPG=$2 + else + GPG="$GPG $2" + fi shift 2;; -g*) - [ -z "$GPG" ] && GPG=() - GPG+=("$(echo "$1" | cut -c"3-")") + if [ -z "$GPG" ]; then + GPG=$(echo "$1" | cut -c"3-") + else + GPG="$GPG $(echo "$1" | cut -c"3-")" + fi shift;; --gpg=*) - [ -z "$GPG" ] && GPG=() - GPG+=("$(echo "$1" | cut -c"7-")") + if [ -z "$GPG" ]; then + GPG=$(echo "$1" | cut -c"7-") + else + GPG="$GPG $(echo "$1" | cut -c"7-")" + fi shift;; -p|--passphrase-file) GPG_PASSPHRASE_FILE="$2" shift 2;; -p*) GPG_PASSPHRASE_FILE="$(echo "$1" | cut -c"3-")" shift;; diff --git a/bin/freight-init b/bin/freight-init index 597c477..0075410 100755 --- a/bin/freight-init +++ b/bin/freight-init @@ -17,7 +17,7 @@ set -e CONF="etc/freight.conf" -GPG=() +GPG="" usage() { grep "^#/" "$0" | cut -c"4-" >&2 @@ -27,13 +27,25 @@ while [ "$#" -gt 0 ] do case "$1" in -g|--gpg) - GPG+=("$2") + if [ -z "$GPG" ]; then + GPG=$2 + else + GPG="$GPG $2" + fi shift 2;; -g*) - GPG+=("$(echo "$1" | cut -c"3-")") + if [ -z "$GPG" ]; then + GPG=$(echo "$1" | cut -c"3-") + else + GPG="$GPG $(echo "$1" | cut -c"3-")" + fi shift;; --gpg=*) - GPG+=("$(echo "$1" | cut -c"7-")") + if [ -z "$GPG" ]; then + GPG=$(echo "$1" | cut -c"7-") + else + GPG="$GPG $(echo "$1" | cut -c"7-")" + fi shift;; -c|--conf) CONF="$2" shift 2;; -c*) CONF="$(echo "$1" | cut -c"3-")" shift;; @@ -57,7 +69,7 @@ do esac done DIRNAME="$(cd "${1:-"."}" && pwd)" -[ ${#GPG[@]} -eq 0 -o -z "$DIRNAME" ] && usage 1 +[ -z "$GPG" -o -z "$DIRNAME" ] && usage 1 # The default value for VARLIB and VARCACHE lies within DIRNAME but otherwise # follows the FHS style. @@ -74,7 +86,7 @@ mkdir -p "$(dirname "$CONF")" cat >"$CONF" <>"$CONF" [ "$ORIGIN" ] && echo "ORIGIN=\"$ORIGIN\"" >>"$CONF" diff --git a/etc/freight.conf.example b/etc/freight.conf.example index 446a16f..771336c 100644 --- a/etc/freight.conf.example +++ b/etc/freight.conf.example @@ -17,10 +17,9 @@ CACHE="off" # repository provider. Use `gpg --gen-key` (see `gpg`(1) for more # details) to generate a key and put its email address here. # -# Multiple addresses can be given in an array to sign the repository with -# them all. +# Multiple addresses can be given sign the repository with them all. GPG="example@example.com" -# GPG=("example@example.com" "another@example.com") +# GPG="example@example.com another@example.com" # Whether to follow symbolic links in `$VARLIB` to produce extra components # in the cache directory (on) or not (off). diff --git a/lib/freight/apt.sh b/lib/freight/apt.sh index 2abde86..3641b88 100644 --- a/lib/freight/apt.sh +++ b/lib/freight/apt.sh @@ -183,7 +183,7 @@ EOF # Sign the top-level `Release` file with `gpg`, for each key and # concatenate signatures. - for GPGKEY in ${GPG[@]}; do + for GPGKEY in $GPG; do gpg -abs$([ "$TTY" ] || echo " --no-tty") --use-agent -u"$GPGKEY" \ $([ "$GPG_PASSPHRASE_FILE" ] && echo " --batch --passphrase-fd 1 --passphrase-file $GPG_PASSPHRASE_FILE") \ -o"$TMP/release_last_signature.gpg" "$DISTCACHE/Release" || { @@ -204,7 +204,7 @@ EOF # the appropriate public keys. `keyring.gpg` is appropriate for # copying directly to `/etc/apt/trusted.gpg.d`. mkdir -m700 -p "$TMP/gpg" - gpg -q --export -a ${GPG[*]} | + gpg -q --export -a $GPG | tee "$VARCACHE/pubkey.gpg" | gpg -q --homedir "$TMP/gpg" --import chmod 644 "$TMP/gpg/pubring.gpg"