Compare commits

..

2 Commits

Author SHA1 Message Date
Vekhir --
e9677a545a Merge branch 'verify-in-chroot' into 'master'
feat(makechrootpkg): Download and verify in chroot

Closes #225 and #224

See merge request archlinux/devtools!246
2025-07-29 22:21:16 +00:00
Vekhir
f73b0510d1 feat(src/makechrootpkg.in): Download and verify in chroot
Downloading the sources and verifying them on the host system is easy and
straightforward, but does have some drawbacks.
Use of custom DLAGENTS for downloading might require additional tools such as
curl or wget to retrieve the source. Those have to be installed on the host
system; declaring them in makedepends only works when using makepkg directly.
This isn't much of an issue with curl as it happens to be part of base (i.e.
always installed), but wget isn't.
The same applies to VCS sources. This issue became apparent rather soon, since
it meant that non-git sources simply couldn't be downloaded if not installed
beforehand (when using makechrootpkg). The solution for that was to make
devtools depend on all VCS tools.
Another recent example is the introduction of the verify() function which
allows arbitrary signature verification (like minisign).

Making devtools depend on all VCS tools, all download clients and all
signature verifiers is hardly an appropriate solution.
Instead, put the downloading and verification into the chroot where the
build is sandboxed and can install all the programs it needs.

Conceptually, the idea is simple: Call download_sources from within the chroot.
Practically, it's more complicated because the chroot cannot directly access
devtools internal functions. However, this problem isn't new, so the solution
is similar to _chrootbuild.
Since we are in the chroot, we don't need the BUILDDIR - we don't build
anything anyhow. The config doesn't need $copydir and the die call is moved
outside the function. The function is also prefixed with an underscore to
show that it's being used in the chroot.
We also don't preserve the environment, instead bind the GPG key directory and
SSH access keys to the readonly directory /verify (same name as the verify()
function). More on that below.
Lastly, move the function a bit further down since the chroot needs to be
ready and the nspawn_build_args defined.

