fix: harden release validation
This commit is contained in:
@@ -15,8 +15,8 @@ jobs:
|
|||||||
test:
|
test:
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version-file: go.mod
|
||||||
cache: false
|
cache: false
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ build() {
|
|||||||
output="$out/proxyctl-linux-$arch"
|
output="$out/proxyctl-linux-$arch"
|
||||||
(
|
(
|
||||||
cd "$repo"
|
cd "$repo"
|
||||||
CGO_ENABLED=0 GOOS=linux GOARCH="$arch" \
|
GOENV=off GOFLAGS= CGO_ENABLED=0 GOOS=linux GOARCH="$arch" \
|
||||||
go build -mod=readonly -buildvcs=false -trimpath \
|
go build -mod=readonly -buildvcs=false -trimpath \
|
||||||
-ldflags="-s -w -X main.version=$VERSION" \
|
-ldflags="-s -w -X main.version=$VERSION" \
|
||||||
-o "$output" ./cmd/proxyctl
|
-o "$output" ./cmd/proxyctl
|
||||||
|
|||||||
@@ -5,21 +5,31 @@ repo=$(CDPATH= cd -- "$(dirname "$0")/.." && pwd)
|
|||||||
work=$(mktemp -d)
|
work=$(mktemp -d)
|
||||||
trap 'rm -rf "$work"' EXIT HUP INT TERM
|
trap 'rm -rf "$work"' EXIT HUP INT TERM
|
||||||
|
|
||||||
|
snapshot_path() {
|
||||||
|
path=$1
|
||||||
|
if [ ! -e "$path" ] && [ ! -L "$path" ]; then
|
||||||
|
printf 'absent\t%s\n' "$path"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Keep traversal, metadata collection, hashing, and sorting as separate
|
||||||
|
# commands so set -e observes every failure without relying on pipefail.
|
||||||
|
find -H "$path" -xdev \
|
||||||
|
-exec stat --printf='metadata\t%F\t%a\t%u\t%g\t%N\n' -- {} + \
|
||||||
|
>"$work/snapshot-metadata"
|
||||||
|
LC_ALL=C sort "$work/snapshot-metadata"
|
||||||
|
find -H "$path" -xdev -type f -exec sha256sum -- {} + \
|
||||||
|
>"$work/snapshot-content"
|
||||||
|
LC_ALL=C sort "$work/snapshot-content"
|
||||||
|
}
|
||||||
|
|
||||||
snapshot() {
|
snapshot() {
|
||||||
for path in \
|
for path in \
|
||||||
"$HOME/.bashrc" "$HOME/.zshrc" "$HOME/.docker/config.json" \
|
"$HOME/.bashrc" "$HOME/.zshrc" "$HOME/.docker/config.json" \
|
||||||
/etc/apt/apt.conf.d /etc/docker/daemon.json /var/lib/proxyctl \
|
/etc/apt/apt.conf.d /etc/docker/daemon.json /var/lib/proxyctl \
|
||||||
/etc/systemd/system/docker.service.d
|
/etc/systemd/system/docker.service.d
|
||||||
do
|
do
|
||||||
if [ -d "$path" ]; then
|
snapshot_path "$path"
|
||||||
find "$path" -type f -print0 2>/dev/null | sort -z | xargs -0 -r sha256sum 2>/dev/null || ls -ld "$path"
|
|
||||||
elif [ -r "$path" ]; then
|
|
||||||
sha256sum "$path"
|
|
||||||
elif [ -e "$path" ]; then
|
|
||||||
ls -ld "$path"
|
|
||||||
else
|
|
||||||
printf 'absent %s\n' "$path"
|
|
||||||
fi
|
|
||||||
done
|
done
|
||||||
if command -v gsettings >/dev/null 2>&1; then
|
if command -v gsettings >/dev/null 2>&1; then
|
||||||
gsettings list-recursively org.gnome.system.proxy 2>&1 || true
|
gsettings list-recursively org.gnome.system.proxy 2>&1 || true
|
||||||
|
|||||||
@@ -5,15 +5,52 @@ repo=$(CDPATH= cd -- "$(dirname "$0")/.." && pwd)
|
|||||||
work=$(mktemp -d)
|
work=$(mktemp -d)
|
||||||
trap 'rm -rf "$work"' EXIT HUP INT TERM
|
trap 'rm -rf "$work"' EXIT HUP INT TERM
|
||||||
|
|
||||||
|
assert_no_process_test_seam() {
|
||||||
|
artifact=$1
|
||||||
|
if LC_ALL=C grep -a -F \
|
||||||
|
-e 'proxyctl_process_test' \
|
||||||
|
-e 'PROXYCTL_PROCESS_TEST_HOME' \
|
||||||
|
"$artifact" >/dev/null
|
||||||
|
then
|
||||||
|
printf 'release artifact contains the process-test seam: %s\n' "$artifact" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
VERSION=v1.0.0 "$repo/scripts/build-release.sh" "$work/first"
|
VERSION=v1.0.0 "$repo/scripts/build-release.sh" "$work/first"
|
||||||
VERSION=v1.0.0 "$repo/scripts/build-release.sh" "$work/second"
|
VERSION=v1.0.0 "$repo/scripts/build-release.sh" "$work/second"
|
||||||
|
GOFLAGS='-tags=proxyctl_process_test' \
|
||||||
|
VERSION=v1.0.0 "$repo/scripts/build-release.sh" "$work/malicious-goflags"
|
||||||
|
printf 'GOFLAGS=-tags=proxyctl_process_test\n' >"$work/malicious-goenv"
|
||||||
|
GOENV="$work/malicious-goenv" \
|
||||||
|
VERSION=v1.0.0 "$repo/scripts/build-release.sh" "$work/malicious-goenv-build"
|
||||||
|
|
||||||
for arch in amd64 arm64; do
|
for arch in amd64 arm64; do
|
||||||
binary="proxyctl-linux-$arch"
|
binary="proxyctl-linux-$arch"
|
||||||
test -x "$work/first/$binary"
|
test -x "$work/first/$binary"
|
||||||
cmp "$work/first/$binary" "$work/second/$binary"
|
cmp "$work/first/$binary" "$work/second/$binary"
|
||||||
|
cmp "$work/first/$binary" "$work/malicious-goflags/$binary"
|
||||||
|
cmp "$work/first/$binary" "$work/malicious-goenv-build/$binary"
|
||||||
|
assert_no_process_test_seam "$work/first/$binary"
|
||||||
|
assert_no_process_test_seam "$work/malicious-goflags/$binary"
|
||||||
|
assert_no_process_test_seam "$work/malicious-goenv-build/$binary"
|
||||||
done
|
done
|
||||||
cmp "$work/first/SHA256SUMS" "$work/second/SHA256SUMS"
|
cmp "$work/first/SHA256SUMS" "$work/second/SHA256SUMS"
|
||||||
|
cmp "$work/first/SHA256SUMS" "$work/malicious-goflags/SHA256SUMS"
|
||||||
|
cmp "$work/first/SHA256SUMS" "$work/malicious-goenv-build/SHA256SUMS"
|
||||||
|
|
||||||
|
(
|
||||||
|
cd "$repo"
|
||||||
|
GOENV=off GOFLAGS= CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
|
||||||
|
go build -mod=readonly -buildvcs=false -trimpath \
|
||||||
|
-tags=proxyctl_process_test -o "$work/known-contaminated" ./cmd/proxyctl
|
||||||
|
)
|
||||||
|
if assert_no_process_test_seam "$work/known-contaminated"; then
|
||||||
|
echo 'release artifact validation accepted a known contaminated binary' >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
printf 'process-test seam scan rejected a known contaminated binary\n'
|
||||||
|
|
||||||
(
|
(
|
||||||
cd "$work/first"
|
cd "$work/first"
|
||||||
sha256sum -c SHA256SUMS
|
sha256sum -c SHA256SUMS
|
||||||
|
|||||||
@@ -10,19 +10,31 @@ state="$work/state"
|
|||||||
bin="$work/bin"
|
bin="$work/bin"
|
||||||
mkdir -p "$home" "$config" "$state" "$bin"
|
mkdir -p "$home" "$config" "$state" "$bin"
|
||||||
|
|
||||||
|
snapshot_host_path() {
|
||||||
|
path=$1
|
||||||
|
if [ ! -e "$path" ] && [ ! -L "$path" ]; then
|
||||||
|
printf 'absent\t%s\n' "$path"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Avoid pipelines: traversal, stat, hash, and sort failures must stop the
|
||||||
|
# verification rather than being mistaken for an unchanged host snapshot.
|
||||||
|
find -H "$path" -xdev \
|
||||||
|
-exec stat --printf='metadata\t%F\t%a\t%u\t%g\t%N\n' -- {} + \
|
||||||
|
>"$work/host-snapshot-metadata"
|
||||||
|
LC_ALL=C sort "$work/host-snapshot-metadata"
|
||||||
|
find -H "$path" -xdev -type f -exec sha256sum -- {} + \
|
||||||
|
>"$work/host-snapshot-content"
|
||||||
|
LC_ALL=C sort "$work/host-snapshot-content"
|
||||||
|
}
|
||||||
|
|
||||||
snapshot_host() {
|
snapshot_host() {
|
||||||
for path in \
|
for path in \
|
||||||
"$HOME/.bashrc" "$HOME/.zshrc" "$HOME/.docker/config.json" \
|
"$HOME/.bashrc" "$HOME/.zshrc" "$HOME/.docker/config.json" \
|
||||||
/etc/apt/apt.conf.d/99proxyctl /etc/docker/daemon.json \
|
/etc/apt/apt.conf.d /etc/docker/daemon.json /var/lib/proxyctl \
|
||||||
/var/lib/proxyctl/docker-daemon.json
|
/etc/systemd/system/docker.service.d
|
||||||
do
|
do
|
||||||
if [ -r "$path" ]; then
|
snapshot_host_path "$path"
|
||||||
sha256sum "$path"
|
|
||||||
elif [ -e "$path" ]; then
|
|
||||||
ls -ld "$path"
|
|
||||||
else
|
|
||||||
printf 'absent %s\n' "$path"
|
|
||||||
fi
|
|
||||||
done
|
done
|
||||||
if command -v gsettings >/dev/null 2>&1; then
|
if command -v gsettings >/dev/null 2>&1; then
|
||||||
gsettings get org.gnome.system.proxy mode 2>&1 || true
|
gsettings get org.gnome.system.proxy mode 2>&1 || true
|
||||||
|
|||||||
Reference in New Issue
Block a user