* [gentoo-dev] [PATCH 1/2] sec-keys.eclass: new eclass
@ 2024-11-27 20:30 Eli Schwartz
2024-11-27 20:30 ` [gentoo-dev] [PATCH 2/2] sec-keys/openpgp-keys-gnutls: update to use sec-keys.eclass Eli Schwartz
` (4 more replies)
0 siblings, 5 replies; 21+ messages in thread
From: Eli Schwartz @ 2024-11-27 20:30 UTC (permalink / raw
To: gentoo-dev
The current state of verify-sig support is a bit awkward. We rely on
validating distfiles against a known trusted keyring, but creating the
known trusted keyring is basically all manual verification. We somehow
decide an ascii armored key is good enough without any portage
assistance, then arrange to download it and trust it by Manifest hash.
How do we know when updating a key is actually safe?
This eclass handles the problem in a manner inspired in part by pacman.
We require an eclass variable that lists all permitted PGP fingerprints,
and the eclass is responsible checking that list against the keys we
will install. It comes with a mechanism for computing SRC_URI for a
couple of well known locations, or you can append your own in the
ebuild.
Key rotations, both expected and malicious, are easily detected by
checking the git log for changes to declared finterprints in a bump. The
former can be rationalized in the commit message. So can the latter, but
in most cases those will be rejected during peer review.
Signed-off-by: Eli Schwartz <eschwartz@gentoo.org>
---
eclass/sec-keys.eclass | 150 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 150 insertions(+)
create mode 100644 eclass/sec-keys.eclass
diff --git a/eclass/sec-keys.eclass b/eclass/sec-keys.eclass
new file mode 100644
index 000000000000..95e1b4a92c0e
--- /dev/null
+++ b/eclass/sec-keys.eclass
@@ -0,0 +1,150 @@
+# Copyright 2024 Gentoo Authors
+# Distributed under the terms of the GNU General Public License v2
+
+# @ECLASS: sec-keys.eclass
+# @MAINTAINER:
+# Eli Schwartz <eschwartz@gentoo.org>
+# @AUTHOR:
+# Eli Schwartz <eschwartz@gentoo.org>
+# @SUPPORTED_EAPIS: 8
+# @BLURB: Provides a uniform way of handling ebuilds which package PGP key material
+# @DESCRIPTION:
+# This eclass provides a streamlined approach to finding suitable source material
+# for OpenPGP keys used by the verify-sig eclass. Its primary purpose is to permit
+# developers to easily and securely package new sec-keys/* packages. The eclass
+# removes the risk of developers accidentally packaging malformed key material, or
+# neglecting to notice when PGP identities have changed.
+#
+# To use the eclass, define SEC_KEYS_VALIDPGPKEYS to contain the fingerprint of
+# the key and the short name of the key's owner.
+#
+# @EXAMPLE:
+# Example use:
+#
+# @CODE
+# SEC_KEYS_VALIDPGPKEYS=(
+# '4EC8A4DB7D2E01C00AF36C49E5C587B5E286C65A:jsmith:github'
+# )
+#
+# inherit sec-keys
+# @CODE
+
+case ${EAPI} in
+ 8) ;;
+ *) die "${ECLASS}: EAPI ${EAPI:-0} not supported" ;;
+esac
+
+if [[ ! ${_SEC_KEYS_ECLASS} ]]; then
+_SEC_KEYS_ECLASS=1
+
+inherit edo
+
+# @ECLASS_VARIABLE: SEC_KEYS_VALIDPGPKEYS
+# @PRE_INHERIT
+# @DEFAULT_UNSET
+# @DESCRIPTION:
+# Mapping of fingerprints, name, and optional location of PGP keys to include,
+# separated by colons. The allowed values for a location are:
+#
+# - github -- fetch key from github.com/${name}.pgp
+#
+# - openpgp -- fetch key by fingerprint from https://keys.openpgp.org
+#
+# - ubuntu -- fetch key by fingerprint from http://keyserver.ubuntu.com (the default)
+#
+# - none -- do not add to SRC_URI, the ebuild will provide a custom download location
+_sec_keys_set_globals() {
+ if [[ ${SEC_KEYS_VALIDPGPKEYS[*]} ]]; then
+ local key fingerprint name loc locations=() remote
+ for key in "${SEC_KEYS_VALIDPGPKEYS[@]}"; do
+ fingerprint=${key%%:*}
+ name=${key#${fingerprint}:}; name=${name%%:*}
+ IFS=: read -r -a locations <<<"${key##*:}"
+ [[ ${locations[@]} ]] || locations=(ubuntu)
+ for loc in "${locations[@]}"; do
+ case ${loc} in
+ github) remote="https://github.com/${name}.gpg";;
+ openpgp) remote="https://keys.openpgp.org/vks/v1/by-fingerprint/${fingerprint}";;
+ ubuntu) remote="https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x${fingerprint}";;
+ # provided via manual SRC_URI
+ none) continue;;
+ *) die "${ECLASS}: unknown PGP key remote: ${loc}";;
+
+ esac
+ SRC_URI+="
+ ${remote} -> openpgp-keys-${name}-${loc}-${PV}.asc
+ "
+ done
+ done
+ fi
+}
+_sec_keys_set_globals
+unset -f _sec_keys_set_globals
+
+BDEPEND="app-crypt/gnupg"
+S=${WORKDIR}
+
+LICENSE="public-domain"
+SLOT="0"
+
+
+# @FUNCTION: sec-keys_src_compile
+# @DESCRIPTION:
+# Default src_compile override that imports all public keys into a keyring,
+# and validates that they are listed in SEC_KEYS_VALIDPGPKEYS.
+sec-keys_src_compile() {
+ local -x GNUPGHOME=${WORKDIR}/gnupg
+ mkdir -m700 -p "${GNUPGHOME}" || die
+
+ pushd "${DISTDIR}" >/dev/null || die
+ gpg --import ${A} || die
+ popd >/dev/null || die
+
+ local line imported_keys=() found=0
+ while IFS=: read -r -a line; do
+ if [[ ${line[0]} = pub ]]; then
+ # new key
+ found=0
+ elif [[ ${found} = 0 && ${line[0]} = fpr ]]; then
+ # primary fingerprint
+ imported_keys+=("${line[9]}")
+ found=1
+ fi
+ done < <(gpg --batch --list-keys --keyid-format=long --with-colons || die)
+
+ printf '%s\n' "${imported_keys[@]}" | sort > imported_keys.list || die
+ printf '%s\n' "${SEC_KEYS_VALIDPGPKEYS[@]%%:*}" | sort > allowed_keys.list || die
+
+ local extra_keys=($(comm -23 imported_keys.list allowed_keys.list || die))
+ local missing_keys=($(comm -13 imported_keys.list allowed_keys.list || die))
+
+ if [[ ${#extra_keys[@]} != 0 ]]; then
+ die "too many keys found. Suspicious keys: ${extra_keys[@]}"
+ fi
+ if [[ ${#missing_keys[@]} != 0 ]]; then
+ die "too few keys found. Unavailable keys: ${missing_keys[@]}"
+ fi
+}
+
+# @FUNCTION: sec-keys_src_install
+# @DESCRIPTION:
+# Default src_install override that minifies and exports all PGP public keys
+# into an ascii-armored keyfile installed to the standard /usr/share/openpgp-keys.
+sec-keys_src_install() {
+ local -x GNUPGHOME=${WORKDIR}/gnupg
+ local fingerprint
+ local gpg_command=(gpg --no-permission-warning --export-options export-minimal)
+
+ for fingerprint in "${SEC_KEYS_VALIDPGPKEYS[@]%%:*}"; do
+ local uids=()
+ mapfile -t uids < <("${gpg_command[@]}" --list-key --with-colons ${fingerprint} | awk -F: '/^uid/{print $10}' || die)
+ edo "${gpg_command[@]}" "${uids[@]/#/--comment=}" --export --armor "${fingerprint}" >> ${PN#openpgp-keys-}.asc || die
+ done
+
+ insinto /usr/share/openpgp-keys
+ doins ${PN#openpgp-keys-}.asc
+}
+
+fi
+
+EXPORT_FUNCTIONS src_compile src_install
--
2.45.2
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [gentoo-dev] [PATCH 2/2] sec-keys/openpgp-keys-gnutls: update to use sec-keys.eclass
2024-11-27 20:30 [gentoo-dev] [PATCH 1/2] sec-keys.eclass: new eclass Eli Schwartz
@ 2024-11-27 20:30 ` Eli Schwartz
2024-11-27 21:12 ` [gentoo-dev] [PATCH 1/2] sec-keys.eclass: new eclass Michał Górny
` (3 subsequent siblings)
4 siblings, 0 replies; 21+ messages in thread
From: Eli Schwartz @ 2024-11-27 20:30 UTC (permalink / raw
To: gentoo-dev
Bump EAPI 7 -> 8 as required by eclass. ;)
This is a nice demonstration of using the eclass. It showcases some
advanced usage, in particular, how to augment a single key within a
keyring published on upstream's website with an extended expiration date
from the openpgp.org keyserver.
The ebuild was previously updated in commit
1061fd37f9491f2601a8b5b6c92ffc3a2f42d7c9 with some explanatory text
about why we needed to do that. It wasn't a very good commit (sorry,
past self!) because it was kind of inscrutable whether the key "should"
have been included. That commit would have instead modified "none" to
"openpgp" for daiki, had the eclass existed exactly a year ago.
Bug: https://gitlab.com/gnutls/web-pages/-/issues/6
Fixes: 1061fd37f9491f2601a8b5b6c92ffc3a2f42d7c9
Signed-off-by: Eli Schwartz <eschwartz@gentoo.org>
---
sec-keys/openpgp-keys-gnutls/Manifest | 1 +
.../openpgp-keys-gnutls-20240415-r1.ebuild | 22 +++++++++++++++++++
2 files changed, 23 insertions(+)
create mode 100644 sec-keys/openpgp-keys-gnutls/openpgp-keys-gnutls-20240415-r1.ebuild
diff --git a/sec-keys/openpgp-keys-gnutls/Manifest b/sec-keys/openpgp-keys-gnutls/Manifest
index eb2132f1bf3e..a2a4a26cbb10 100644
--- a/sec-keys/openpgp-keys-gnutls/Manifest
+++ b/sec-keys/openpgp-keys-gnutls/Manifest
@@ -1,3 +1,4 @@
+DIST openpgp-keys-daiki-openpgp-20240415.asc 7345 BLAKE2B 0a6a0079483320bbd191f3c04a21ac52b159f755518ae7d6e27869b7cda06548bd7772ce5b9def07dece791537cbfb750bd227023f7665cbd860b5ce200fc4d0 SHA512 55af264087eceb244c59bec2aa4c62df3fc0b25ceb13728e7c3fc9042ae2fb990282367e6b46eda7ceb7f267add6dcd8948a887d825c7759cc61c642d6e0a3a7
DIST openpgp-keys-gnutls-20220320-release-keyring.gpg 20850 BLAKE2B 06865e4ac4e69237f7c52f551ce902281f6699bd144cb16c97e81aa1323c99187637c3b62730e700bc1c7f9de4608da55d3a80c8ffc240ee533e42b4707e4fbe SHA512 ebf592298142c7f05d8298a9e42aa4e5579eda54bf3c16f5a97cd22c22f3904a635a20cb1af6587d03b1a58545b925bddb769b098c664a336abce8ec9f10b9d5
DIST openpgp-keys-gnutls-20221017-release-keyring.gpg 26256 BLAKE2B c42024bade07f5e0d189c653a052aaa89803c71c8c3a5653417fb1ae3961c392257b5ef719b9236d291d449490d5dba90ae238b76391933f864458c0805e02e0 SHA512 5c14d83f4f37bd319c652db0d76fc5bb04752fb461bbe853e25b20ffe41d6d14faae6c0bdd0193ac6242975bf1205ce606a9d0082261cc4581fd680abfcdbd4d
DIST openpgp-keys-gnutls-20231129-daiki-updated.gpg 7345 BLAKE2B 0a6a0079483320bbd191f3c04a21ac52b159f755518ae7d6e27869b7cda06548bd7772ce5b9def07dece791537cbfb750bd227023f7665cbd860b5ce200fc4d0 SHA512 55af264087eceb244c59bec2aa4c62df3fc0b25ceb13728e7c3fc9042ae2fb990282367e6b46eda7ceb7f267add6dcd8948a887d825c7759cc61c642d6e0a3a7
diff --git a/sec-keys/openpgp-keys-gnutls/openpgp-keys-gnutls-20240415-r1.ebuild b/sec-keys/openpgp-keys-gnutls/openpgp-keys-gnutls-20240415-r1.ebuild
new file mode 100644
index 000000000000..d98e32a90411
--- /dev/null
+++ b/sec-keys/openpgp-keys-gnutls/openpgp-keys-gnutls-20240415-r1.ebuild
@@ -0,0 +1,22 @@
+# Copyright 1999-2024 Gentoo Authors
+# Distributed under the terms of the GNU General Public License v2
+
+EAPI=8
+
+SEC_KEYS_VALIDPGPKEYS=(
+ 'E987AB7F7E89667776D05B3BB0E9DD20B29F1432:alexander.sosedken:none'
+ # GnuTLS website has expired key for daiki
+ '462225C3B46F34879FC8496CD605848ED7E69871:daiki:openpgp'
+ '1CB27DBC98614B2D5841646D08302DB6A2670428:tim.ruehsen:none'
+ '5D46CB0F763405A7053556F47A75A648B3F9220C:zfridrich:none'
+)
+
+inherit sec-keys
+
+DESCRIPTION="OpenPGP keys used by GnuTLS"
+HOMEPAGE="https://www.gnutls.org/download.html"
+SRC_URI+="
+ https://gnutls.org/gnutls-release-keyring.gpg -> ${P}-release-keyring.gpg
+"
+
+KEYWORDS="~alpha ~amd64 ~arm ~arm64 ~hppa ~loong ~m68k ~mips ~ppc ~ppc64 ~riscv ~s390 ~sparc ~x86"
--
2.45.2
^ permalink raw reply related [flat|nested] 21+ messages in thread
* Re: [gentoo-dev] [PATCH 1/2] sec-keys.eclass: new eclass
2024-11-27 20:30 [gentoo-dev] [PATCH 1/2] sec-keys.eclass: new eclass Eli Schwartz
2024-11-27 20:30 ` [gentoo-dev] [PATCH 2/2] sec-keys/openpgp-keys-gnutls: update to use sec-keys.eclass Eli Schwartz
@ 2024-11-27 21:12 ` Michał Górny
2024-11-27 21:52 ` Sam James
2024-11-28 4:24 ` Eli Schwartz
2024-11-27 21:57 ` Sam James
` (2 subsequent siblings)
4 siblings, 2 replies; 21+ messages in thread
From: Michał Górny @ 2024-11-27 21:12 UTC (permalink / raw
To: gentoo-dev
[-- Attachment #1: Type: text/plain, Size: 1025 bytes --]
On Wed, 2024-11-27 at 15:30 -0500, Eli Schwartz wrote:
> The current state of verify-sig support is a bit awkward. We rely on
> validating distfiles against a known trusted keyring, but creating the
> known trusted keyring is basically all manual verification. We somehow
> decide an ascii armored key is good enough without any portage
> assistance, then arrange to download it and trust it by Manifest hash.
> How do we know when updating a key is actually safe?
>
> This eclass handles the problem in a manner inspired in part by pacman.
> We require an eclass variable that lists all permitted PGP fingerprints,
> and the eclass is responsible checking that list against the keys we
> will install. It comes with a mechanism for computing SRC_URI for a
> couple of well known locations, or you can append your own in the
> ebuild.
How about adding a src_test() that would check if the key needs bumping,
i.e. if an online update triggers any meaningful changes?
--
Best regards,
Michał Górny
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 512 bytes --]
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [gentoo-dev] [PATCH 1/2] sec-keys.eclass: new eclass
2024-11-27 21:12 ` [gentoo-dev] [PATCH 1/2] sec-keys.eclass: new eclass Michał Górny
@ 2024-11-27 21:52 ` Sam James
2024-11-28 4:24 ` Eli Schwartz
1 sibling, 0 replies; 21+ messages in thread
From: Sam James @ 2024-11-27 21:52 UTC (permalink / raw
To: Michał Górny; +Cc: gentoo-dev
Michał Górny <mgorny@gentoo.org> writes:
> On Wed, 2024-11-27 at 15:30 -0500, Eli Schwartz wrote:
>> The current state of verify-sig support is a bit awkward. We rely on
>> validating distfiles against a known trusted keyring, but creating the
>> known trusted keyring is basically all manual verification. We somehow
>> decide an ascii armored key is good enough without any portage
>> assistance, then arrange to download it and trust it by Manifest hash.
>> How do we know when updating a key is actually safe?
>>
>> This eclass handles the problem in a manner inspired in part by pacman.
>> We require an eclass variable that lists all permitted PGP fingerprints,
>> and the eclass is responsible checking that list against the keys we
>> will install. It comes with a mechanism for computing SRC_URI for a
>> couple of well known locations, or you can append your own in the
>> ebuild.
>
> How about adding a src_test() that would check if the key needs bumping,
> i.e. if an online update triggers any meaningful changes?
Ooh, I like this idea. We could print a pgpdump diff.
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [gentoo-dev] [PATCH 1/2] sec-keys.eclass: new eclass
2024-11-27 20:30 [gentoo-dev] [PATCH 1/2] sec-keys.eclass: new eclass Eli Schwartz
2024-11-27 20:30 ` [gentoo-dev] [PATCH 2/2] sec-keys/openpgp-keys-gnutls: update to use sec-keys.eclass Eli Schwartz
2024-11-27 21:12 ` [gentoo-dev] [PATCH 1/2] sec-keys.eclass: new eclass Michał Górny
@ 2024-11-27 21:57 ` Sam James
2024-11-28 4:17 ` Eli Schwartz
2024-11-28 4:32 ` [gentoo-dev] [PATCH v2 0/2] sec-keys.eclass Eli Schwartz
2024-11-28 10:35 ` [gentoo-dev] [PATCH 1/2] sec-keys.eclass: new eclass Ulrich Müller
4 siblings, 1 reply; 21+ messages in thread
From: Sam James @ 2024-11-27 21:57 UTC (permalink / raw
To: Eli Schwartz; +Cc: gentoo-dev
Eli Schwartz <eschwartz@gentoo.org> writes:
> The current state of verify-sig support is a bit awkward. We rely on
> validating distfiles against a known trusted keyring, but creating the
> known trusted keyring is basically all manual verification. We somehow
> decide an ascii armored key is good enough without any portage
> assistance, then arrange to download it and trust it by Manifest hash.
> How do we know when updating a key is actually safe?
>
> This eclass handles the problem in a manner inspired in part by pacman.
> We require an eclass variable that lists all permitted PGP fingerprints,
> and the eclass is responsible checking that list against the keys we
> will install. It comes with a mechanism for computing SRC_URI for a
> couple of well known locations, or you can append your own in the
> ebuild.
>
> Key rotations, both expected and malicious, are easily detected by
> checking the git log for changes to declared finterprints in a bump. The
s/finterprints/fingerprints/
> former can be rationalized in the commit message. So can the latter, but
> in most cases those will be rejected during peer review.
>
> Signed-off-by: Eli Schwartz <eschwartz@gentoo.org>
> ---
> eclass/sec-keys.eclass | 150 +++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 150 insertions(+)
> create mode 100644 eclass/sec-keys.eclass
>
I'm actually pleasantly surprised by how elegant this is -- which is no
reflection on you, just I sort of assumed the implementation would have
to be a bit gnarlier (not sure why).
One thing I really want to solve in general is key refreshes, given we
have no "src_fetch", users can't opt in to key refreshes even with
verify-sig.eclass unless network-sandbox is off, but that's a separate
problem. mgorny's suggestion wrt src_test would allow us to even
tinderbox needed refreshes which somewhat solves that problem too.
I like it.
> diff --git a/eclass/sec-keys.eclass b/eclass/sec-keys.eclass
> new file mode 100644
> index 000000000000..95e1b4a92c0e
> --- /dev/null
> +++ b/eclass/sec-keys.eclass
> @@ -0,0 +1,150 @@
> +# Copyright 2024 Gentoo Authors
> +# Distributed under the terms of the GNU General Public License v2
> +
> +# @ECLASS: sec-keys.eclass
> +# @MAINTAINER:
> +# Eli Schwartz <eschwartz@gentoo.org>
> +# @AUTHOR:
> +# Eli Schwartz <eschwartz@gentoo.org>
> +# @SUPPORTED_EAPIS: 8
> +# @BLURB: Provides a uniform way of handling ebuilds which package PGP key material
> +# @DESCRIPTION:
> +# This eclass provides a streamlined approach to finding suitable source material
> +# for OpenPGP keys used by the verify-sig eclass. Its primary purpose is to permit
> +# developers to easily and securely package new sec-keys/* packages. The eclass
> +# removes the risk of developers accidentally packaging malformed key material, or
> +# neglecting to notice when PGP identities have changed.
> +#
> +# To use the eclass, define SEC_KEYS_VALIDPGPKEYS to contain the fingerprint of
> +# the key and the short name of the key's owner.
> +#
> +# @EXAMPLE:
> +# Example use:
> +#
> +# @CODE
> +# SEC_KEYS_VALIDPGPKEYS=(
> +# '4EC8A4DB7D2E01C00AF36C49E5C587B5E286C65A:jsmith:github'
> +# )
Can you expand the example(s) here maybe with some comments in the array
to help people see when it might be suitable to use e.g. none with a mix
of github?
> +#
> +# inherit sec-keys
> +# @CODE
> +
> +case ${EAPI} in
> + 8) ;;
> + *) die "${ECLASS}: EAPI ${EAPI:-0} not supported" ;;
> +esac
> +
> +if [[ ! ${_SEC_KEYS_ECLASS} ]]; then
> +_SEC_KEYS_ECLASS=1
> +
> +inherit edo
> +
> +# @ECLASS_VARIABLE: SEC_KEYS_VALIDPGPKEYS
> +# @PRE_INHERIT
> +# @DEFAULT_UNSET
> +# @DESCRIPTION:
> +# Mapping of fingerprints, name, and optional location of PGP keys to include,
> +# separated by colons. The allowed values for a location are:
> +#
> +# - github -- fetch key from github.com/${name}.pgp
> +#
> +# - openpgp -- fetch key by fingerprint from https://keys.openpgp.org
> +#
> +# - ubuntu -- fetch key by fingerprint from http://keyserver.ubuntu.com (the default)
> +#
> +# - none -- do not add to SRC_URI, the ebuild will provide a custom download location
> +_sec_keys_set_globals() {
> + if [[ ${SEC_KEYS_VALIDPGPKEYS[*]} ]]; then
> + local key fingerprint name loc locations=() remote
> + for key in "${SEC_KEYS_VALIDPGPKEYS[@]}"; do
> + fingerprint=${key%%:*}
> + name=${key#${fingerprint}:}; name=${name%%:*}
> + IFS=: read -r -a locations <<<"${key##*:}"
> + [[ ${locations[@]} ]] || locations=(ubuntu)
> + for loc in "${locations[@]}"; do
> + case ${loc} in
> + github) remote="https://github.com/${name}.gpg";;
> + openpgp) remote="https://keys.openpgp.org/vks/v1/by-fingerprint/${fingerprint}";;
> + ubuntu) remote="https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x${fingerprint}";;
> + # provided via manual SRC_URI
> + none) continue;;
> + *) die "${ECLASS}: unknown PGP key remote: ${loc}";;
> +
> + esac
> + SRC_URI+="
> + ${remote} -> openpgp-keys-${name}-${loc}-${PV}.asc
> + "
> + done
> + done
> + fi
> +}
> +_sec_keys_set_globals
> +unset -f _sec_keys_set_globals
> +
> +BDEPEND="app-crypt/gnupg"
> +S=${WORKDIR}
> +
> +LICENSE="public-domain"
> +SLOT="0"
> +
> +
> +# @FUNCTION: sec-keys_src_compile
> +# @DESCRIPTION:
> +# Default src_compile override that imports all public keys into a keyring,
> +# and validates that they are listed in SEC_KEYS_VALIDPGPKEYS.
> +sec-keys_src_compile() {
> + local -x GNUPGHOME=${WORKDIR}/gnupg
> + mkdir -m700 -p "${GNUPGHOME}" || die
Is there any value in using gemato's gpg-wrap for this function?
> +
> + pushd "${DISTDIR}" >/dev/null || die
> + gpg --import ${A} || die
> + popd >/dev/null || die
> +
> + local line imported_keys=() found=0
> + while IFS=: read -r -a line; do
> + if [[ ${line[0]} = pub ]]; then
> + # new key
> + found=0
> + elif [[ ${found} = 0 && ${line[0]} = fpr ]]; then
> + # primary fingerprint
> + imported_keys+=("${line[9]}")
> + found=1
> + fi
> + done < <(gpg --batch --list-keys --keyid-format=long --with-colons || die)
> +
> + printf '%s\n' "${imported_keys[@]}" | sort > imported_keys.list || die
> + printf '%s\n' "${SEC_KEYS_VALIDPGPKEYS[@]%%:*}" | sort > allowed_keys.list || die
> +
> + local extra_keys=($(comm -23 imported_keys.list allowed_keys.list || die))
> + local missing_keys=($(comm -13 imported_keys.list allowed_keys.list || die))
Any reason to not readarray this instead?
> +
> + if [[ ${#extra_keys[@]} != 0 ]]; then
> + die "too many keys found. Suspicious keys: ${extra_keys[@]}"
> + fi
> + if [[ ${#missing_keys[@]} != 0 ]]; then
> + die "too few keys found. Unavailable keys: ${missing_keys[@]}"
> + fi
> +}
> +
> +# @FUNCTION: sec-keys_src_install
> +# @DESCRIPTION:
> +# Default src_install override that minifies and exports all PGP public keys
> +# into an ascii-armored keyfile installed to the standard /usr/share/openpgp-keys.
> +sec-keys_src_install() {
> + local -x GNUPGHOME=${WORKDIR}/gnupg
> + local fingerprint
> + local gpg_command=(gpg --no-permission-warning --export-options export-minimal)
> +
> + for fingerprint in "${SEC_KEYS_VALIDPGPKEYS[@]%%:*}"; do
> + local uids=()
> + mapfile -t uids < <("${gpg_command[@]}" --list-key --with-colons ${fingerprint} | awk -F: '/^uid/{print $10}' || die)
> + edo "${gpg_command[@]}" "${uids[@]/#/--comment=}" --export --armor "${fingerprint}" >> ${PN#openpgp-keys-}.asc || die
No need for the die here.
> + done
> +
> + insinto /usr/share/openpgp-keys
> + doins ${PN#openpgp-keys-}.asc
> +}
> +
> +fi
> +
> +EXPORT_FUNCTIONS src_compile src_install
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [gentoo-dev] [PATCH 1/2] sec-keys.eclass: new eclass
2024-11-27 21:57 ` Sam James
@ 2024-11-28 4:17 ` Eli Schwartz
0 siblings, 0 replies; 21+ messages in thread
From: Eli Schwartz @ 2024-11-28 4:17 UTC (permalink / raw
To: Sam James; +Cc: gentoo-dev
[-- Attachment #1.1: Type: text/plain, Size: 2626 bytes --]
On 11/27/24 4:57 PM, Sam James wrote:
> Eli Schwartz <eschwartz@gentoo.org> writes:
>> +# @EXAMPLE:
>> +# Example use:
>> +#
>> +# @CODE
>> +# SEC_KEYS_VALIDPGPKEYS=(
>> +# '4EC8A4DB7D2E01C00AF36C49E5C587B5E286C65A:jsmith:github'
>> +# )
>
> Can you expand the example(s) here maybe with some comments in the array
> to help people see when it might be suitable to use e.g. none with a mix
> of github?
Sure, good idea.
>> +# @FUNCTION: sec-keys_src_compile
>> +# @DESCRIPTION:
>> +# Default src_compile override that imports all public keys into a keyring,
>> +# and validates that they are listed in SEC_KEYS_VALIDPGPKEYS.
>> +sec-keys_src_compile() {
>> + local -x GNUPGHOME=${WORKDIR}/gnupg
>> + mkdir -m700 -p "${GNUPGHOME}" || die
>
> Is there any value in using gemato's gpg-wrap for this function?
I don't think so. The main use case for gemato that I see is it
automatically entering a tempdir context based on a keyfile. We need to
support multiple keyfiles, including ebuild-specified SRC_URI that may
not be ascii-armored and cannot be concatenated together, which means in
order to get to a point where gpg-wrap can be used to run one-off
commands using a keyfile in which gemato wraps the creation of a
keyring... we've basically done all the work we actually wanted to do.
>> + local extra_keys=($(comm -23 imported_keys.list allowed_keys.list || die))
>> + local missing_keys=($(comm -13 imported_keys.list allowed_keys.list || die))
>
> Any reason to not readarray this instead?
The files each contain a list of words (PGP fingerprint, consisting of
characters matching [0-9A-F] and nothing else), with the only whitspace
in the file being newline characters.
Both readarray and command substitution tokenize this the same. I'm not
sure it particularly matters which one to use, but command substitution
can be done on one line (local variable=($(command || die)) ) whereas
readarray requires two lines (local -a variable; readarray -t varlist <
<(command || die) ) and you have to remember to use -t and -a.
I don't think readarray provides any functionality we need here.
>> + for fingerprint in "${SEC_KEYS_VALIDPGPKEYS[@]%%:*}"; do
>> + local uids=()
>> + mapfile -t uids < <("${gpg_command[@]}" --list-key --with-colons ${fingerprint} | awk -F: '/^uid/{print $10}' || die)
>> + edo "${gpg_command[@]}" "${uids[@]/#/--comment=}" --export --armor "${fingerprint}" >> ${PN#openpgp-keys-}.asc || die
>
> No need for the die here.
Right, I probably forgot to remove this when I switched to edo.
--
Eli Schwartz
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 236 bytes --]
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [gentoo-dev] [PATCH 1/2] sec-keys.eclass: new eclass
2024-11-27 21:12 ` [gentoo-dev] [PATCH 1/2] sec-keys.eclass: new eclass Michał Górny
2024-11-27 21:52 ` Sam James
@ 2024-11-28 4:24 ` Eli Schwartz
1 sibling, 0 replies; 21+ messages in thread
From: Eli Schwartz @ 2024-11-28 4:24 UTC (permalink / raw
To: gentoo-dev
[-- Attachment #1.1: Type: text/plain, Size: 1684 bytes --]
On 11/27/24 4:12 PM, Michał Górny wrote:
> On Wed, 2024-11-27 at 15:30 -0500, Eli Schwartz wrote:
>> The current state of verify-sig support is a bit awkward. We rely on
>> validating distfiles against a known trusted keyring, but creating the
>> known trusted keyring is basically all manual verification. We somehow
>> decide an ascii armored key is good enough without any portage
>> assistance, then arrange to download it and trust it by Manifest hash.
>> How do we know when updating a key is actually safe?
>>
>> This eclass handles the problem in a manner inspired in part by pacman.
>> We require an eclass variable that lists all permitted PGP fingerprints,
>> and the eclass is responsible checking that list against the keys we
>> will install. It comes with a mechanism for computing SRC_URI for a
>> couple of well known locations, or you can append your own in the
>> ebuild.
>
> How about adding a src_test() that would check if the key needs bumping,
> i.e. if an online update triggers any meaningful changes?
This is a really nice suggestion. I used Sam's tip about pgpdump, so
that we print a diff after the online update, and fail if diff produces
a diff.
We use a cleaned and minimized version of the key, so it will only
show/trigger on changes to the uid or self-sig packets, which isn't
exactly the same as "meaningful changes". For example, running the tests
on the gnutls keyring in the second patch, Daiki's key has been updated
on Ubuntu with an additional expiry date change for a secondary uid,
which may be meaningful in certain senses but we can validate it just
fine using the primary uid.
--
Eli Schwartz
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 236 bytes --]
^ permalink raw reply [flat|nested] 21+ messages in thread
* [gentoo-dev] [PATCH v2 0/2] sec-keys.eclass
2024-11-27 20:30 [gentoo-dev] [PATCH 1/2] sec-keys.eclass: new eclass Eli Schwartz
` (2 preceding siblings ...)
2024-11-27 21:57 ` Sam James
@ 2024-11-28 4:32 ` Eli Schwartz
2024-11-28 4:32 ` [gentoo-dev] [PATCH v2 1/2] sec-keys.eclass: new eclass Eli Schwartz
2024-11-28 4:32 ` [gentoo-dev] [PATCH v2 2/2] sec-keys/openpgp-keys-gnutls: update to use sec-keys.eclass Eli Schwartz
2024-11-28 10:35 ` [gentoo-dev] [PATCH 1/2] sec-keys.eclass: new eclass Ulrich Müller
4 siblings, 2 replies; 21+ messages in thread
From: Eli Schwartz @ 2024-11-28 4:32 UTC (permalink / raw
To: gentoo-dev
v2 changes:
- add src_test
- add support for gentoo keyserver
- fix small typo in handling multiple sources
- remove outdated die based on review
Eli Schwartz (2):
sec-keys.eclass: new eclass
sec-keys/openpgp-keys-gnutls: update to use sec-keys.eclass
eclass/sec-keys.eclass | 197 ++++++++++++++++++
sec-keys/openpgp-keys-gnutls/Manifest | 1 +
.../openpgp-keys-gnutls-20240415-r1.ebuild | 22 ++
3 files changed, 220 insertions(+)
create mode 100644 eclass/sec-keys.eclass
create mode 100644 sec-keys/openpgp-keys-gnutls/openpgp-keys-gnutls-20240415-r1.ebuild
Range-diff against v1:
1: 02c47372ec21 ! 1: 6777dbb541bf sec-keys.eclass: new eclass
@@ Commit message
ebuild.
Key rotations, both expected and malicious, are easily detected by
- checking the git log for changes to declared finterprints in a bump. The
+ checking the git log for changes to declared fingerprints in a bump. The
former can be rationalized in the commit message. So can the latter, but
in most cases those will be rejected during peer review.
@@ eclass/sec-keys.eclass (new)
+#
+# @CODE
+# SEC_KEYS_VALIDPGPKEYS=(
-+# '4EC8A4DB7D2E01C00AF36C49E5C587B5E286C65A:jsmith:github'
++# # implicit Ubuntu
++# '3DB7F3CA6C1D90B99FE25B38D4B476A4D175C54F:bjones:'
++# '4EC8A4DB7D2E01C00AF36C49E5C587B5E286C65A:jsmith:github,openpgp'
++# # key only available on personal website, use manual SRC_URI
++# '5FD9B5EC8E3F12D11BA47D50F6D698C6F397D76B:awhite:none'
+# )
+#
+# inherit sec-keys
++#
++# SRC_URI+="https://awhite.com/awhite.gpg -> awhite-${PV}.gpg"
+# @CODE
+
+case ${EAPI} in
@@ eclass/sec-keys.eclass (new)
+# Mapping of fingerprints, name, and optional location of PGP keys to include,
+# separated by colons. The allowed values for a location are:
+#
++# - gentoo -- fetch key by fingerprint from https://keys.gentoo.org
++#
+# - github -- fetch key from github.com/${name}.pgp
+#
+# - openpgp -- fetch key by fingerprint from https://keys.openpgp.org
@@ eclass/sec-keys.eclass (new)
+ for key in "${SEC_KEYS_VALIDPGPKEYS[@]}"; do
+ fingerprint=${key%%:*}
+ name=${key#${fingerprint}:}; name=${name%%:*}
-+ IFS=: read -r -a locations <<<"${key##*:}"
++ IFS=, read -r -a locations <<<"${key##*:}"
+ [[ ${locations[@]} ]] || locations=(ubuntu)
+ for loc in "${locations[@]}"; do
+ case ${loc} in
++ gentoo) remote="https://keys.gentoo.org/pks/lookup?op=get&search=0x${fingerprint}";;
+ github) remote="https://github.com/${name}.gpg";;
+ openpgp) remote="https://keys.openpgp.org/vks/v1/by-fingerprint/${fingerprint}";;
+ ubuntu) remote="https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x${fingerprint}";;
@@ eclass/sec-keys.eclass (new)
+_sec_keys_set_globals
+unset -f _sec_keys_set_globals
+
-+BDEPEND="app-crypt/gnupg"
++IUSE="test"
++PROPERTIES="test_network"
++RESTRICT="test"
++
++BDEPEND="
++ app-crypt/gnupg
++ test? ( app-crypt/pgpdump )
++"
+S=${WORKDIR}
+
+LICENSE="public-domain"
@@ eclass/sec-keys.eclass (new)
+ fi
+}
+
++
++sec-keys_src_test() {
++ local -x GNUPGHOME=${WORKDIR}/gnupg
++ local key fingerprint name server
++ local gpg_command=(gpg --export-options export-minimal)
++
++ for fingerprint in "${SEC_KEYS_VALIDPGPKEYS[@]%%:*}"; do
++ "${gpg_command[@]}" --export "${fingerprint}" | pgpdump > "${fingerprint}.pgpdump" || die
++ done
++
++ # Best-effort attempt to check for updates. keyservers can and usually do
++ # fail for weird reasons, (such as being unable to import a key without a
++ # uid) as well as normal reasons, like the key being exclusive to a
++ # different keyserver. this isn't a reason to fail src_test.
++ for server in keys.gentoo.org keys.openpgp.org keyserver.ubuntu.com; do
++ gpg --refresh-keys --keyserver "hkps://${server}"
++ done
++ for key in "${SEC_KEYS_VALIDPGPKEYS[@]}"; do
++ if [[ ${key##*:} = *github* ]]; then
++ name=${key#*:}; name=${name%%:*}
++ wget -qO- https://github.com/${name}.gpg | gpg --import || die
++ fi
++ done
++
++ for fingerprint in "${SEC_KEYS_VALIDPGPKEYS[@]%%:*}"; do
++ "${gpg_command[@]}" --export "${fingerprint}" | pgpdump > "${fingerprint}.pgpdump.new" || die
++ diff -u "${fingerprint}.pgpdump" "${fingerprint}.pgpdump.new" || die "updates available for PGP key: ${fingerprint}"
++ done
++
++}
++
+# @FUNCTION: sec-keys_src_install
+# @DESCRIPTION:
+# Default src_install override that minifies and exports all PGP public keys
@@ eclass/sec-keys.eclass (new)
+ for fingerprint in "${SEC_KEYS_VALIDPGPKEYS[@]%%:*}"; do
+ local uids=()
+ mapfile -t uids < <("${gpg_command[@]}" --list-key --with-colons ${fingerprint} | awk -F: '/^uid/{print $10}' || die)
-+ edo "${gpg_command[@]}" "${uids[@]/#/--comment=}" --export --armor "${fingerprint}" >> ${PN#openpgp-keys-}.asc || die
++ edo "${gpg_command[@]}" "${uids[@]/#/--comment=}" --export --armor "${fingerprint}" >> ${PN#openpgp-keys-}.asc
+ done
+
+ insinto /usr/share/openpgp-keys
@@ eclass/sec-keys.eclass (new)
+
+fi
+
-+EXPORT_FUNCTIONS src_compile src_install
++EXPORT_FUNCTIONS src_compile src_test src_install
2: 0060997db9cb = 2: 2f78bceaed3b sec-keys/openpgp-keys-gnutls: update to use sec-keys.eclass
--
2.45.2
^ permalink raw reply [flat|nested] 21+ messages in thread
* [gentoo-dev] [PATCH v2 1/2] sec-keys.eclass: new eclass
2024-11-28 4:32 ` [gentoo-dev] [PATCH v2 0/2] sec-keys.eclass Eli Schwartz
@ 2024-11-28 4:32 ` Eli Schwartz
2024-11-28 13:10 ` Michał Górny
2024-11-29 7:30 ` Florian Schmaus
2024-11-28 4:32 ` [gentoo-dev] [PATCH v2 2/2] sec-keys/openpgp-keys-gnutls: update to use sec-keys.eclass Eli Schwartz
1 sibling, 2 replies; 21+ messages in thread
From: Eli Schwartz @ 2024-11-28 4:32 UTC (permalink / raw
To: gentoo-dev
The current state of verify-sig support is a bit awkward. We rely on
validating distfiles against a known trusted keyring, but creating the
known trusted keyring is basically all manual verification. We somehow
decide an ascii armored key is good enough without any portage
assistance, then arrange to download it and trust it by Manifest hash.
How do we know when updating a key is actually safe?
This eclass handles the problem in a manner inspired in part by pacman.
We require an eclass variable that lists all permitted PGP fingerprints,
and the eclass is responsible checking that list against the keys we
will install. It comes with a mechanism for computing SRC_URI for a
couple of well known locations, or you can append your own in the
ebuild.
Key rotations, both expected and malicious, are easily detected by
checking the git log for changes to declared fingerprints in a bump. The
former can be rationalized in the commit message. So can the latter, but
in most cases those will be rejected during peer review.
Signed-off-by: Eli Schwartz <eschwartz@gentoo.org>
---
eclass/sec-keys.eclass | 197 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 197 insertions(+)
create mode 100644 eclass/sec-keys.eclass
diff --git a/eclass/sec-keys.eclass b/eclass/sec-keys.eclass
new file mode 100644
index 000000000000..7ea4d34a8c1c
--- /dev/null
+++ b/eclass/sec-keys.eclass
@@ -0,0 +1,197 @@
+# Copyright 2024 Gentoo Authors
+# Distributed under the terms of the GNU General Public License v2
+
+# @ECLASS: sec-keys.eclass
+# @MAINTAINER:
+# Eli Schwartz <eschwartz@gentoo.org>
+# @AUTHOR:
+# Eli Schwartz <eschwartz@gentoo.org>
+# @SUPPORTED_EAPIS: 8
+# @BLURB: Provides a uniform way of handling ebuilds which package PGP key material
+# @DESCRIPTION:
+# This eclass provides a streamlined approach to finding suitable source material
+# for OpenPGP keys used by the verify-sig eclass. Its primary purpose is to permit
+# developers to easily and securely package new sec-keys/* packages. The eclass
+# removes the risk of developers accidentally packaging malformed key material, or
+# neglecting to notice when PGP identities have changed.
+#
+# To use the eclass, define SEC_KEYS_VALIDPGPKEYS to contain the fingerprint of
+# the key and the short name of the key's owner.
+#
+# @EXAMPLE:
+# Example use:
+#
+# @CODE
+# SEC_KEYS_VALIDPGPKEYS=(
+# # implicit Ubuntu
+# '3DB7F3CA6C1D90B99FE25B38D4B476A4D175C54F:bjones:'
+# '4EC8A4DB7D2E01C00AF36C49E5C587B5E286C65A:jsmith:github,openpgp'
+# # key only available on personal website, use manual SRC_URI
+# '5FD9B5EC8E3F12D11BA47D50F6D698C6F397D76B:awhite:none'
+# )
+#
+# inherit sec-keys
+#
+# SRC_URI+="https://awhite.com/awhite.gpg -> awhite-${PV}.gpg"
+# @CODE
+
+case ${EAPI} in
+ 8) ;;
+ *) die "${ECLASS}: EAPI ${EAPI:-0} not supported" ;;
+esac
+
+if [[ ! ${_SEC_KEYS_ECLASS} ]]; then
+_SEC_KEYS_ECLASS=1
+
+inherit edo
+
+# @ECLASS_VARIABLE: SEC_KEYS_VALIDPGPKEYS
+# @PRE_INHERIT
+# @DEFAULT_UNSET
+# @DESCRIPTION:
+# Mapping of fingerprints, name, and optional location of PGP keys to include,
+# separated by colons. The allowed values for a location are:
+#
+# - gentoo -- fetch key by fingerprint from https://keys.gentoo.org
+#
+# - github -- fetch key from github.com/${name}.pgp
+#
+# - openpgp -- fetch key by fingerprint from https://keys.openpgp.org
+#
+# - ubuntu -- fetch key by fingerprint from http://keyserver.ubuntu.com (the default)
+#
+# - none -- do not add to SRC_URI, the ebuild will provide a custom download location
+_sec_keys_set_globals() {
+ if [[ ${SEC_KEYS_VALIDPGPKEYS[*]} ]]; then
+ local key fingerprint name loc locations=() remote
+ for key in "${SEC_KEYS_VALIDPGPKEYS[@]}"; do
+ fingerprint=${key%%:*}
+ name=${key#${fingerprint}:}; name=${name%%:*}
+ IFS=, read -r -a locations <<<"${key##*:}"
+ [[ ${locations[@]} ]] || locations=(ubuntu)
+ for loc in "${locations[@]}"; do
+ case ${loc} in
+ gentoo) remote="https://keys.gentoo.org/pks/lookup?op=get&search=0x${fingerprint}";;
+ github) remote="https://github.com/${name}.gpg";;
+ openpgp) remote="https://keys.openpgp.org/vks/v1/by-fingerprint/${fingerprint}";;
+ ubuntu) remote="https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x${fingerprint}";;
+ # provided via manual SRC_URI
+ none) continue;;
+ *) die "${ECLASS}: unknown PGP key remote: ${loc}";;
+
+ esac
+ SRC_URI+="
+ ${remote} -> openpgp-keys-${name}-${loc}-${PV}.asc
+ "
+ done
+ done
+ fi
+}
+_sec_keys_set_globals
+unset -f _sec_keys_set_globals
+
+IUSE="test"
+PROPERTIES="test_network"
+RESTRICT="test"
+
+BDEPEND="
+ app-crypt/gnupg
+ test? ( app-crypt/pgpdump )
+"
+S=${WORKDIR}
+
+LICENSE="public-domain"
+SLOT="0"
+
+
+# @FUNCTION: sec-keys_src_compile
+# @DESCRIPTION:
+# Default src_compile override that imports all public keys into a keyring,
+# and validates that they are listed in SEC_KEYS_VALIDPGPKEYS.
+sec-keys_src_compile() {
+ local -x GNUPGHOME=${WORKDIR}/gnupg
+ mkdir -m700 -p "${GNUPGHOME}" || die
+
+ pushd "${DISTDIR}" >/dev/null || die
+ gpg --import ${A} || die
+ popd >/dev/null || die
+
+ local line imported_keys=() found=0
+ while IFS=: read -r -a line; do
+ if [[ ${line[0]} = pub ]]; then
+ # new key
+ found=0
+ elif [[ ${found} = 0 && ${line[0]} = fpr ]]; then
+ # primary fingerprint
+ imported_keys+=("${line[9]}")
+ found=1
+ fi
+ done < <(gpg --batch --list-keys --keyid-format=long --with-colons || die)
+
+ printf '%s\n' "${imported_keys[@]}" | sort > imported_keys.list || die
+ printf '%s\n' "${SEC_KEYS_VALIDPGPKEYS[@]%%:*}" | sort > allowed_keys.list || die
+
+ local extra_keys=($(comm -23 imported_keys.list allowed_keys.list || die))
+ local missing_keys=($(comm -13 imported_keys.list allowed_keys.list || die))
+
+ if [[ ${#extra_keys[@]} != 0 ]]; then
+ die "too many keys found. Suspicious keys: ${extra_keys[@]}"
+ fi
+ if [[ ${#missing_keys[@]} != 0 ]]; then
+ die "too few keys found. Unavailable keys: ${missing_keys[@]}"
+ fi
+}
+
+
+sec-keys_src_test() {
+ local -x GNUPGHOME=${WORKDIR}/gnupg
+ local key fingerprint name server
+ local gpg_command=(gpg --export-options export-minimal)
+
+ for fingerprint in "${SEC_KEYS_VALIDPGPKEYS[@]%%:*}"; do
+ "${gpg_command[@]}" --export "${fingerprint}" | pgpdump > "${fingerprint}.pgpdump" || die
+ done
+
+ # Best-effort attempt to check for updates. keyservers can and usually do
+ # fail for weird reasons, (such as being unable to import a key without a
+ # uid) as well as normal reasons, like the key being exclusive to a
+ # different keyserver. this isn't a reason to fail src_test.
+ for server in keys.gentoo.org keys.openpgp.org keyserver.ubuntu.com; do
+ gpg --refresh-keys --keyserver "hkps://${server}"
+ done
+ for key in "${SEC_KEYS_VALIDPGPKEYS[@]}"; do
+ if [[ ${key##*:} = *github* ]]; then
+ name=${key#*:}; name=${name%%:*}
+ wget -qO- https://github.com/${name}.gpg | gpg --import || die
+ fi
+ done
+
+ for fingerprint in "${SEC_KEYS_VALIDPGPKEYS[@]%%:*}"; do
+ "${gpg_command[@]}" --export "${fingerprint}" | pgpdump > "${fingerprint}.pgpdump.new" || die
+ diff -u "${fingerprint}.pgpdump" "${fingerprint}.pgpdump.new" || die "updates available for PGP key: ${fingerprint}"
+ done
+
+}
+
+# @FUNCTION: sec-keys_src_install
+# @DESCRIPTION:
+# Default src_install override that minifies and exports all PGP public keys
+# into an ascii-armored keyfile installed to the standard /usr/share/openpgp-keys.
+sec-keys_src_install() {
+ local -x GNUPGHOME=${WORKDIR}/gnupg
+ local fingerprint
+ local gpg_command=(gpg --no-permission-warning --export-options export-minimal)
+
+ for fingerprint in "${SEC_KEYS_VALIDPGPKEYS[@]%%:*}"; do
+ local uids=()
+ mapfile -t uids < <("${gpg_command[@]}" --list-key --with-colons ${fingerprint} | awk -F: '/^uid/{print $10}' || die)
+ edo "${gpg_command[@]}" "${uids[@]/#/--comment=}" --export --armor "${fingerprint}" >> ${PN#openpgp-keys-}.asc
+ done
+
+ insinto /usr/share/openpgp-keys
+ doins ${PN#openpgp-keys-}.asc
+}
+
+fi
+
+EXPORT_FUNCTIONS src_compile src_test src_install
--
2.45.2
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [gentoo-dev] [PATCH v2 2/2] sec-keys/openpgp-keys-gnutls: update to use sec-keys.eclass
2024-11-28 4:32 ` [gentoo-dev] [PATCH v2 0/2] sec-keys.eclass Eli Schwartz
2024-11-28 4:32 ` [gentoo-dev] [PATCH v2 1/2] sec-keys.eclass: new eclass Eli Schwartz
@ 2024-11-28 4:32 ` Eli Schwartz
1 sibling, 0 replies; 21+ messages in thread
From: Eli Schwartz @ 2024-11-28 4:32 UTC (permalink / raw
To: gentoo-dev
Bump EAPI 7 -> 8 as required by eclass. ;)
This is a nice demonstration of using the eclass. It showcases some
advanced usage, in particular, how to augment a single key within a
keyring published on upstream's website with an extended expiration date
from the openpgp.org keyserver.
The ebuild was previously updated in commit
1061fd37f9491f2601a8b5b6c92ffc3a2f42d7c9 with some explanatory text
about why we needed to do that. It wasn't a very good commit (sorry,
past self!) because it was kind of inscrutable whether the key "should"
have been included. That commit would have instead modified "none" to
"openpgp" for daiki, had the eclass existed exactly a year ago.
Bug: https://gitlab.com/gnutls/web-pages/-/issues/6
Fixes: 1061fd37f9491f2601a8b5b6c92ffc3a2f42d7c9
Signed-off-by: Eli Schwartz <eschwartz@gentoo.org>
---
sec-keys/openpgp-keys-gnutls/Manifest | 1 +
.../openpgp-keys-gnutls-20240415-r1.ebuild | 22 +++++++++++++++++++
2 files changed, 23 insertions(+)
create mode 100644 sec-keys/openpgp-keys-gnutls/openpgp-keys-gnutls-20240415-r1.ebuild
diff --git a/sec-keys/openpgp-keys-gnutls/Manifest b/sec-keys/openpgp-keys-gnutls/Manifest
index eb2132f1bf3e..a2a4a26cbb10 100644
--- a/sec-keys/openpgp-keys-gnutls/Manifest
+++ b/sec-keys/openpgp-keys-gnutls/Manifest
@@ -1,3 +1,4 @@
+DIST openpgp-keys-daiki-openpgp-20240415.asc 7345 BLAKE2B 0a6a0079483320bbd191f3c04a21ac52b159f755518ae7d6e27869b7cda06548bd7772ce5b9def07dece791537cbfb750bd227023f7665cbd860b5ce200fc4d0 SHA512 55af264087eceb244c59bec2aa4c62df3fc0b25ceb13728e7c3fc9042ae2fb990282367e6b46eda7ceb7f267add6dcd8948a887d825c7759cc61c642d6e0a3a7
DIST openpgp-keys-gnutls-20220320-release-keyring.gpg 20850 BLAKE2B 06865e4ac4e69237f7c52f551ce902281f6699bd144cb16c97e81aa1323c99187637c3b62730e700bc1c7f9de4608da55d3a80c8ffc240ee533e42b4707e4fbe SHA512 ebf592298142c7f05d8298a9e42aa4e5579eda54bf3c16f5a97cd22c22f3904a635a20cb1af6587d03b1a58545b925bddb769b098c664a336abce8ec9f10b9d5
DIST openpgp-keys-gnutls-20221017-release-keyring.gpg 26256 BLAKE2B c42024bade07f5e0d189c653a052aaa89803c71c8c3a5653417fb1ae3961c392257b5ef719b9236d291d449490d5dba90ae238b76391933f864458c0805e02e0 SHA512 5c14d83f4f37bd319c652db0d76fc5bb04752fb461bbe853e25b20ffe41d6d14faae6c0bdd0193ac6242975bf1205ce606a9d0082261cc4581fd680abfcdbd4d
DIST openpgp-keys-gnutls-20231129-daiki-updated.gpg 7345 BLAKE2B 0a6a0079483320bbd191f3c04a21ac52b159f755518ae7d6e27869b7cda06548bd7772ce5b9def07dece791537cbfb750bd227023f7665cbd860b5ce200fc4d0 SHA512 55af264087eceb244c59bec2aa4c62df3fc0b25ceb13728e7c3fc9042ae2fb990282367e6b46eda7ceb7f267add6dcd8948a887d825c7759cc61c642d6e0a3a7
diff --git a/sec-keys/openpgp-keys-gnutls/openpgp-keys-gnutls-20240415-r1.ebuild b/sec-keys/openpgp-keys-gnutls/openpgp-keys-gnutls-20240415-r1.ebuild
new file mode 100644
index 000000000000..d98e32a90411
--- /dev/null
+++ b/sec-keys/openpgp-keys-gnutls/openpgp-keys-gnutls-20240415-r1.ebuild
@@ -0,0 +1,22 @@
+# Copyright 1999-2024 Gentoo Authors
+# Distributed under the terms of the GNU General Public License v2
+
+EAPI=8
+
+SEC_KEYS_VALIDPGPKEYS=(
+ 'E987AB7F7E89667776D05B3BB0E9DD20B29F1432:alexander.sosedken:none'
+ # GnuTLS website has expired key for daiki
+ '462225C3B46F34879FC8496CD605848ED7E69871:daiki:openpgp'
+ '1CB27DBC98614B2D5841646D08302DB6A2670428:tim.ruehsen:none'
+ '5D46CB0F763405A7053556F47A75A648B3F9220C:zfridrich:none'
+)
+
+inherit sec-keys
+
+DESCRIPTION="OpenPGP keys used by GnuTLS"
+HOMEPAGE="https://www.gnutls.org/download.html"
+SRC_URI+="
+ https://gnutls.org/gnutls-release-keyring.gpg -> ${P}-release-keyring.gpg
+"
+
+KEYWORDS="~alpha ~amd64 ~arm ~arm64 ~hppa ~loong ~m68k ~mips ~ppc ~ppc64 ~riscv ~s390 ~sparc ~x86"
--
2.45.2
^ permalink raw reply related [flat|nested] 21+ messages in thread
* Re: [gentoo-dev] [PATCH 1/2] sec-keys.eclass: new eclass
2024-11-27 20:30 [gentoo-dev] [PATCH 1/2] sec-keys.eclass: new eclass Eli Schwartz
` (3 preceding siblings ...)
2024-11-28 4:32 ` [gentoo-dev] [PATCH v2 0/2] sec-keys.eclass Eli Schwartz
@ 2024-11-28 10:35 ` Ulrich Müller
2024-11-28 15:36 ` Eli Schwartz
4 siblings, 1 reply; 21+ messages in thread
From: Ulrich Müller @ 2024-11-28 10:35 UTC (permalink / raw
To: Eli Schwartz; +Cc: gentoo-dev
[-- Attachment #1: Type: text/plain, Size: 5740 bytes --]
>>>>> On Wed, 27 Nov 2024, Eli Schwartz wrote:
> --- /dev/null
> +++ b/eclass/sec-keys.eclass
> @@ -0,0 +1,150 @@
> +# Copyright 2024 Gentoo Authors
> +# Distributed under the terms of the GNU General Public License v2
> +
> +# @ECLASS: sec-keys.eclass
> +# @MAINTAINER:
> +# Eli Schwartz <eschwartz@gentoo.org>
> +# @AUTHOR:
> +# Eli Schwartz <eschwartz@gentoo.org>
> +# @SUPPORTED_EAPIS: 8
> +# @BLURB: Provides a uniform way of handling ebuilds which package PGP key material
> +# @DESCRIPTION:
> +# This eclass provides a streamlined approach to finding suitable source material
> +# for OpenPGP keys used by the verify-sig eclass. Its primary purpose is to permit
> +# developers to easily and securely package new sec-keys/* packages. The eclass
> +# removes the risk of developers accidentally packaging malformed key material, or
> +# neglecting to notice when PGP identities have changed.
> +#
> +# To use the eclass, define SEC_KEYS_VALIDPGPKEYS to contain the fingerprint of
> +# the key and the short name of the key's owner.
Please wrap these comment lines to a line length of 70-ish characters
for readability.
Also, there should be two spaces after every full stop (except when it's
followed by a newline), so groff can recognise the sentence end in the
generated man page.
> +#
> +# @EXAMPLE:
> +# Example use:
> +#
> +# @CODE
> +# SEC_KEYS_VALIDPGPKEYS=(
> +# '4EC8A4DB7D2E01C00AF36C49E5C587B5E286C65A:jsmith:github'
> +# )
> +#
> +# inherit sec-keys
> +# @CODE
> +
> +case ${EAPI} in
> + 8) ;;
> + *) die "${ECLASS}: EAPI ${EAPI:-0} not supported" ;;
> +esac
> +
> +if [[ ! ${_SEC_KEYS_ECLASS} ]]; then
> +_SEC_KEYS_ECLASS=1
> +
> +inherit edo
> +
> +# @ECLASS_VARIABLE: SEC_KEYS_VALIDPGPKEYS
> +# @PRE_INHERIT
> +# @DEFAULT_UNSET
> +# @DESCRIPTION:
> +# Mapping of fingerprints, name, and optional location of PGP keys to include,
> +# separated by colons. The allowed values for a location are:
> +#
> +# - github -- fetch key from github.com/${name}.pgp
> +#
> +# - openpgp -- fetch key by fingerprint from https://keys.openpgp.org
> +#
> +# - ubuntu -- fetch key by fingerprint from http://keyserver.ubuntu.com (the default)
> +#
> +# - none -- do not add to SRC_URI, the ebuild will provide a custom download location
> +_sec_keys_set_globals() {
> + if [[ ${SEC_KEYS_VALIDPGPKEYS[*]} ]]; then
Why is the if needed? If the array is empty, the following for loop
won't execute.
> + local key fingerprint name loc locations=() remote
> + for key in "${SEC_KEYS_VALIDPGPKEYS[@]}"; do
> + fingerprint=${key%%:*}
> + name=${key#${fingerprint}:}; name=${name%%:*}
> + IFS=: read -r -a locations <<<"${key##*:}"
> + [[ ${locations[@]} ]] || locations=(ubuntu)
> + for loc in "${locations[@]}"; do
> + case ${loc} in
> + github) remote="https://github.com/${name}.gpg";;
> + openpgp) remote="https://keys.openpgp.org/vks/v1/by-fingerprint/${fingerprint}";;
> + ubuntu) remote="https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x${fingerprint}";;
> + # provided via manual SRC_URI
> + none) continue;;
> + *) die "${ECLASS}: unknown PGP key remote: ${loc}";;
> +
> + esac
> + SRC_URI+="
> + ${remote} -> openpgp-keys-${name}-${loc}-${PV}.asc
> + "
> + done
> + done
> + fi
> +}
> +_sec_keys_set_globals
> +unset -f _sec_keys_set_globals
> +
> +BDEPEND="app-crypt/gnupg"
> +S=${WORKDIR}
> +
> +LICENSE="public-domain"
> +SLOT="0"
> +
> +
> +# @FUNCTION: sec-keys_src_compile
> +# @DESCRIPTION:
> +# Default src_compile override that imports all public keys into a keyring,
> +# and validates that they are listed in SEC_KEYS_VALIDPGPKEYS.
> +sec-keys_src_compile() {
> + local -x GNUPGHOME=${WORKDIR}/gnupg
> + mkdir -m700 -p "${GNUPGHOME}" || die
> +
> + pushd "${DISTDIR}" >/dev/null || die
> + gpg --import ${A} || die
> + popd >/dev/null || die
> +
> + local line imported_keys=() found=0
> + while IFS=: read -r -a line; do
> + if [[ ${line[0]} = pub ]]; then
> + # new key
> + found=0
> + elif [[ ${found} = 0 && ${line[0]} = fpr ]]; then
> + # primary fingerprint
> + imported_keys+=("${line[9]}")
> + found=1
> + fi
> + done < <(gpg --batch --list-keys --keyid-format=long --with-colons || die)
> +
> + printf '%s\n' "${imported_keys[@]}" | sort > imported_keys.list || die
> + printf '%s\n' "${SEC_KEYS_VALIDPGPKEYS[@]%%:*}" | sort > allowed_keys.list || die
Maybe create these files in ${T} instead?
> +
> + local extra_keys=($(comm -23 imported_keys.list allowed_keys.list || die))
> + local missing_keys=($(comm -13 imported_keys.list allowed_keys.list || die))
> +
> + if [[ ${#extra_keys[@]} != 0 ]]; then
> + die "too many keys found. Suspicious keys: ${extra_keys[@]}"
> + fi
> + if [[ ${#missing_keys[@]} != 0 ]]; then
> + die "too few keys found. Unavailable keys: ${missing_keys[@]}"
> + fi
> +}
> +
> +# @FUNCTION: sec-keys_src_install
> +# @DESCRIPTION:
> +# Default src_install override that minifies and exports all PGP public keys
> +# into an ascii-armored keyfile installed to the standard /usr/share/openpgp-keys.
> +sec-keys_src_install() {
> + local -x GNUPGHOME=${WORKDIR}/gnupg
> + local fingerprint
> + local gpg_command=(gpg --no-permission-warning --export-options export-minimal)
> +
> + for fingerprint in "${SEC_KEYS_VALIDPGPKEYS[@]%%:*}"; do
> + local uids=()
> + mapfile -t uids < <("${gpg_command[@]}" --list-key --with-colons ${fingerprint} | awk -F: '/^uid/{print $10}' || die)
> + edo "${gpg_command[@]}" "${uids[@]/#/--comment=}" --export --armor "${fingerprint}" >> ${PN#openpgp-keys-}.asc || die
edo dies by itself, so "|| die" is not needed.
> + done
> +
> + insinto /usr/share/openpgp-keys
> + doins ${PN#openpgp-keys-}.asc
> +}
> +
> +fi
> +
> +EXPORT_FUNCTIONS src_compile src_install
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 507 bytes --]
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [gentoo-dev] [PATCH v2 1/2] sec-keys.eclass: new eclass
2024-11-28 4:32 ` [gentoo-dev] [PATCH v2 1/2] sec-keys.eclass: new eclass Eli Schwartz
@ 2024-11-28 13:10 ` Michał Górny
2024-11-28 15:36 ` Eli Schwartz
2024-11-29 7:30 ` Florian Schmaus
1 sibling, 1 reply; 21+ messages in thread
From: Michał Górny @ 2024-11-28 13:10 UTC (permalink / raw
To: gentoo-dev
[-- Attachment #1: Type: text/plain, Size: 6647 bytes --]
On Wed, 2024-11-27 at 23:32 -0500, Eli Schwartz wrote:
> +# @ECLASS_VARIABLE: SEC_KEYS_VALIDPGPKEYS
> +# @PRE_INHERIT
> +# @DEFAULT_UNSET
> +# @DESCRIPTION:
> +# Mapping of fingerprints, name, and optional location of PGP keys to include,
So "location" or "locations", plural?
> +# separated by colons. The allowed values for a location are:
> +#
> +# - gentoo -- fetch key by fingerprint from https://keys.gentoo.org
> +#
> +# - github -- fetch key from github.com/${name}.pgp
> +#
> +# - openpgp -- fetch key by fingerprint from https://keys.openpgp.org
> +#
> +# - ubuntu -- fetch key by fingerprint from http://keyserver.ubuntu.com (the default)
I'd go without a default. Typing 6 more letters doesn't cost anything,
and makes the contents more consistent. Also saves us from regretting
having chosen a bad default in the future.
> +#
> +# - none -- do not add to SRC_URI, the ebuild will provide a custom download location
Perhaps "manual"? "None" sounds like there would be no key at all.
> +_sec_keys_set_globals() {
> + if [[ ${SEC_KEYS_VALIDPGPKEYS[*]} ]]; then
> + local key fingerprint name loc locations=() remote
> + for key in "${SEC_KEYS_VALIDPGPKEYS[@]}"; do
> + fingerprint=${key%%:*}
> + name=${key#${fingerprint}:}; name=${name%%:*}
> + IFS=, read -r -a locations <<<"${key##*:}"
> + [[ ${locations[@]} ]] || locations=(ubuntu)
> + for loc in "${locations[@]}"; do
> + case ${loc} in
> + gentoo) remote="https://keys.gentoo.org/pks/lookup?op=get&search=0x${fingerprint}";;
> + github) remote="https://github.com/${name}.gpg";;
> + openpgp) remote="https://keys.openpgp.org/vks/v1/by-fingerprint/${fingerprint}";;
> + ubuntu) remote="https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x${fingerprint}";;
> + # provided via manual SRC_URI
> + none) continue;;
> + *) die "${ECLASS}: unknown PGP key remote: ${loc}";;
> +
Stray empty line.
> + esac
> + SRC_URI+="
> + ${remote} -> openpgp-keys-${name}-${loc}-${PV}.asc
> + "
> + done
> + done
> + fi
> +}
> +_sec_keys_set_globals
> +unset -f _sec_keys_set_globals
> +
> +IUSE="test"
> +PROPERTIES="test_network"
> +RESTRICT="test"
> +
> +BDEPEND="
> + app-crypt/gnupg
> + test? ( app-crypt/pgpdump )
> +"
> +S=${WORKDIR}
> +
> +LICENSE="public-domain"
> +SLOT="0"
Please keep ebuildy variables in the standard/skel order, or at least
as close to it as you can get.
> +# @FUNCTION: sec-keys_src_compile
> +# @DESCRIPTION:
> +# Default src_compile override that imports all public keys into a keyring,
> +# and validates that they are listed in SEC_KEYS_VALIDPGPKEYS.
> +sec-keys_src_compile() {
> + local -x GNUPGHOME=${WORKDIR}/gnupg
> + mkdir -m700 -p "${GNUPGHOME}" || die
> +
> + pushd "${DISTDIR}" >/dev/null || die
> + gpg --import ${A} || die
> + popd >/dev/null || die
> +
> + local line imported_keys=() found=0
> + while IFS=: read -r -a line; do
> + if [[ ${line[0]} = pub ]]; then
Please use '==' in ebuilds and eclasses.
> + # new key
> + found=0
> + elif [[ ${found} = 0 && ${line[0]} = fpr ]]; then
> + # primary fingerprint
> + imported_keys+=("${line[9]}")
> + found=1
> + fi
> + done < <(gpg --batch --list-keys --keyid-format=long --with-colons || die)
Why do you need --keyid-format? You're using fingerprints only, aren't
you?
> +
> + printf '%s\n' "${imported_keys[@]}" | sort > imported_keys.list || die
> + printf '%s\n' "${SEC_KEYS_VALIDPGPKEYS[@]%%:*}" | sort > allowed_keys.list || die
> +
> + local extra_keys=($(comm -23 imported_keys.list allowed_keys.list || die))
> + local missing_keys=($(comm -13 imported_keys.list allowed_keys.list || die))
> +
> + if [[ ${#extra_keys[@]} != 0 ]]; then
> + die "too many keys found. Suspicious keys: ${extra_keys[@]}"
The first sentence is not capitalized.
> + fi
> + if [[ ${#missing_keys[@]} != 0 ]]; then
> + die "too few keys found. Unavailable keys: ${missing_keys[@]}"
> + fi
> +}
> +
> +
> +sec-keys_src_test() {
> + local -x GNUPGHOME=${WORKDIR}/gnupg
> + local key fingerprint name server
> + local gpg_command=(gpg --export-options export-minimal)
> +
> + for fingerprint in "${SEC_KEYS_VALIDPGPKEYS[@]%%:*}"; do
> + "${gpg_command[@]}" --export "${fingerprint}" | pgpdump > "${fingerprint}.pgpdump" || die
> + done
> +
> + # Best-effort attempt to check for updates. keyservers can and usually do
> + # fail for weird reasons, (such as being unable to import a key without a
> + # uid) as well as normal reasons, like the key being exclusive to a
> + # different keyserver. this isn't a reason to fail src_test.
Well, I dare say that if refreshing against the server specified
as the reference source fails, that would count as a reason to fail.
Consider the case of someone removing a compromised key instead
of revoking it.
> + for server in keys.gentoo.org keys.openpgp.org keyserver.ubuntu.com; do
> + gpg --refresh-keys --keyserver "hkps://${server}"
> + done
> + for key in "${SEC_KEYS_VALIDPGPKEYS[@]}"; do
> + if [[ ${key##*:} = *github* ]]; then
> + name=${key#*:}; name=${name%%:*}
> + wget -qO- https://github.com/${name}.gpg | gpg --import || die
> + fi
> + done
> +
> + for fingerprint in "${SEC_KEYS_VALIDPGPKEYS[@]%%:*}"; do
> + "${gpg_command[@]}" --export "${fingerprint}" | pgpdump > "${fingerprint}.pgpdump.new" || die
> + diff -u "${fingerprint}.pgpdump" "${fingerprint}.pgpdump.new" || die "updates available for PGP key: ${fingerprint}"
> + done
> +
> +}
> +
> +# @FUNCTION: sec-keys_src_install
> +# @DESCRIPTION:
> +# Default src_install override that minifies and exports all PGP public keys
> +# into an ascii-armored keyfile installed to the standard /usr/share/openpgp-keys.
> +sec-keys_src_install() {
> + local -x GNUPGHOME=${WORKDIR}/gnupg
> + local fingerprint
> + local gpg_command=(gpg --no-permission-warning --export-options export-minimal)
> +
> + for fingerprint in "${SEC_KEYS_VALIDPGPKEYS[@]%%:*}"; do
> + local uids=()
> + mapfile -t uids < <("${gpg_command[@]}" --list-key --with-colons ${fingerprint} | awk -F: '/^uid/{print $10}' || die)
> + edo "${gpg_command[@]}" "${uids[@]/#/--comment=}" --export --armor "${fingerprint}" >> ${PN#openpgp-keys-}.asc
> + done
That looks like something you could do in src_compile() already.
Also, I'm confused by the purpose of this whole logic. After all, you
have already verified that there are no stray keys in the keyring,
right? So why not just export the whole thing?
--
Best regards,
Michał Górny
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 512 bytes --]
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [gentoo-dev] [PATCH v2 1/2] sec-keys.eclass: new eclass
2024-11-28 13:10 ` Michał Górny
@ 2024-11-28 15:36 ` Eli Schwartz
2024-11-28 16:42 ` Michał Górny
` (2 more replies)
0 siblings, 3 replies; 21+ messages in thread
From: Eli Schwartz @ 2024-11-28 15:36 UTC (permalink / raw
To: gentoo-dev
[-- Attachment #1.1: Type: text/plain, Size: 7064 bytes --]
On 11/28/24 8:10 AM, Michał Górny wrote:
> On Wed, 2024-11-27 at 23:32 -0500, Eli Schwartz wrote:
>> +# @ECLASS_VARIABLE: SEC_KEYS_VALIDPGPKEYS
>> +# @PRE_INHERIT
>> +# @DEFAULT_UNSET
>> +# @DESCRIPTION:
>> +# Mapping of fingerprints, name, and optional location of PGP keys to include,
>
> So "location" or "locations", plural?
Fixed.
>> +# separated by colons. The allowed values for a location are:
>> +#
>> +# - gentoo -- fetch key by fingerprint from https://keys.gentoo.org
>> +#
>> +# - github -- fetch key from github.com/${name}.pgp
>> +#
>> +# - openpgp -- fetch key by fingerprint from https://keys.openpgp.org
>> +#
>> +# - ubuntu -- fetch key by fingerprint from http://keyserver.ubuntu.com (the default)
>
> I'd go without a default. Typing 6 more letters doesn't cost anything,
> and makes the contents more consistent. Also saves us from regretting
> having chosen a bad default in the future.
>
>> +#
>> +# - none -- do not add to SRC_URI, the ebuild will provide a custom download location
>
> Perhaps "manual"? "None" sounds like there would be no key at all.
Maybe I could just document as a recommendation to use ubuntu. Other
sources are likely to be extremely unreliable, unfortunately.
openpgp.org only works if the key owner manually verifies their email,
for example.
>> + case ${loc} in
>> + gentoo) remote="https://keys.gentoo.org/pks/lookup?op=get&search=0x${fingerprint}";;
>> + github) remote="https://github.com/${name}.gpg";;
>> + openpgp) remote="https://keys.openpgp.org/vks/v1/by-fingerprint/${fingerprint}";;
>> + ubuntu) remote="https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x${fingerprint}";;
>> + # provided via manual SRC_URI
>> + none) continue;;
>> + *) die "${ECLASS}: unknown PGP key remote: ${loc}";;
>> +
>
> Stray empty line.
Fixed.
>> +S=${WORKDIR}
>> +
>> +LICENSE="public-domain"
>> +SLOT="0"
>
> Please keep ebuildy variables in the standard/skel order, or at least
> as close to it as you can get.
Fixed.
>> +# @FUNCTION: sec-keys_src_compile
>> +# @DESCRIPTION:
>> +# Default src_compile override that imports all public keys into a keyring,
>> +# and validates that they are listed in SEC_KEYS_VALIDPGPKEYS.
>> +sec-keys_src_compile() {
>> + local -x GNUPGHOME=${WORKDIR}/gnupg
>> + mkdir -m700 -p "${GNUPGHOME}" || die
>> +
>> + pushd "${DISTDIR}" >/dev/null || die
>> + gpg --import ${A} || die
>> + popd >/dev/null || die
>> +
>> + local line imported_keys=() found=0
>> + while IFS=: read -r -a line; do
>> + if [[ ${line[0]} = pub ]]; then
>
> Please use '==' in ebuilds and eclasses.
...
>> + # new key
>> + found=0
>> + elif [[ ${found} = 0 && ${line[0]} = fpr ]]; then
>> + # primary fingerprint
>> + imported_keys+=("${line[9]}")
>> + found=1
>> + fi
>> + done < <(gpg --batch --list-keys --keyid-format=long --with-colons || die)
>
> Why do you need --keyid-format? You're using fingerprints only, aren't
> you?
I'm used to it mattering in various contexts and added it instinctively.
You're right, it doesn't do anything here.
>> +
>> + printf '%s\n' "${imported_keys[@]}" | sort > imported_keys.list || die
>> + printf '%s\n' "${SEC_KEYS_VALIDPGPKEYS[@]%%:*}" | sort > allowed_keys.list || die
>> +
>> + local extra_keys=($(comm -23 imported_keys.list allowed_keys.list || die))
>> + local missing_keys=($(comm -13 imported_keys.list allowed_keys.list || die))
>> +
>> + if [[ ${#extra_keys[@]} != 0 ]]; then
>> + die "too many keys found. Suspicious keys: ${extra_keys[@]}"
>
> The first sentence is not capitalized.
Fixed.
>> + fi
>> + if [[ ${#missing_keys[@]} != 0 ]]; then
>> + die "too few keys found. Unavailable keys: ${missing_keys[@]}"
>> + fi
>> +}
>> +
>> +
>> +sec-keys_src_test() {
>> + local -x GNUPGHOME=${WORKDIR}/gnupg
>> + local key fingerprint name server
>> + local gpg_command=(gpg --export-options export-minimal)
>> +
>> + for fingerprint in "${SEC_KEYS_VALIDPGPKEYS[@]%%:*}"; do
>> + "${gpg_command[@]}" --export "${fingerprint}" | pgpdump > "${fingerprint}.pgpdump" || die
>> + done
>> +
>> + # Best-effort attempt to check for updates. keyservers can and usually do
>> + # fail for weird reasons, (such as being unable to import a key without a
>> + # uid) as well as normal reasons, like the key being exclusive to a
>> + # different keyserver. this isn't a reason to fail src_test.
>
> Well, I dare say that if refreshing against the server specified
> as the reference source fails, that would count as a reason to fail.
> Consider the case of someone removing a compromised key instead
> of revoking it.
This doesn't test a useful property.
People cannot "remove" compromised keys from a keyserver to begin with.
If they did, then checking to build the package with GENTOO_MIRRORS=
DISTDIR=$(mktemp -d) is a significantly more useful test.
Removing a key for whatever reason, doesn't tell you why it was removed,
or even who removed it. It is also not how the PGP standard says you are
supposed to handle a *compromised* key. It's not like GnuPG will delete
keys from your keyring if the server doesn't possess it anymore... there
is actually no such thing as a user of PGP that would be correctly
served by someone removing a compromised key in the hopes that those
users would interpret it as an indicator of compromise.
In exchange for no assurances whatsoever, the code would become a lot
more complicated. As I already said, gpg exiting with something other
than 0 for success can mean many things and there's no good way to
figure out what it did in fact mean.
I'm going to need a better argument if you want me to change this.
>> +sec-keys_src_install() {
>> + local -x GNUPGHOME=${WORKDIR}/gnupg
>> + local fingerprint
>> + local gpg_command=(gpg --no-permission-warning --export-options export-minimal)
>> +
>> + for fingerprint in "${SEC_KEYS_VALIDPGPKEYS[@]%%:*}"; do
>> + local uids=()
>> + mapfile -t uids < <("${gpg_command[@]}" --list-key --with-colons ${fingerprint} | awk -F: '/^uid/{print $10}' || die)
>> + edo "${gpg_command[@]}" "${uids[@]/#/--comment=}" --export --armor "${fingerprint}" >> ${PN#openpgp-keys-}.asc
>> + done
>
> That looks like something you could do in src_compile() already.
Perhaps. But it felt like exporting keys is work that is conceptually
part of installing, in much the way that running a meson project's
`meson install` step does more than just copy files into ${D} -- it also
processes those files in order to do things like patch the rpath.
I guess I am not too attached to either approach.
> Also, I'm confused by the purpose of this whole logic. After all, you
> have already verified that there are no stray keys in the keyring,
> right? So why not just export the whole thing?
Because this is doing additional steps that aren't just exporting the
whole thing?
--
Eli Schwartz
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 236 bytes --]
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [gentoo-dev] [PATCH 1/2] sec-keys.eclass: new eclass
2024-11-28 10:35 ` [gentoo-dev] [PATCH 1/2] sec-keys.eclass: new eclass Ulrich Müller
@ 2024-11-28 15:36 ` Eli Schwartz
0 siblings, 0 replies; 21+ messages in thread
From: Eli Schwartz @ 2024-11-28 15:36 UTC (permalink / raw
To: gentoo-dev
[-- Attachment #1.1: Type: text/plain, Size: 2526 bytes --]
On 11/28/24 5:35 AM, Ulrich Müller wrote:
>>>>>> On Wed, 27 Nov 2024, Eli Schwartz wrote:
>
>> --- /dev/null
>> +++ b/eclass/sec-keys.eclass
>> @@ -0,0 +1,150 @@
>> +# Copyright 2024 Gentoo Authors
>> +# Distributed under the terms of the GNU General Public License v2
>> +
>> +# @ECLASS: sec-keys.eclass
>> +# @MAINTAINER:
>> +# Eli Schwartz <eschwartz@gentoo.org>
>> +# @AUTHOR:
>> +# Eli Schwartz <eschwartz@gentoo.org>
>> +# @SUPPORTED_EAPIS: 8
>> +# @BLURB: Provides a uniform way of handling ebuilds which package PGP key material
>> +# @DESCRIPTION:
>> +# This eclass provides a streamlined approach to finding suitable source material
>> +# for OpenPGP keys used by the verify-sig eclass. Its primary purpose is to permit
>> +# developers to easily and securely package new sec-keys/* packages. The eclass
>> +# removes the risk of developers accidentally packaging malformed key material, or
>> +# neglecting to notice when PGP identities have changed.
>> +#
>> +# To use the eclass, define SEC_KEYS_VALIDPGPKEYS to contain the fingerprint of
>> +# the key and the short name of the key's owner.
>
> Please wrap these comment lines to a line length of 70-ish characters
> for readability.
>
> Also, there should be two spaces after every full stop (except when it's
> followed by a newline), so groff can recognise the sentence end in the
> generated man page.
I usually do 80-ish for readability! Okay, I can do 70 too. :) Thanks
for the tip about the spaces, I don't usually write groff by hand.
Surprising that groff cannot handle this automatically, though.
>> +_sec_keys_set_globals() {
>> + if [[ ${SEC_KEYS_VALIDPGPKEYS[*]} ]]; then
>
> Why is the if needed? If the array is empty, the following for loop
> won't execute.
Not sure, perhaps an artifact of a previous revision that had different
handling. Let's remove it.
>> + printf '%s\n' "${imported_keys[@]}" | sort > imported_keys.list || die
>> + printf '%s\n' "${SEC_KEYS_VALIDPGPKEYS[@]%%:*}" | sort > allowed_keys.list || die
>
> Maybe create these files in ${T} instead?
I'm not sure this is an important distinction. It's the main thing the
package works on. I could put GNUPGHOME in ${T} as well, if you like? :)
But keeping it in ${WORKDIR} makes it more straightforward for people to
look at manually when a failed build happens. And that's important when
dealing with the primary logic of a package (there's no source code to
compile here).
--
Eli Schwartz
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 236 bytes --]
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [gentoo-dev] [PATCH v2 1/2] sec-keys.eclass: new eclass
2024-11-28 15:36 ` Eli Schwartz
@ 2024-11-28 16:42 ` Michał Górny
2024-11-28 16:56 ` Sam James
2024-11-29 18:31 ` Robin H. Johnson
2 siblings, 0 replies; 21+ messages in thread
From: Michał Górny @ 2024-11-28 16:42 UTC (permalink / raw
To: gentoo-dev
[-- Attachment #1: Type: text/plain, Size: 4891 bytes --]
On Thu, 2024-11-28 at 10:36 -0500, Eli Schwartz wrote:
> On 11/28/24 8:10 AM, Michał Górny wrote:
>
> > > +# separated by colons. The allowed values for a location are:
> > > +#
> > > +# - gentoo -- fetch key by fingerprint from https://keys.gentoo.org
> > > +#
> > > +# - github -- fetch key from github.com/${name}.pgp
> > > +#
> > > +# - openpgp -- fetch key by fingerprint from https://keys.openpgp.org
> > > +#
> > > +# - ubuntu -- fetch key by fingerprint from http://keyserver.ubuntu.com (the default)
> >
> > I'd go without a default. Typing 6 more letters doesn't cost anything,
> > and makes the contents more consistent. Also saves us from regretting
> > having chosen a bad default in the future.
> >
> > > +#
> > > +# - none -- do not add to SRC_URI, the ebuild will provide a custom download location
> >
> > Perhaps "manual"? "None" sounds like there would be no key at all.
>
>
> Maybe I could just document as a recommendation to use ubuntu. Other
> sources are likely to be extremely unreliable, unfortunately.
> openpgp.org only works if the key owner manually verifies their email,
> for example.
Yeah. I don't see why anyone would specify a non-working location (such
as openpgp.org).
> > > + # new key
> > > + found=0
> > > + elif [[ ${found} = 0 && ${line[0]} = fpr ]]; then
> > > + # primary fingerprint
> > > + imported_keys+=("${line[9]}")
> > > + found=1
> > > + fi
> > > + done < <(gpg --batch --list-keys --keyid-format=long --with-colons || die)
> >
> > Why do you need --keyid-format? You're using fingerprints only, aren't
> > you?
>
>
> I'm used to it mattering in various contexts and added it instinctively.
> You're right, it doesn't do anything here.
Yeah. And for a minute, it made me worry that you're using long ids
instead of fingerprints.
> > > + fi
> > > + if [[ ${#missing_keys[@]} != 0 ]]; then
> > > + die "too few keys found. Unavailable keys: ${missing_keys[@]}"
> > > + fi
> > > +}
> > > +
> > > +
> > > +sec-keys_src_test() {
> > > + local -x GNUPGHOME=${WORKDIR}/gnupg
> > > + local key fingerprint name server
> > > + local gpg_command=(gpg --export-options export-minimal)
> > > +
> > > + for fingerprint in "${SEC_KEYS_VALIDPGPKEYS[@]%%:*}"; do
> > > + "${gpg_command[@]}" --export "${fingerprint}" | pgpdump > "${fingerprint}.pgpdump" || die
> > > + done
> > > +
> > > + # Best-effort attempt to check for updates. keyservers can and usually do
> > > + # fail for weird reasons, (such as being unable to import a key without a
> > > + # uid) as well as normal reasons, like the key being exclusive to a
> > > + # different keyserver. this isn't a reason to fail src_test.
> >
> > Well, I dare say that if refreshing against the server specified
> > as the reference source fails, that would count as a reason to fail.
> > Consider the case of someone removing a compromised key instead
> > of revoking it.
>
>
> This doesn't test a useful property.
>
> People cannot "remove" compromised keys from a keyserver to begin with.
> If they did, then checking to build the package with GENTOO_MIRRORS=
> DISTDIR=$(mktemp -d) is a significantly more useful test.
In fact, this is the kind of test I was originally thinking of -- i.e.
literally refetching the file manually and seeing if the minimal export
differs...
> > > +sec-keys_src_install() {
> > > + local -x GNUPGHOME=${WORKDIR}/gnupg
> > > + local fingerprint
> > > + local gpg_command=(gpg --no-permission-warning --export-options export-minimal)
> > > +
> > > + for fingerprint in "${SEC_KEYS_VALIDPGPKEYS[@]%%:*}"; do
> > > + local uids=()
> > > + mapfile -t uids < <("${gpg_command[@]}" --list-key --with-colons ${fingerprint} | awk -F: '/^uid/{print $10}' || die)
> > > + edo "${gpg_command[@]}" "${uids[@]/#/--comment=}" --export --armor "${fingerprint}" >> ${PN#openpgp-keys-}.asc
> > > + done
> >
> > That looks like something you could do in src_compile() already.
>
>
> Perhaps. But it felt like exporting keys is work that is conceptually
> part of installing, in much the way that running a meson project's
> `meson install` step does more than just copy files into ${D} -- it also
> processes those files in order to do things like patch the rpath.
>
> I guess I am not too attached to either approach.
>
>
> > Also, I'm confused by the purpose of this whole logic. After all, you
> > have already verified that there are no stray keys in the keyring,
> > right? So why not just export the whole thing?
>
>
> Because this is doing additional steps that aren't just exporting the
> whole thing?
>
Then perhaps you should add an explanatory comment instead of expecting
people to catch that from a command so long it doesn't fit on people's
screens.
--
Best regards,
Michał Górny
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 512 bytes --]
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [gentoo-dev] [PATCH v2 1/2] sec-keys.eclass: new eclass
2024-11-28 15:36 ` Eli Schwartz
2024-11-28 16:42 ` Michał Górny
@ 2024-11-28 16:56 ` Sam James
2024-11-28 17:06 ` Michał Górny
2024-11-29 18:31 ` Robin H. Johnson
2 siblings, 1 reply; 21+ messages in thread
From: Sam James @ 2024-11-28 16:56 UTC (permalink / raw
To: Eli Schwartz; +Cc: gentoo-dev
Eli Schwartz <eschwartz@gentoo.org> writes:
> On 11/28/24 8:10 AM, Michał Górny wrote:
>> On Wed, 2024-11-27 at 23:32 -0500, Eli Schwartz wrote:
>>
>> That looks like something you could do in src_compile() already.
>
>
> Perhaps. But it felt like exporting keys is work that is conceptually
> part of installing, in much the way that running a meson project's
> `meson install` step does more than just copy files into ${D} -- it also
> processes those files in order to do things like patch the rpath.
>
> I guess I am not too attached to either approach.
I think I very loosely prefer doing it in src_compile as it's
"compilation" of the resulting files and then src_install just moving
the bundle, but I hardly feel strongly about it at all and feel it's
personal preference. Not worth the electrons ;)
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [gentoo-dev] [PATCH v2 1/2] sec-keys.eclass: new eclass
2024-11-28 16:56 ` Sam James
@ 2024-11-28 17:06 ` Michał Górny
2024-11-28 17:22 ` Sam James
0 siblings, 1 reply; 21+ messages in thread
From: Michał Górny @ 2024-11-28 17:06 UTC (permalink / raw
To: gentoo-dev, Eli Schwartz
[-- Attachment #1: Type: text/plain, Size: 1191 bytes --]
On Thu, 2024-11-28 at 16:56 +0000, Sam James wrote:
> Eli Schwartz <eschwartz@gentoo.org> writes:
>
> > On 11/28/24 8:10 AM, Michał Górny wrote:
> > > On Wed, 2024-11-27 at 23:32 -0500, Eli Schwartz wrote:
> > >
> > > That looks like something you could do in src_compile() already.
> >
> >
> > Perhaps. But it felt like exporting keys is work that is conceptually
> > part of installing, in much the way that running a meson project's
> > `meson install` step does more than just copy files into ${D} -- it also
> > processes those files in order to do things like patch the rpath.
> >
> > I guess I am not too attached to either approach.
>
> I think I very loosely prefer doing it in src_compile as it's
> "compilation" of the resulting files and then src_install just moving
> the bundle, but I hardly feel strongly about it at all and feel it's
> personal preference. Not worth the electrons ;)
My point was mostly that you wouldn't have to start with GNUPGHOME
again. Also, tests might be able to reuse the result then. Also, tests
wouldn't be able to mess up src_install() (think of test-fail-continue
too).
--
Best regards,
Michał Górny
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 512 bytes --]
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [gentoo-dev] [PATCH v2 1/2] sec-keys.eclass: new eclass
2024-11-28 17:06 ` Michał Górny
@ 2024-11-28 17:22 ` Sam James
0 siblings, 0 replies; 21+ messages in thread
From: Sam James @ 2024-11-28 17:22 UTC (permalink / raw
To: Michał Górny; +Cc: gentoo-dev, Eli Schwartz
Michał Górny <mgorny@gentoo.org> writes:
> On Thu, 2024-11-28 at 16:56 +0000, Sam James wrote:
>> Eli Schwartz <eschwartz@gentoo.org> writes:
>>
>> > On 11/28/24 8:10 AM, Michał Górny wrote:
>> > > On Wed, 2024-11-27 at 23:32 -0500, Eli Schwartz wrote:
>> > >
>> > > That looks like something you could do in src_compile() already.
>> >
>> >
>> > Perhaps. But it felt like exporting keys is work that is conceptually
>> > part of installing, in much the way that running a meson project's
>> > `meson install` step does more than just copy files into ${D} -- it also
>> > processes those files in order to do things like patch the rpath.
>> >
>> > I guess I am not too attached to either approach.
>>
>> I think I very loosely prefer doing it in src_compile as it's
>> "compilation" of the resulting files and then src_install just moving
>> the bundle, but I hardly feel strongly about it at all and feel it's
>> personal preference. Not worth the electrons ;)
>
> My point was mostly that you wouldn't have to start with GNUPGHOME
> again. Also, tests might be able to reuse the result then. Also, tests
> wouldn't be able to mess up src_install() (think of test-fail-continue
> too).
Ah, test-fail-continue is a good point.
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [gentoo-dev] [PATCH v2 1/2] sec-keys.eclass: new eclass
2024-11-28 4:32 ` [gentoo-dev] [PATCH v2 1/2] sec-keys.eclass: new eclass Eli Schwartz
2024-11-28 13:10 ` Michał Górny
@ 2024-11-29 7:30 ` Florian Schmaus
1 sibling, 0 replies; 21+ messages in thread
From: Florian Schmaus @ 2024-11-29 7:30 UTC (permalink / raw
To: gentoo-dev
[-- Attachment #1.1.1: Type: text/plain, Size: 8906 bytes --]
On 28/11/2024 05.32, Eli Schwartz wrote:
> The current state of verify-sig support is a bit awkward. We rely on
> validating distfiles against a known trusted keyring, but creating the
> known trusted keyring is basically all manual verification. We somehow
> decide an ascii armored key is good enough without any portage
> assistance, then arrange to download it and trust it by Manifest hash.
> How do we know when updating a key is actually safe?
>
> This eclass handles the problem in a manner inspired in part by pacman.
> We require an eclass variable that lists all permitted PGP fingerprints,
> and the eclass is responsible checking that list against the keys we
> will install. It comes with a mechanism for computing SRC_URI for a
> couple of well known locations, or you can append your own in the
> ebuild.
>
> Key rotations, both expected and malicious, are easily detected by
> checking the git log for changes to declared fingerprints in a bump. The
> former can be rationalized in the commit message. So can the latter, but
> in most cases those will be rejected during peer review.
>
> Signed-off-by: Eli Schwartz <eschwartz@gentoo.org>
> ---
> eclass/sec-keys.eclass | 197 +++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 197 insertions(+)
> create mode 100644 eclass/sec-keys.eclass
>
> diff --git a/eclass/sec-keys.eclass b/eclass/sec-keys.eclass
> new file mode 100644
> index 000000000000..7ea4d34a8c1c
> --- /dev/null
> +++ b/eclass/sec-keys.eclass
> @@ -0,0 +1,197 @@
> +# Copyright 2024 Gentoo Authors
> +# Distributed under the terms of the GNU General Public License v2
> +
> +# @ECLASS: sec-keys.eclass
> +# @MAINTAINER:
> +# Eli Schwartz <eschwartz@gentoo.org>
> +# @AUTHOR:
> +# Eli Schwartz <eschwartz@gentoo.org>
> +# @SUPPORTED_EAPIS: 8
> +# @BLURB: Provides a uniform way of handling ebuilds which package PGP key material
> +# @DESCRIPTION:
> +# This eclass provides a streamlined approach to finding suitable source material
> +# for OpenPGP keys used by the verify-sig eclass. Its primary purpose is to permit
> +# developers to easily and securely package new sec-keys/* packages. The eclass
> +# removes the risk of developers accidentally packaging malformed key material, or
> +# neglecting to notice when PGP identities have changed.
> +#
> +# To use the eclass, define SEC_KEYS_VALIDPGPKEYS to contain the fingerprint of
> +# the key and the short name of the key's owner.
> +#
> +# @EXAMPLE:
> +# Example use:
> +#
> +# @CODE
> +# SEC_KEYS_VALIDPGPKEYS=(
> +# # implicit Ubuntu
> +# '3DB7F3CA6C1D90B99FE25B38D4B476A4D175C54F:bjones:'
> +# '4EC8A4DB7D2E01C00AF36C49E5C587B5E286C65A:jsmith:github,openpgp'
> +# # key only available on personal website, use manual SRC_URI
> +# '5FD9B5EC8E3F12D11BA47D50F6D698C6F397D76B:awhite:none'
> +# )
> +#
> +# inherit sec-keys
> +#
> +# SRC_URI+="https://awhite.com/awhite.gpg -> awhite-${PV}.gpg"
> +# @CODE
> +
> +case ${EAPI} in
> + 8) ;;
> + *) die "${ECLASS}: EAPI ${EAPI:-0} not supported" ;;
> +esac
> +
> +if [[ ! ${_SEC_KEYS_ECLASS} ]]; then
> +_SEC_KEYS_ECLASS=1
> +
> +inherit edo
> +
> +# @ECLASS_VARIABLE: SEC_KEYS_VALIDPGPKEYS
> +# @PRE_INHERIT
> +# @DEFAULT_UNSET
> +# @DESCRIPTION:
> +# Mapping of fingerprints, name, and optional location of PGP keys to include,
> +# separated by colons. The allowed values for a location are:
> +#
> +# - gentoo -- fetch key by fingerprint from https://keys.gentoo.org
> +#
> +# - github -- fetch key from github.com/${name}.pgp
> +#
> +# - openpgp -- fetch key by fingerprint from https://keys.openpgp.org
> +#
> +# - ubuntu -- fetch key by fingerprint from http://keyserver.ubuntu.com (the default)
> +#
> +# - none -- do not add to SRC_URI, the ebuild will provide a custom download location
> +_sec_keys_set_globals() {
> + if [[ ${SEC_KEYS_VALIDPGPKEYS[*]} ]]; then
The function's body is already pretty wide, therefore maybe consider
using an early return here.
> + local key fingerprint name loc locations=() remote
> + for key in "${SEC_KEYS_VALIDPGPKEYS[@]}"; do
> + fingerprint=${key%%:*}
> + name=${key#${fingerprint}:}; name=${name%%:*}
> + IFS=, read -r -a locations <<<"${key##*:}"
> + [[ ${locations[@]} ]] || locations=(ubuntu)
> + for loc in "${locations[@]}"; do
> + case ${loc} in
> + gentoo) remote="https://keys.gentoo.org/pks/lookup?op=get&search=0x${fingerprint}";;
> + github) remote="https://github.com/${name}.gpg";;
> + openpgp) remote="https://keys.openpgp.org/vks/v1/by-fingerprint/${fingerprint}";;
> + ubuntu) remote="https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x${fingerprint}";;
> + # provided via manual SRC_URI
> + none) continue;;
> + *) die "${ECLASS}: unknown PGP key remote: ${loc}";;
> +
> + esac
> + SRC_URI+="
> + ${remote} -> openpgp-keys-${name}-${loc}-${PV}.asc
> + "
> + done
> + done
> + fi
> +}
> +_sec_keys_set_globals
> +unset -f _sec_keys_set_globals
> +
> +IUSE="test"
> +PROPERTIES="test_network"
> +RESTRICT="test"
> +
> +BDEPEND="
> + app-crypt/gnupg
> + test? ( app-crypt/pgpdump )
> +"
> +S=${WORKDIR}
> +
> +LICENSE="public-domain"
> +SLOT="0"
> +
> +
> +# @FUNCTION: sec-keys_src_compile
> +# @DESCRIPTION:
> +# Default src_compile override that imports all public keys into a keyring,
> +# and validates that they are listed in SEC_KEYS_VALIDPGPKEYS.
> +sec-keys_src_compile() {
> + local -x GNUPGHOME=${WORKDIR}/gnupg
> + mkdir -m700 -p "${GNUPGHOME}" || die
> +
> + pushd "${DISTDIR}" >/dev/null || die
> + gpg --import ${A} || die
> + popd >/dev/null || die
> +
> + local line imported_keys=() found=0
> + while IFS=: read -r -a line; do
> + if [[ ${line[0]} = pub ]]; then
> + # new key
> + found=0
> + elif [[ ${found} = 0 && ${line[0]} = fpr ]]; then
> + # primary fingerprint
> + imported_keys+=("${line[9]}")
> + found=1
> + fi
> + done < <(gpg --batch --list-keys --keyid-format=long --with-colons || die)
> +
> + printf '%s\n' "${imported_keys[@]}" | sort > imported_keys.list || die
> + printf '%s\n' "${SEC_KEYS_VALIDPGPKEYS[@]%%:*}" | sort > allowed_keys.list || die
> +
> + local extra_keys=($(comm -23 imported_keys.list allowed_keys.list || die))
> + local missing_keys=($(comm -13 imported_keys.list allowed_keys.list || die))
> +
> + if [[ ${#extra_keys[@]} != 0 ]]; then
> + die "too many keys found. Suspicious keys: ${extra_keys[@]}"
> + fi
> + if [[ ${#missing_keys[@]} != 0 ]]; then
> + die "too few keys found. Unavailable keys: ${missing_keys[@]}"
> + fi
> +}
> +
> +
> +sec-keys_src_test() {
> + local -x GNUPGHOME=${WORKDIR}/gnupg
> + local key fingerprint name server
> + local gpg_command=(gpg --export-options export-minimal)
> +
> + for fingerprint in "${SEC_KEYS_VALIDPGPKEYS[@]%%:*}"; do
> + "${gpg_command[@]}" --export "${fingerprint}" | pgpdump > "${fingerprint}.pgpdump" || die
> + done
> +
> + # Best-effort attempt to check for updates. keyservers can and usually do
> + # fail for weird reasons, (such as being unable to import a key without a
> + # uid) as well as normal reasons, like the key being exclusive to a
> + # different keyserver. this isn't a reason to fail src_test.
> + for server in keys.gentoo.org keys.openpgp.org keyserver.ubuntu.com; do
> + gpg --refresh-keys --keyserver "hkps://${server}"
> + done
> + for key in "${SEC_KEYS_VALIDPGPKEYS[@]}"; do
> + if [[ ${key##*:} = *github* ]]; then
> + name=${key#*:}; name=${name%%:*}
> + wget -qO- https://github.com/${name}.gpg | gpg --import || die
> + fi
> + done
> +
> + for fingerprint in "${SEC_KEYS_VALIDPGPKEYS[@]%%:*}"; do
> + "${gpg_command[@]}" --export "${fingerprint}" | pgpdump > "${fingerprint}.pgpdump.new" || die
> + diff -u "${fingerprint}.pgpdump" "${fingerprint}.pgpdump.new" || die "updates available for PGP key: ${fingerprint}"
> + done
> +
> +}
> +
> +# @FUNCTION: sec-keys_src_install
> +# @DESCRIPTION:
> +# Default src_install override that minifies and exports all PGP public keys
> +# into an ascii-armored keyfile installed to the standard /usr/share/openpgp-keys.
> +sec-keys_src_install() {
> + local -x GNUPGHOME=${WORKDIR}/gnupg
> + local fingerprint
> + local gpg_command=(gpg --no-permission-warning --export-options export-minimal)
> +
> + for fingerprint in "${SEC_KEYS_VALIDPGPKEYS[@]%%:*}"; do
> + local uids=()
> + mapfile -t uids < <("${gpg_command[@]}" --list-key --with-colons ${fingerprint} | awk -F: '/^uid/{print $10}' || die)
> + edo "${gpg_command[@]}" "${uids[@]/#/--comment=}" --export --armor "${fingerprint}" >> ${PN#openpgp-keys-}.asc
> + done
> +
> + insinto /usr/share/openpgp-keys
> + doins ${PN#openpgp-keys-}.asc
> +}
> +
> +fi
> +
> +EXPORT_FUNCTIONS src_compile src_test src_install
[-- Attachment #1.1.2: OpenPGP public key --]
[-- Type: application/pgp-keys, Size: 21567 bytes --]
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 618 bytes --]
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [gentoo-dev] [PATCH v2 1/2] sec-keys.eclass: new eclass
2024-11-28 15:36 ` Eli Schwartz
2024-11-28 16:42 ` Michał Górny
2024-11-28 16:56 ` Sam James
@ 2024-11-29 18:31 ` Robin H. Johnson
2024-11-29 19:02 ` Eli Schwartz
2 siblings, 1 reply; 21+ messages in thread
From: Robin H. Johnson @ 2024-11-29 18:31 UTC (permalink / raw
To: gentoo-dev
[-- Attachment #1: Type: text/plain, Size: 1755 bytes --]
On Thu, Nov 28, 2024 at 10:36:36AM -0500, Eli Schwartz wrote:
> This doesn't test a useful property.
>
> People cannot "remove" compromised keys from a keyserver to begin with.
> If they did, then checking to build the package with GENTOO_MIRRORS=
> DISTDIR=$(mktemp -d) is a significantly more useful test.
From a technical perspective, that depends on the keyserver design.
But the canonical "why" is GDPR Article 17 - right-to-erasure.
Hockeypuck even ships a script to make it easy for admins to delete
keys:
https://github.com/hockeypuck/hockeypuck/blob/5cc0fffe46f44986cbf78a554ab482e3baaa5143/contrib/docker-compose/standalone/README.md?plain=1#L177-L190
There is another more obvious reason why a key might vanish from a
keyserver: ephemeral & eventually consistent state
The SKS server implementation is sufficiently unreliable** for
keys.gentoo.org that one node occasionally corrupts it's database, and I
have a script that rebuilds it. If a key is uploaded to a node, and NOT
yet propagated to other nodes before the corruption event, this could
lead to the appearance of a key being removed.
The SKS network, when it still ran, also provided an eventually
consistent behaviour, such that a series of rapid queries to the DNS
rotation might not always return the same data for a given key if
changes to that key were in flight.
** Yes, one of the gentoo nodes is running Hockeypuck now, and I hope to
replace all of SKS with Hockeypuck in future, but it's not quite the
same yet.
--
Robin Hugh Johnson
Gentoo Linux: Dev, Infra Lead, Foundation Treasurer
E-Mail : robbat2@gentoo.org
GnuPG FP : 11ACBA4F 4778E3F6 E4EDF38E B27B944E 34884E85
GnuPG FP : 7D0B3CEB E9B85B1F 825BCECF EE05E6F6 A48F6136
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 1113 bytes --]
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [gentoo-dev] [PATCH v2 1/2] sec-keys.eclass: new eclass
2024-11-29 18:31 ` Robin H. Johnson
@ 2024-11-29 19:02 ` Eli Schwartz
0 siblings, 0 replies; 21+ messages in thread
From: Eli Schwartz @ 2024-11-29 19:02 UTC (permalink / raw
To: gentoo-dev
[-- Attachment #1.1: Type: text/plain, Size: 1461 bytes --]
On 11/29/24 1:31 PM, Robin H. Johnson wrote:
> From a technical perspective, that depends on the keyserver design.
>
> But the canonical "why" is GDPR Article 17 - right-to-erasure.
>
> Hockeypuck even ships a script to make it easy for admins to delete
> keys:
> https://github.com/hockeypuck/hockeypuck/blob/5cc0fffe46f44986cbf78a554ab482e3baaa5143/contrib/docker-compose/standalone/README.md?plain=1#L177-L190
>
> There is another more obvious reason why a key might vanish from a
> keyserver: ephemeral & eventually consistent state
Indeed, but that just reinforces my point that this doesn't represent a
failing test. :)
GDPR argumentation aside and practicalities alone, PGP keys for
developers of software packaged in linux distributions *cannot* be
forgotten, period, since they exist in tons of places including
committed to git as *.asc files in multiple distros' package sources.
And user-requested deletion would anyways not be a test failure as the
package is plainly fine and can continue to be verified. It's possible
for us to be independently asked to make a commit that removes the key
in question from ::gentoo, but that's a separate story.
Ephemeral state is an even greater indication of why refreshes should be
expected to fail without failing the test :) since an ephemeral state
that is not yet consistent does not mean the key has disappeared or that
it needs to be updated.
--
Eli Schwartz
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 236 bytes --]
^ permalink raw reply [flat|nested] 21+ messages in thread
end of thread, other threads:[~2024-11-29 19:02 UTC | newest]
Thread overview: 21+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-11-27 20:30 [gentoo-dev] [PATCH 1/2] sec-keys.eclass: new eclass Eli Schwartz
2024-11-27 20:30 ` [gentoo-dev] [PATCH 2/2] sec-keys/openpgp-keys-gnutls: update to use sec-keys.eclass Eli Schwartz
2024-11-27 21:12 ` [gentoo-dev] [PATCH 1/2] sec-keys.eclass: new eclass Michał Górny
2024-11-27 21:52 ` Sam James
2024-11-28 4:24 ` Eli Schwartz
2024-11-27 21:57 ` Sam James
2024-11-28 4:17 ` Eli Schwartz
2024-11-28 4:32 ` [gentoo-dev] [PATCH v2 0/2] sec-keys.eclass Eli Schwartz
2024-11-28 4:32 ` [gentoo-dev] [PATCH v2 1/2] sec-keys.eclass: new eclass Eli Schwartz
2024-11-28 13:10 ` Michał Górny
2024-11-28 15:36 ` Eli Schwartz
2024-11-28 16:42 ` Michał Górny
2024-11-28 16:56 ` Sam James
2024-11-28 17:06 ` Michał Górny
2024-11-28 17:22 ` Sam James
2024-11-29 18:31 ` Robin H. Johnson
2024-11-29 19:02 ` Eli Schwartz
2024-11-29 7:30 ` Florian Schmaus
2024-11-28 4:32 ` [gentoo-dev] [PATCH v2 2/2] sec-keys/openpgp-keys-gnutls: update to use sec-keys.eclass Eli Schwartz
2024-11-28 10:35 ` [gentoo-dev] [PATCH 1/2] sec-keys.eclass: new eclass Ulrich Müller
2024-11-28 15:36 ` Eli Schwartz
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox