mirror of
https://github.com/xcat2/xcat-dep.git
synced 2026-10-18 17:50:36 +00:00
fix(genesis): pin verified release checksums
This commit is contained in:
+48
-5
@@ -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
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user