2
0
mirror of https://github.com/xcat2/xcat-dep.git synced 2026-10-18 09:40:32 +00:00

fix(genesis): pin verified release checksums

This commit is contained in:
Vinícius Ferrão
2026-08-22 01:25:39 -03:00
parent 23d7cbf5dc
commit 779f5f7b21
3 changed files with 97 additions and 18 deletions
+48 -5
View File
@@ -13,6 +13,15 @@ GPG_KEY_ID="xcat@megware.com"
SKIP_SIGN=0
DRY_RUN=0
GENESIS_RELEASE=""
GENESIS_CHECKSUMS=""
GENESIS_VERIFIER=""
cleanup() {
if [[ -n "$GENESIS_CHECKSUMS" ]]; then
rm -f -- "$GENESIS_CHECKSUMS"
fi
}
trap cleanup EXIT
declare -A CODENAME_MAP=(
[ubuntu22.04]=jammy
@@ -86,10 +95,8 @@ APT_DIR="${APT_DIR:-$REPO_ROOT/repos/apt}"
if [[ -n "$GENESIS_RELEASE" ]]; then
[[ -d "$GENESIS_RELEASE" ]] || die "Genesis release directory not found: $GENESIS_RELEASE"
GENESIS_RELEASE="$(cd "$GENESIS_RELEASE" && pwd)"
verifier="$SCRIPT_DIR/genesis-openembedded/verify-release"
[[ -x "$verifier" ]] || die "Genesis release verifier not found: $verifier"
"$verifier" --complete --format deb "$GENESIS_RELEASE"
echo "Genesis release: $GENESIS_RELEASE"
GENESIS_VERIFIER="$SCRIPT_DIR/genesis-openembedded/verify-release"
[[ -x "$GENESIS_VERIFIER" ]] || die "Genesis release verifier not found: $GENESIS_VERIFIER"
fi
# Default to all known versions when no DIST arg was given; otherwise validate
@@ -110,6 +117,21 @@ command -v apt-ftparchive >/dev/null 2>&1 \
|| die "apt-ftparchive not found. Install: sudo apt-get install apt-utils"
command -v gpg >/dev/null 2>&1 \
|| die "gpg not found. Install: sudo apt-get install gnupg"
command -v cmp >/dev/null 2>&1 \
|| die "cmp not found. Install: sudo apt-get install diffutils"
command -v dpkg-deb >/dev/null 2>&1 \
|| die "dpkg-deb not found. Install: sudo apt-get install dpkg"
command -v sha256sum >/dev/null 2>&1 \
|| die "sha256sum not found. Install: sudo apt-get install coreutils"
if [[ -n "$GENESIS_RELEASE" ]]; then
GENESIS_CHECKSUMS=$(mktemp "${TMPDIR:-/tmp}/xcat-genesis-checksums.XXXXXX")
cp -- "$GENESIS_RELEASE/SHA256SUMS" "$GENESIS_CHECKSUMS"
"$GENESIS_VERIFIER" --complete --format deb "$GENESIS_RELEASE"
cmp -s "$GENESIS_CHECKSUMS" "$GENESIS_RELEASE/SHA256SUMS" \
|| die "Genesis release changed during verification"
echo "Genesis release: $GENESIS_RELEASE"
fi
if [[ $SKIP_SIGN -eq 0 ]]; then
if ! gpg --list-secret-keys "$GPG_KEY_ID" >/dev/null 2>&1; then
@@ -168,6 +190,27 @@ copy_deb() {
ln "$source" "$destination" 2>/dev/null || cp "$source" "$destination"
}
copy_genesis_deb() {
local source="$1"
local directory="$2"
local name destination relative expected actual
name=$(basename "$source")
destination="$directory/$name"
relative="deb/$name"
if [[ -e "$destination" ]]; then
cmp -s "$source" "$destination" \
|| die "Package collision with different content: $destination"
else
cp -- "$source" "$destination"
fi
expected=$(awk -v path="$relative" '$2 == path { print $1 }' "$GENESIS_CHECKSUMS")
[[ -n "$expected" ]] || die "Missing verified checksum for $relative"
actual=$(sha256sum "$destination")
actual=${actual%% *}
[[ "$actual" = "$expected" ]] \
|| die "Collected Genesis package checksum mismatch: $destination"
}
for ver in "${SELECTED_VERS[@]}"; do
codename="${CODENAME_MAP[$ver]}"
src="$APT_DIR/$ver"
@@ -179,7 +222,7 @@ for ver in "${SELECTED_VERS[@]}"; do
done
if [[ -n "$GENESIS_RELEASE" ]]; then
for deb in "$GENESIS_RELEASE"/deb/*.deb; do
copy_deb "$deb" "$dst"
copy_genesis_deb "$deb" "$dst"
done
fi
fi
@@ -13,10 +13,12 @@ our @EXPORT_OK = qw(
deb_package_name
read_release_manifest
rpm_package_name
validated_release_checksums
validate_architecture
validate_complete_release
validate_export
validate_release
verify_release_file
);
my @ARCHITECTURES = qw(x86 x86_64 ppc64 ppc64le armv7hf aarch64 riscv64);
@@ -126,7 +128,7 @@ sub _verify_checksums {
for my $name (keys %{$checksums}) {
die "Checksum names a missing file: $name\n" unless $files{$name};
}
return 1;
return $checksums;
}
sub validate_export {
@@ -256,4 +258,23 @@ sub validate_complete_release {
return $manifest;
}
sub validated_release_checksums {
my ($directory) = @_;
validate_release($directory);
return _read_checksums($directory);
}
sub verify_release_file {
my ($checksums, $relative, $path) = @_;
die "Missing verified checksum for $relative\n"
unless ref($checksums) eq 'HASH' && exists($checksums->{$relative});
die "Invalid collected release file: $path\n" unless -f $path && !-l $path;
open(my $fh, '<:raw', $path) or die "Cannot read $path: $!\n";
my $digest = Digest::SHA->new(256)->addfile($fh)->hexdigest;
close($fh) or die "Cannot close $path: $!\n";
die "Collected release file checksum mismatch: $path\n"
unless $digest eq $checksums->{$relative};
return 1;
}
1;
+27 -12
View File
@@ -9,9 +9,15 @@ use File::Copy qw(copy);
use File::Find qw(find);
use File::Glob qw(bsd_glob);
use File::Path qw(make_path);
use FindBin;
use Getopt::Long qw(GetOptions);
use Parallel::ForkManager;
use POSIX qw(strftime);
use lib "$FindBin::Bin/genesis-openembedded/lib";
use XCAT::GenesisRelease qw(
validated_release_checksums
verify_release_file
);
my $script_dir = abs_path(dirname(__FILE__));
my $repo_root = abs_path($script_dir);
@@ -38,6 +44,7 @@ my $skip_genesis = 0;
my $skip_createrepo = 0;
my $skip_tarball = 0;
my $genesis_release = '';
my $genesis_release_checksums;
my $scrub_all_chroots = 0;
my $dry_run = 0;
my @extra_collect_dirs;
@@ -139,14 +146,18 @@ require_command('rpmsign') if $gpg_sign;
require_command('gpg') if $gpg_sign;
if ($genesis_release ne '') {
require_command('cmp');
$genesis_release = abs_path($genesis_release)
or die "Cannot resolve --genesis-release directory\n";
die "Genesis release directory not found: $genesis_release\n"
unless -d $genesis_release;
my $verifier = "$repo_root/genesis-openembedded/verify-release";
die "Genesis release verifier not found: $verifier\n" unless -x $verifier;
my $checksums_before = validated_release_checksums($genesis_release);
run_argv($^X, $verifier, '--complete', '--format', 'rpm', $genesis_release);
my $checksums_after = validated_release_checksums($genesis_release);
die "Genesis release changed during verification\n"
unless checksum_sets_match($checksums_before, $checksums_after);
$genesis_release_checksums = $checksums_before;
$skip_genesis = 1;
}
@@ -933,23 +944,27 @@ sub remove_genesis_packages {
sub assert_genesis_release_copied {
my ($binary_directory, $source_directory) = @_;
for my $entry (
[ "$genesis_release/rpm", $binary_directory ],
[ "$genesis_release/srpm", $source_directory ],
[ 'rpm', $binary_directory ],
[ 'srpm', $source_directory ],
) {
my ($source_root, $destination_root) = @{$entry};
for my $source (bsd_glob("$source_root/*.rpm")) {
my $destination = "$destination_root/" . basename($source);
die "Genesis package was not collected: $destination\n" unless -f $destination;
die "Collected Genesis package differs from release: $destination\n"
unless files_match($source, $destination);
my ($prefix, $destination_root) = @{$entry};
for my $relative (
sort grep { /^\Q$prefix\E\/[^\/]+\.rpm\z/ } keys %{$genesis_release_checksums}
)
{
my $destination = "$destination_root/" . basename($relative);
verify_release_file($genesis_release_checksums, $relative, $destination);
}
}
}
sub files_match {
sub checksum_sets_match {
my ($left, $right) = @_;
return 0 unless -f $left && -f $right;
return system('cmp', '-s', $left, $right) == 0;
return 0 unless keys(%{$left}) == keys(%{$right});
for my $name (keys %{$left}) {
return 0 unless exists($right->{$name}) && $left->{$name} eq $right->{$name};
}
return 1;
}
sub collect_rpms {