From 4c88545e9f38b9ce2d6329019a8279afa8be8020 Mon Sep 17 00:00:00 2001 From: Aleksa Sarai Date: Mon, 9 Feb 2026 10:46:55 +1100 Subject: [PATCH 1/3] MAINTAINERS: add maintainers file Signed-off-by: Aleksa Sarai --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) create mode 100644 MAINTAINERS diff --git a/MAINTAINERS b/MAINTAINERS new file mode 100644 index 00000000..1241335d --- /dev/null +++ b/MAINTAINERS @@ -0,0 +1 @@ +Aleksa Sarai (@cyphar) From 2bc07ecdfdf08c3d9c3c1378b562a1e71eda116e Mon Sep 17 00:00:00 2001 From: Aleksa Sarai Date: Mon, 9 Feb 2026 10:47:36 +1100 Subject: [PATCH 2/3] keyring: add keyring file and management scripts The management scripts come from umoci. Signed-off-by: Aleksa Sarai --- .github/workflows/rust.yml | 7 +++ Makefile | 4 ++ hack/keyring-addkey.sh | 40 +++++++++++++ hack/keyring-validate.sh | 112 +++++++++++++++++++++++++++++++++++++ libpathrs.keyring | 56 +++++++++++++++++++ 5 files changed, 219 insertions(+) create mode 100755 hack/keyring-addkey.sh create mode 100755 hack/keyring-validate.sh create mode 100644 libpathrs.keyring diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 9b0f9006..c81d1c0d 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -166,6 +166,12 @@ jobs: run: sudo apt-get install -y lld - run: make validate-elf-symbols + validate-keyring: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - run: make validate-keyring + build: runs-on: ubuntu-latest strategy: @@ -521,6 +527,7 @@ jobs: - check-lint-nohack - validate-cbindgen - validate-elf-symbols + - validate-keyring - build - rustdoc - doctest diff --git a/Makefile b/Makefile index cd0760cf..ae060ffd 100644 --- a/Makefile +++ b/Makefile @@ -131,6 +131,10 @@ validate-cbindgen: validate-elf-symbols: release ./hack/check-elf-symbols.sh ./target/release/libpathrs.so +.PHONY: validate-keyring +validate-keyring: + ./hack/keyring-validate.sh + .PHONY: test-rust-doctest test-rust-doctest: $(CARGO_LLVM_COV) --no-report --branch --doc diff --git a/hack/keyring-addkey.sh b/hack/keyring-addkey.sh new file mode 100755 index 00000000..ef0d1324 --- /dev/null +++ b/hack/keyring-addkey.sh @@ -0,0 +1,40 @@ +#!/bin/bash +# SPDX-License-Identifier: Apache-2.0 +# Copyright (C) 2023-2025 SUSE LLC. +# Copyright (C) 2023 Open Containers Authors +# Copyright (C) 2026 Aleksa Sarai +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -Eeuxo pipefail + +project="libpathrs" +root="$(readlink -f "$(dirname "${BASH_SOURCE[0]}")/..")" +keyring_file="$root/$project.keyring" + +function bail() { + echo "$@" >&2 + exit 1 +} + +[[ "$#" -eq 2 ]] || bail "usage: $0 " + +github_handle="${1}" +gpg_keyid="${2}" + +cat >>"$keyring_file" < +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -Eeuo pipefail + +project="libpathrs" +root="$(readlink -f "$(dirname "${BASH_SOURCE[0]}")/..")" + +function log() { + echo "[*]" "$@" >&2 +} + +function bail() { + log "$@" + exit 1 +} + +# Temporary GPG keyring for messing around with. +tmp_gpgdir="$(mktemp -d --tmpdir "$project-validate-tmpkeyring.XXXXXX")" +trap 'rm -r "$tmp_gpgdir"' EXIT + +function gpg_user() { + local user=$1 + shift + gpg --homedir="$tmp_gpgdir" --no-default-keyring --keyring="$user.keyring" "$@" +} + +# Get the set of MAINTAINERS. +readarray -t maintainers < <(sed -E 's|.* <.*> \(@?(.*)\)$|\1|' <"$root/MAINTAINERS") +echo "------------------------------------------------------------" +echo "$project maintainers:" +printf " * %s\n" "${maintainers[@]}" +echo "------------------------------------------------------------" + +# Create a dummy gpg keyring from the set of MAINTAINERS. +while IFS="" read -r username || [ -n "$username" ]; do + curl -sSL "https://github.com/$username.gpg" | gpg_user "$username" --import +done < <(printf '%s\n' "${maintainers[@]}") + +# Make sure all of the keys in the keyring have a github=... comment. +awk <"$root/$project.keyring" ' + /^-----BEGIN PGP PUBLIC KEY BLOCK-----$/ { key_idx++; in_pgp=1; has_comment=0; } + + # PGP comments are never broken up over several lines, and we only have one + # comment entry in our keyring file anyway. + in_pgp && /^Comment:.* github=\w+.*/ { has_comment=1 } + + /^-----END PGP PUBLIC KEY BLOCK-----$/ { + if (!has_comment) { + print "[!] Key", key_idx, "in '$project'.keyring is missing a github= comment." + exit 1 + } + } +' + +echo "------------------------------------------------------------" +echo "$project release managers:" +sed -En "s|^Comment:.* github=(\w+).*| * \1|p" <"$root/$project.keyring" | sort -u +echo "------------------------------------------------------------" +gpg --show-keys <"$root/$project.keyring" +echo "------------------------------------------------------------" + +# Check that each entry in the keyring is actually a maintainer's key. +while IFS="" read -d $'\0' -r block || [ -n "$block" ]; do + username="$(sed -En "s|^Comment:.* github=(\w+).*|\1|p" <<<"$block")" + + # FIXME: This is to work around codespell thinking that f-p-r is a + # misspelling of some other word, and the lack of support for inline + # ignores in codespell. + fprfield="f""p""r" + + # Check the username is actually a maintainer. This is just a sanity check, + # since you can put whatever you like in the Comment field. + [ -f "$tmp_gpgdir/$username.keyring" ] || bail "User $username in $project.keyring is not a maintainer!" + grep "(@$username)$" "$root/MAINTAINERS" >/dev/null || bail "User $username in $project.keyring is not a maintainer!" + + # Check that the key in the block actually matches a known key for that + # maintainer. Note that a block can contain multiple keys, so we need to + # check all of them. Since we have to handle multiple keys anyway, we'll + # also verify all of the subkeys (this is simpler to implement anyway since + # the --with-colons format outputs fingerprints for both primary and + # subkeys in the same way). + # + # Fingerprints have a field 1 of $fprfield and field 10 containing the + # fingerprint. See + # for more details. + while IFS="" read -r key || [ -n "$key" ]; do + gpg_user "$username" --list-keys --with-colons | grep "$fprfield:::::::::$key:" >/dev/null || + bail "(Sub?)Key $key in $project.keyring is NOT actually one of $username's keys!" + log "Successfully verified $username's (sub?)key $key is legitimate." + done < <(gpg --show-keys --with-colons <<<"$block" | + grep "^$fprfield:" | cut -d: -f10) +done < <(awk <"$root/$project.keyring" ' + /^-----BEGIN PGP PUBLIC KEY BLOCK-----$/ { in_block=1 } + in_block { print } + /^-----END PGP PUBLIC KEY BLOCK-----$/ { in_block=0; printf("\0"); } +') diff --git a/libpathrs.keyring b/libpathrs.keyring new file mode 100644 index 00000000..bb5d5b2a --- /dev/null +++ b/libpathrs.keyring @@ -0,0 +1,56 @@ +pub ed25519 2019-06-21 [C] + C9C370B246B09F6DBCFC744C34401015D1D2D386 +uid [ultimate] Aleksa Sarai +sub ed25519 2022-09-30 [S] [expires: 2030-03-25] + B64E4955B29FA3D463F2A9062897FAD2B7E9446F +sub cv25519 2022-09-30 [E] [expires: 2030-03-25] + 0C23601C4F4561640663556524325218CEA61CB8 +sub ed25519 2022-09-30 [A] [expires: 2030-03-25] + A6BBD7976DBC7617FC73737D2374658C6654AF23 + +-----BEGIN PGP PUBLIC KEY BLOCK----- +Comment: github=cyphar + +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 +=Ab7w +-----END PGP PUBLIC KEY BLOCK----- + From 8b989b34e4affd5eb2e5d8442a584eb8afbabf25 Mon Sep 17 00:00:00 2001 From: Aleksa Sarai Date: Tue, 3 Mar 2026 01:14:16 +1100 Subject: [PATCH 3/3] make: add signed release build script This is modelled after the umoci and runc release scripts, with two minor changes: 1. We generate a vendor.tar.zst containing the output of "cargo vendor". Most distributions do not need this (Debian packages every crate individually, other distros generate their own vendor.tar.zst at packaging time) but it is nice to provide anyway. 2. We do not produce any binary artefacts for our releases at this point. We could in principle produce static libpathrs.a libraries that people could link against, but that would require also compiling with musl to get properly static binaries and would complicate the release procedure somewhat. Signed-off-by: Aleksa Sarai --- .github/workflows/rust.yml | 12 +++ .gitignore | 3 + CHANGELOG.md | 3 + Makefile | 6 ++ hack/readlinkf.sh | 44 +++++++++ hack/release.sh | 190 +++++++++++++++++++++++++++++++++++++ 6 files changed, 258 insertions(+) create mode 100644 hack/readlinkf.sh create mode 100755 hack/release.sh diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index c81d1c0d..e47a3017 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -172,6 +172,17 @@ jobs: - uses: actions/checkout@v6 - run: make validate-keyring + validate-dist-release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: dtolnay/rust-toolchain@stable + - run: make dist-release + - name: check release artefacts + run: |- + ls -la release/*/ + cat release/*/libpathrs.sha256sum + build: runs-on: ubuntu-latest strategy: @@ -528,6 +539,7 @@ jobs: - validate-cbindgen - validate-elf-symbols - validate-keyring + - validate-dist-release - build - rustdoc - doctest diff --git a/.gitignore b/.gitignore index 057f876a..668c93f3 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,9 @@ /target **/*.rs.bk +# Releases directory. +/release + # pkg-config generated by install.sh. /pathrs.pc diff --git a/CHANGELOG.md b/CHANGELOG.md index 191ea321..ede1512a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - `install.sh` now accepts `--rust-target` and `--rust-buildmode` as parameters to make cross-compilation workflows easier to write (in particular, this is needed for runc's release scripts). +- We now produce signed release artefacts for our releases (though currently + only in the form of signed source and `cargo vendor` tarballs). The accepted + set of signing keys are available in [`libpathrs.keyring`](./libpathrs.keyring). ### Changed ### - The `O_PATH` resolver for `procfs` now has an additional bit of hardening diff --git a/Makefile b/Makefile index ae060ffd..16629c1d 100644 --- a/Makefile +++ b/Makefile @@ -180,3 +180,9 @@ install: release @echo "[Sleeping for 3 seconds.]" @sleep 3s ./install.sh + +GPG_KEYID ?= cyphar@cyphar.com + +.PHONY: dist-release +dist-release: + ./hack/release.sh -S $(GPG_KEYID) diff --git a/hack/readlinkf.sh b/hack/readlinkf.sh new file mode 100644 index 00000000..26cd42ed --- /dev/null +++ b/hack/readlinkf.sh @@ -0,0 +1,44 @@ +#!/bin/bash +# SPDX-License-Identifier: CC0-1.0 +# readlinkf: POSIX-compliant implementation of readlink -f. +# Author: Koichi Nakashima +# Licensed under the Creative Commons Zero v1.0 Universal license. +# + +# Copied verbatim from v1.1.0 of . + +# POSIX compliant version +readlinkf_posix() { + [ "${1:-}" ] || return 1 + max_symlinks=40 + CDPATH='' # to avoid changing to an unexpected directory + + target=$1 + [ -e "${target%/}" ] || target=${1%"${1##*[!/]}"} # trim trailing slashes + [ -d "${target:-/}" ] && target="$target/" + + cd -P . 2>/dev/null || return 1 + while [ "$max_symlinks" -ge 0 ] && max_symlinks=$((max_symlinks - 1)); do + if [ ! "$target" = "${target%/*}" ]; then + case $target in + /*) cd -P "${target%/*}/" 2>/dev/null || break ;; + *) cd -P "./${target%/*}" 2>/dev/null || break ;; + esac + target=${target##*/} + fi + + if [ ! -L "$target" ]; then + target="${PWD%/}${target:+/}${target}" + printf '%s\n' "${target:-/}" + return 0 + fi + + # `ls -dl` format: "%s %u %s %s %u %s %s -> %s\n", + # , , , , + # , , , + # https://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html + link=$(ls -dl -- "$target" 2>/dev/null) || break + target=${link#*" $target -> "} + done + return 1 +} diff --git a/hack/release.sh b/hack/release.sh new file mode 100755 index 00000000..9fcb693b --- /dev/null +++ b/hack/release.sh @@ -0,0 +1,190 @@ +#!/bin/bash +# SPDX-License-Identifier: MPL-2.0 +# release.sh: configurable signed-artefact release script +# Copyright (C) 2016-2025 SUSE LLC +# Copyright (C) 2026 Aleksa Sarai +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +set -Eeuo pipefail +# shellcheck source=./readlinkf.sh +source "$(dirname "${BASH_SOURCE[0]}")/readlinkf.sh" + +## ---> +# Project-specific options and functions. In *theory* you shouldn't need to +# touch anything else in this script in order to use this elsewhere. +project="libpathrs" +root="$(readlinkf_posix "$(dirname "${BASH_SOURCE[0]}")/..")" + +# These functions allow you to configure how the defaults are computed. +function get_host_target() { rustc --print host-tuple ; } +function get_version() { cargo metadata --format-version=1 | jq -rM '.packages[] | select(.name == "pathrs") | .version' ; } + +# Any pre-configuration steps should be done here -- for instance ./configure. +function setup_project() { true ; } + +# This function takes an output path as an argument, where the built +# (preferably static) binary should be placed. +function build_project() { + # TODO: Figure out what we should do for builds... + true +} + +# Generates a vendor.tar.zstd file to "$1" (set to "-" to get it to stdout). +function generate_vendor() { + local vendor_tar="$1" + + local tmpvendor + tmpvendor="$(mktemp -dt "$project-vendor.XXXXXX")" + # shellcheck disable=SC2064 # We want to expand the variables immediately. + trap "rm -rf '$tmpvendor'" RETURN + + cargo vendor --versioned-dirs "$tmpvendor/vendor" + tar cv -f "$vendor_tar" -C "$tmpvendor/" vendor/ +} +# End of the easy-to-configure portion. +## <--- + +# Print usage information. +function usage() { + echo "usage: release.sh [-h] [-v ] [-c ] [-o ]" >&2 + echo " [-H ] [-S ]" >&2 +} + +# Log something to stderr. +function log() { + echo "[*]" "$@" >&2 +} + +# Log something to stderr and then exit with 0. +function quit() { + log "$@" + exit 0 +} + +# Conduct a sanity-check to make sure that GPG provided with the given +# arguments can sign something. Inability to sign things is not a fatal error. +function gpg_cansign() { + gpg "$@" --clear-sign /dev/null +} + +# When creating releases we need to build (ideally static) binaries, an archive +# of the current commit, and generate detached signatures for both. +keyid="" +version="" +commit="HEAD" +hashcmd="sha256sum" +while getopts ":c:H:h:o:S:v:" opt; do + case "$opt" in + c) + commit="$OPTARG" + ;; + H) + hashcmd="$OPTARG" + ;; + h) + usage ; exit 0 + ;; + o) + outputdir="$OPTARG" + ;; + S) + keyid="$OPTARG" + ;; + v) + version="$OPTARG" + ;; + :) + echo "Missing argument: -$OPTARG" >&2 + usage ; exit 1 + ;; + \?) + echo "Invalid option: -$OPTARG" >&2 + usage ; exit 1 + ;; + esac +done + +# Run project setup first... +( set -x ; setup_project ) + +# Generate the defaults for version and so on *after* argument parsing and +# setup_project, to avoid calling get_version() needlessly. +version="${version:-$(get_version)}" +outputdir="${outputdir:-release/$version}" + +log "[[ $project ]]" +log "version: $version" +log "commit: $commit" +log "output_dir: $outputdir" +log "key: ${keyid:-(default)}" +log "hash_cmd: $hashcmd" + +# Make explicit what we're doing. +set -x + +# Make the release directory. +rm -rf "$outputdir" && mkdir -p "$outputdir" + +# Build project. +# TODO: Figure out what we should do for builds... +#for target in "${targets[@]}"; do +# target="${target//\//.}" +# os="$(cut -d. -f1 <<<"$target")" +# arch="$(cut -d. -f2 <<<"$target")" +# GOOS="$os" GOARCH="$arch" build_project "$outputdir/$project.$target" +#done + +# Generate vendor.tar.zst. +generate_vendor - | zstd -11 >"$outputdir/$project.vendor.tar.zst" + +# Generate new archive. +git archive --format=tar --prefix="$project-$version/" "$commit" | xz > "$outputdir/$project-$version.tar.xz" + +# Generate sha256 checksums for everything. +( cd "$outputdir" ; "$hashcmd" "$project"* > "$project.$hashcmd" ; ) + +# Set up the gpgflags. +gpgflags=() +[[ -z "$keyid" ]] || gpgflags+=("--default-key=$keyid") +gpg_cansign "${gpgflags[@]}" || quit "Could not find suitable GPG key, skipping signing step." + +# Make explicit what we're doing. +set -x + +# Check that the keyid is actually in the $project.keyring by signing a piece +# of dummy text then verifying it against the list of keys in that keyring. +tmp_gpgdir="$(mktemp -d --tmpdir "$project-sign-tmpkeyring.XXXXXX")" +trap 'rm -r "$tmp_gpgdir"' EXIT + +tmp_project_gpgflags=("--homedir=$tmp_gpgdir" "--no-default-keyring" "--keyring=$project.keyring") +gpg "${tmp_project_gpgflags[@]}" --import <"$root/$project.keyring" + +gpg "${gpgflags[@]}" --clear-sign <<<"[This is test text used for $project release scripts. $(date --rfc-email)]" | + gpg "${tmp_project_gpgflags[@]}" --verify || bail "Signing key ${keyid:-DEFAULT} is not in trusted $project.keyring list!" + +# Make sure the signer is okay with the list of keys in the keyring (once this +# release is signed, distributions will trust this keyring). +cat >&2 <