The ad-hoc bash command is structured like this:
1. Copy the function declaration (we can't call it directly)
2. Copy verifysource_args literally (we can't access the variable later)
3. Call _download_sources to download and verify.

The call to makepkg also gets 3 new arguments:
--syncdeps:  to download the necessary makedepends.
--noconfirm: to automatically do so.
--log:       to keep the new log for the verify() function.

The biggest hurdle, and sortof the only drawback, is to make sure that
makepkg has access to the necessary keys for verification. In particular
GPG with its web of trust wants to ensure that the provided keys are
trustworthy. This is normally done by importing one or several keys into the
local keyring. While archlinux-keyring covers that aspect for official
packages, keys for other packagers or even the user themself need to be made
available within the chroot.
For that purpose, the SSH_AUTH_SOCK is bind-ro to /verify/ssh and the GPG
keyring to /verify/gnupg. The latter can be either set via GNUPGHOME or, as
default, located at $HOME/.gnupg. $HOME within makechrootpkg.in usually refers
to /root (because root user), when we need the $HOME for the makepkg user. It
is retrieved when calling load_makepkg_config, so also set DEVTOOLS_GNUPGHOME
to that.
Drawback being that other keyrings will need the same treatment, whereas they
essentially just work right now.

This is not intended as a breaking change, and I don't believe it leads to
breakage. download_sources is only used in makechrootpkg.in, so renaming it
is fine.
Shellcheck found a few issues which I corrected. No new issues remain.

Tested with:
genymotion (requires wget for download)
libchewing (requires minisign for verify())
devtools (requires access to host GPG keys)
All Haskell packages (sanity check)
2024-11-27 22:37:45 +00:00
10 changed files with 20 additions and 76 deletions

View File

@@ -150,7 +150,6 @@ _pkgctl_cmds=(
db db
diff diff
issue issue
license
release release
repo repo
search search

View File

@@ -155,7 +155,7 @@ if (( ${#needsversioning[*]} )); then
if [[ ! -f "${file}" ]]; then if [[ ! -f "${file}" ]]; then
continue continue
fi fi
if ! git ls-files --error-unmatch "$file" >/dev/null; then if ! git ls-files --error-unmatch "$file"; then
die "%s is not under version control" "$file" die "%s is not under version control" "$file"
fi fi
done done

View File

@@ -312,7 +312,7 @@ pkgctl_build() {
# Update pacman cache for auto-detection # Update pacman cache for auto-detection
if [[ -z ${REPO} ]]; then if [[ -z ${REPO} ]]; then
update_pacman_repo_cache stable update_pacman_repo_cache multilib
# Check valid repos if not resolved dynamically # Check valid repos if not resolved dynamically
elif ! in_array "${REPO}" "${DEVTOOLS_VALID_REPOS[@]}"; then elif ! in_array "${REPO}" "${DEVTOOLS_VALID_REPOS[@]}"; then
die "Invalid repository target: %s" "${REPO}" die "Invalid repository target: %s" "${REPO}"

View File

@@ -51,6 +51,7 @@ pkgctl_db_remove() {
local partial=0 local partial=0
local confirm=1 local confirm=1
local dbscripts_options=() local dbscripts_options=()
local lookup_repo=multilib
local pkgname local pkgname
# option checking # option checking
@@ -105,13 +106,13 @@ pkgctl_db_remove() {
update_pacman_repo_cache unstable update_pacman_repo_cache unstable
;; ;;
*-staging) *-staging)
update_pacman_repo_cache staging update_pacman_repo_cache multilib-staging
;; ;;
*-testing) *-testing)
update_pacman_repo_cache testing update_pacman_repo_cache multilib-testing
;; ;;
*) *)
update_pacman_repo_cache stable update_pacman_repo_cache multilib
;; ;;
esac esac

View File

@@ -188,13 +188,10 @@ path = [
"README.md", "README.md",
"keys/**", "keys/**",
".SRCINFO", ".SRCINFO",
".gitignore",
".nvchecker.toml", ".nvchecker.toml",
"*.install", "*.install",
"*.sysusers", "*.sysusers",
"*sysusers.conf",
"*.tmpfiles", "*.tmpfiles",
"*tmpfiles.conf",
"*.logrotate", "*.logrotate",
"*.pam", "*.pam",
"*.service", "*.service",

View File

@@ -124,7 +124,7 @@ pkgctl_release() {
# Update pacman cache for auto-detection # Update pacman cache for auto-detection
if [[ -z ${REPO} ]]; then if [[ -z ${REPO} ]]; then
update_pacman_repo_cache stable update_pacman_repo_cache multilib
# Check valid repos if not resolved dynamically # Check valid repos if not resolved dynamically
elif ! in_array "${REPO}" "${DEVTOOLS_VALID_REPOS[@]}"; then elif ! in_array "${REPO}" "${DEVTOOLS_VALID_REPOS[@]}"; then
die "Invalid repository target: %s" "${REPO}" die "Invalid repository target: %s" "${REPO}"

View File

@@ -1,26 +0,0 @@
#!/hint/bash
#
# SPDX-License-Identifier: GPL-3.0-or-later
[[ -z ${DEVTOOLS_INCLUDE_UTIL_MACHINE_SH:-} ]] || return 0
DEVTOOLS_INCLUDE_UTIL_MACHINE_SH=1
_DEVTOOLS_LIBRARY_DIR=${_DEVTOOLS_LIBRARY_DIR:-@pkgdatadir@}
# shellcheck source=src/lib/common.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/common.sh
set -eo pipefail
machine_get_hardware_name() {
uname --machine
}
machine_has_multilib() {
case "$(machine_get_hardware_name)" in
x86_64*)
return 0
;;
esac
return 1
}

View File

@@ -8,8 +8,6 @@ DEVTOOLS_INCLUDE_UTIL_PACMAN_SH=1
_DEVTOOLS_LIBRARY_DIR=${_DEVTOOLS_LIBRARY_DIR:-@pkgdatadir@} _DEVTOOLS_LIBRARY_DIR=${_DEVTOOLS_LIBRARY_DIR:-@pkgdatadir@}
# shellcheck source=src/lib/common.sh # shellcheck source=src/lib/common.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/common.sh source "${_DEVTOOLS_LIBRARY_DIR}"/lib/common.sh
# shellcheck source=src/lib/util/machine.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/util/machine.sh
set -e set -e
@@ -20,8 +18,7 @@ readonly _DEVTOOLS_MAKEPKG_CONF_DIR=${_DEVTOOLS_LIBRARY_DIR}/makepkg.conf.d
update_pacman_repo_cache() { update_pacman_repo_cache() {
local repo=${1:-stable} local repo=${1:-multilib}
repo=$(pacman_resolve_virtual_repo_name "${repo}")
mkdir -p "${_DEVTOOLS_PACMAN_CACHE_DIR}" mkdir -p "${_DEVTOOLS_PACMAN_CACHE_DIR}"
msg "Updating pacman database cache" msg "Updating pacman database cache"
@@ -35,8 +32,7 @@ update_pacman_repo_cache() {
get_pacman_repo_from_pkgbuild() { get_pacman_repo_from_pkgbuild() {
local path=${1:-PKGBUILD} local path=${1:-PKGBUILD}
local repo=${2:-stable} local repo=${2:-multilib}
repo=$(pacman_resolve_virtual_repo_name "${repo}")
local -a pkgnames local -a pkgnames
# shellcheck source=contrib/makepkg/PKGBUILD.proto # shellcheck source=contrib/makepkg/PKGBUILD.proto
@@ -76,7 +72,6 @@ get_pkgnames_from_repo_pkgbase() {
# update the pacman repo cache if it doesn't exist yet # update the pacman repo cache if it doesn't exist yet
if [[ ! -d "${_DEVTOOLS_PACMAN_CACHE_DIR}" ]]; then if [[ ! -d "${_DEVTOOLS_PACMAN_CACHE_DIR}" ]]; then
# TODO: universe includes multilib, switch for architecture
update_pacman_repo_cache universe update_pacman_repo_cache universe
fi fi
@@ -96,23 +91,3 @@ get_pkgnames_from_repo_pkgbase() {
printf "%s\n" "${pkgnames[@]}" printf "%s\n" "${pkgnames[@]}"
return 0 return 0
} }
pacman_resolve_virtual_repo_name() {
local repo=$1
local repo_class=extra
if machine_has_multilib; then
repo_class=multilib
fi
case "${repo}" in
stable)
repo=${repo_class}
;;
testing|staging)
repo="${repo_class}-${repo}"
;;
esac
printf "%s" "${repo}"
}

View File

@@ -6,7 +6,6 @@
# shellcheck disable=2034 # shellcheck disable=2034
DEVTOOLS_VALID_BINARY_ARCHES=( DEVTOOLS_VALID_BINARY_ARCHES=(
x86_64 x86_64
aarch64
) )
# shellcheck disable=2034 # shellcheck disable=2034

View File

@@ -19,7 +19,7 @@ shopt -s nullglob
default_makepkg_args=(--syncdeps --noconfirm --log --holdver --skipinteg) default_makepkg_args=(--syncdeps --noconfirm --log --holdver --skipinteg)
makepkg_args=("${default_makepkg_args[@]}") makepkg_args=("${default_makepkg_args[@]}")
verifysource_args=() verifysource_args=(--syncdeps --noconfirm --log)
chrootdir= chrootdir=
passeddir= passeddir=
makepkg_user= makepkg_user=
@@ -175,7 +175,7 @@ prepare_chroot() {
printf >>"$copydir/etc/passwd" 'builduser:x:%d:%d:builduser:/build:/bin/bash\n' "$builduser_uid" "$builduser_gid" printf >>"$copydir/etc/passwd" 'builduser:x:%d:%d:builduser:/build:/bin/bash\n' "$builduser_uid" "$builduser_gid"
printf >>"$copydir/etc/shadow" 'builduser:!!:%d::::::\n' "$(( $(date -u +%s) / 86400 ))" printf >>"$copydir/etc/shadow" 'builduser:!!:%d::::::\n' "$(( $(date -u +%s) / 86400 ))"
$install -d "$copydir"/{build,startdir,{pkg,srcpkg,src,log}dest} $install -d "$copydir"/{build,startdir,{pkg,srcpkg,src,log}dest,verify/{gnupg,ssh}}
sed -e '/^MAKEFLAGS=/d' -e '/^PACKAGER=/d' -i "$copydir/etc/makepkg.conf" sed -e '/^MAKEFLAGS=/d' -e '/^PACKAGER=/d' -i "$copydir/etc/makepkg.conf"
for x in BUILDDIR=/build PKGDEST=/pkgdest SRCPKGDEST=/srcpkgdest SRCDEST=/srcdest LOGDEST=/logdest \ for x in BUILDDIR=/build PKGDEST=/pkgdest SRCPKGDEST=/srcpkgdest SRCDEST=/srcdest LOGDEST=/logdest \
@@ -247,15 +247,10 @@ _chrootnamcap() {
done done
} }
download_sources() { _download_sources() {
setup_workdir
chown "$makepkg_user:" "$WORKDIR"
# Ensure sources are downloaded # Ensure sources are downloaded
sudo -u "$makepkg_user" --preserve-env=GNUPGHOME,SSH_AUTH_SOCK \ sudo -u builduser env SRCDEST="/srcdest" GNUPGHOME="/verify/gnupg" SSH_AUTH_SOCK="/verify/ssh" \
env SRCDEST="$SRCDEST" BUILDDIR="$WORKDIR" \ bash -c "cd /startdir; makepkg --config=/etc/makepkg.conf --verifysource -o ${verifysource_args[*]}"
makepkg --config="$copydir/etc/makepkg.conf" --verifysource -o "${verifysource_args[@]}" ||
die "Could not download sources."
} }
move_logfiles() { move_logfiles() {
@@ -352,6 +347,7 @@ umask 0022
ORIG_HOME=$HOME ORIG_HOME=$HOME
IFS=: read -r _ _ _ _ _ HOME _ < <(getent passwd "${SUDO_USER:-$USER}") IFS=: read -r _ _ _ _ _ HOME _ < <(getent passwd "${SUDO_USER:-$USER}")
load_makepkg_config load_makepkg_config
DEVTOOLS_GNUPGHOME="${GNUPGHOME:-$HOME/.gnupg}"
HOME=$ORIG_HOME HOME=$ORIG_HOME
# Use PKGBUILD directory if these don't exist # Use PKGBUILD directory if these don't exist
@@ -383,8 +379,6 @@ if [[ "$(id -u "$makepkg_user")" == 0 ]]; then
exit 1 exit 1
fi fi
download_sources
prepare_chroot prepare_chroot
nspawn_build_args=( nspawn_build_args=(
@@ -396,6 +390,11 @@ nspawn_build_args=(
"${bindmounts_tmpfs[@]}" "${bindmounts_tmpfs[@]}"
) )
arch-nspawn "$copydir" \
"${nspawn_build_args[@]}" --bind-ro="${DEVTOOLS_GNUPGHOME//:/\\:}:/verify/gnupg" --bind-ro="${SSH_AUTH_SOCK//:/\\:}:/verify/ssh" \
bash -c "$(declare -f _download_sources); verifysource_args=(${verifysource_args[*]}); _download_sources" ||
die "Could not download sources."
if arch-nspawn "$copydir" \ if arch-nspawn "$copydir" \
"${nspawn_build_args[@]}" \ "${nspawn_build_args[@]}" \
/chrootbuild "${makepkg_args[@]}" /chrootbuild "${makepkg_args[@]}"