#!/bin/bash # This bash script maintains /etc/portage/gnupg in a Gentoo installation. # It should be run as root always before downloading and installing binary # packages. # If the directory does not exist yet, it will be set up such that # the Gentoo Release Engineering keys are trusted for signing binary # packages. # The keys are taken from sec-keys/openpgp-keys-gentoo-release. # If the directory already exists, then all keys in it will be refreshed # from keyservers. # In addition sanity checks and corrections are performed ... # Runtime requirements: # app-crypt/gnupg # dev-libs/openssl # sec-keys/openpgp-keys-gentoo-release # sys-apps/gentoo-functions [[ ${PORTAGE_DEBUG} || ${GETUTO_DEBUG} ]] && set -x [[ $(whoami) == 'root' ]] || { echo "${0} must be run as root!" ; exit 100 ; } export GNUPGHOME="${ROOT%/}"/etc/portage/gnupg LASTRUNFILE=${GNUPGHOME}/.getuto.last QUIET='1' QUIET_GPG='' [[ $1 == '-v' ]] && QUIET='' # If QUIET is enabled, pass '--quiet' and suppress GPG homedir permission warnings # These permission warnings cannot be suppressed using gpg.conf [[ -n ${QUIET} ]] && QUIET_GPG='--quiet --no-permission-warning' if [[ -f /lib/gentoo/functions.sh && -v TERM && -n "${TERM}" && "${TERM}" != dumb ]] ; then source /lib/gentoo/functions.sh else ebegin() { echo "$@" } eend() { true } einfo() { echo "$@" } fi gpgconf --kill all set -e mykeyservers=( "hkps://keys.openpgp.org" "hkps://keys.gentoo.org" ) getuto_refresh() { DAY=86400 NOW=$(date +%s) if [[ -f ${LASTRUNFILE} ]] ; then LST=$(date -r ${LASTRUNFILE} +%s) else LST='0' fi if (( ${NOW} - ${DAY} >= ${LST} )) ; then einfo "Updating gnupg keyring for package signatures" >&3 # Always re-import the system keys because it might be our only source of updates # for e.g. revocations, renewals, etc if we're on a firewalled machine. gpg ${QUIET_GPG} --batch --import "${ROOT%/}"/usr/share/openpgp-keys/gentoo-release.asc # Refresh all keys from the keyserver if we can. for keyserver in "${mykeyservers[@]}" ; do # TODO: keys.openpgp.org lacks a UID for our keys, need to verify email gpg ${QUIET_GPG} --batch --keyserver "${keyserver}" --refresh-keys || true done # We only sign (-> ultimate trust) the keys we originally import, so this is fine and # just serves as an additional refresh method. gpg ${QUIET_GPG} --auto-key-locate=clear,nodefault,wkd --locate-key releng@gentoo.org infrastructure@gentoo.org repomirrorci@gentoo.org || true touch ${LASTRUNFILE} else [[ -n ${QUIET} ]] || einfo "gnupg keyring for package signatures already up-to-date." >&3 fi } if [[ ! -d ${GNUPGHOME} ]] ; then # The directory does not exist yet. ebegin "Initializing ${GNUPGHOME}" mkdir -p "${GNUPGHOME}" chmod u=rwx,go=rx "${GNUPGHOME}" cat > "${GNUPGHOME}"/dirmngr.conf <<-EOF honor-http-proxy no-use-tor resolver-timeout 180 connect-timeout 180 EOF cat > "${GNUPGHOME}"/gpg-agent.conf <<-EOF disable-scdaemon EOF cat > "${GNUPGHOME}"/gpg.conf <<-EOF no-greeting EOF # Generate a local ultimate trust anchor key. PASS="$(openssl rand -base64 32)" KEY_CONFIG_FILE="$(mktemp)" chmod 600 "${KEY_CONFIG_FILE}" cat > "${KEY_CONFIG_FILE}" <<-EOF %echo Generating Portage local OpenPGP trust key Key-Type: RSA Key-Length: 3072 Subkey-Type: RSA Subkey-Length: 3072 Name-Real: Portage Local Trust Key Name-Comment: local signing only Name-Email: portage@localhost Expire-Date: 0 Passphrase: ${PASS} %commit %echo done EOF gpg ${QUIET_GPG} --batch --generate-key "${KEY_CONFIG_FILE}" rm -f "${KEY_CONFIG_FILE}" touch "${GNUPGHOME}/pass" chmod 600 "${GNUPGHOME}/pass" echo "${PASS}" > "${GNUPGHOME}/pass" gpg ${QUIET_GPG} --batch --list-secret-keys --keyid-format=long --with-colons \ | grep "^fpr" \ | sed -n 's/^fpr:::::::::\([[:alnum:]]\+\):/\1/p' \ > "${GNUPGHOME}/mykeyid" mykeyid=$(<"${GNUPGHOME}/mykeyid") # Import all release engineering keys. if [[ ! -f "${ROOT%/}"/usr/share/openpgp-keys/gentoo-release.asc ]] ; then echo "\"${ROOT%/}\"/usr/share/openpgp-keys/gentoo-release.asc not found. Is sec-keys/openpgp-keys-gentoo-release installed?" exit 1 fi gpg ${QUIET_GPG} --batch --import "${ROOT%/}"/usr/share/openpgp-keys/gentoo-release.asc # List all release engineering keys (https://serverfault.com/a/946428) myrelkeys=$(gpg ${QUIET_GPG} --batch --list-keys --keyid-format=long --with-colons \ | grep "^fpr" \ | sed -n 's/^fpr:::::::::\([[:alnum:]]\+\):/\1/p' \ | grep -v "${mykeyid}") # TODO: keys.openpgp.org lacks a UID for our keys, need to verify email for keyserver in "${mykeyservers[@]}" ; do gpg ${QUIET_GPG} --batch --keyserver "${keyserver}" --recv-keys ${myrelkeys} || true done # We only sign (-> ultimate trust) the keys we originally import, so this is fine and # just serves as an additional refresh method. gpg ${QUIET_GPG} --auto-key-locate=clear,nodefault,wkd --locate-key releng@gentoo.org infrastructure@gentoo.org repomirrorci@gentoo.org || true # Locally sign all release engineering keys. for relkeyid in ${myrelkeys} ; do # We have to use --quick-lsign-key for this to work with batch: https://dev.gnupg.org/T1606 if ! gpg ${QUIET_GPG} --batch --yes --no-tty --passphrase-file="${GNUPGHOME}/pass" --pinentry-mode loopback --quick-lsign-key "${relkeyid}" ; then # But that won't work for subkeys, so fallback to a hackier method. set -o pipefail echo -e "y\ny\n" | gpg ${QUIET_GPG} --command-fd 0 --yes --no-tty --passphrase-file="${GNUPGHOME}/pass" --pinentry-mode loopback --lsign-key "${relkeyid}" set +o pipefail fi done # Update the trustdb gpg ${QUIET_GPG} --batch --check-trustdb # Make sure the trustdb is world-readable. chmod ugo+r "${GNUPGHOME}/trustdb.gpg" touch ${LASTRUNFILE} eend else # The keydir already exists, so our job is to just to refresh and check # permissions. # We want to be able to filter error messages export LC_ALL=C.UTF8 { if [[ -n ${QUIET} ]] ; then getuto_refresh >/dev/null 2>&1 else getuto_refresh fi } 3>&1 fi # Make sure the trustdb is world-readable (again). chmod ugo+r "${GNUPGHOME}/trustdb.gpg" # Clean up. gpgconf --kill all