From 5a615ad580af17706332a5494fb5664cd2f11d11 Mon Sep 17 00:00:00 2001 From: chlins Date: Tue, 4 Aug 2026 17:19:25 +0800 Subject: [PATCH] Verify build tool downloads Pin architecture-specific SHA-256 checksums for kubebuilder, protoc, and GoReleaser before installation. Signed-off-by: chlins --- .../unreleased/RS-MIRRORS_GITHUB_VELERO-22 | 1 + hack/build-image/Dockerfile | 81 +++++++------ hack/verify-build-image-tool-checksums.sh | 111 ++++++++++++++++++ 3 files changed, 160 insertions(+), 33 deletions(-) create mode 100644 changelogs/unreleased/RS-MIRRORS_GITHUB_VELERO-22 create mode 100755 hack/verify-build-image-tool-checksums.sh diff --git a/changelogs/unreleased/RS-MIRRORS_GITHUB_VELERO-22 b/changelogs/unreleased/RS-MIRRORS_GITHUB_VELERO-22 new file mode 100644 index 000000000..b17917098 --- /dev/null +++ b/changelogs/unreleased/RS-MIRRORS_GITHUB_VELERO-22 @@ -0,0 +1 @@ +Verify downloaded build tools against architecture-specific SHA-256 checksums before installation. diff --git a/hack/build-image/Dockerfile b/hack/build-image/Dockerfile index 4f34ba470..8978855b2 100644 --- a/hack/build-image/Dockerfile +++ b/hack/build-image/Dockerfile @@ -29,8 +29,18 @@ RUN go install sigs.k8s.io/controller-runtime/tools/setup-envtest@v0.0.0-2026030 ENVTEST_ASSETS_DIR=$(setup-envtest use 1.33.0 --bin-dir /usr/local/kubebuilder/bin -p path) && \ cp -r ${ENVTEST_ASSETS_DIR}/* /usr/local/kubebuilder/bin/ -RUN wget --quiet https://github.com/kubernetes-sigs/kubebuilder/releases/download/v3.2.0/kubebuilder_linux_$(go env GOARCH) && \ - mv kubebuilder_linux_$(go env GOARCH) /usr/local/kubebuilder/bin/kubebuilder && \ +RUN set -eux; \ + ARCH="$(go env GOARCH)"; \ + case "$ARCH" in \ + amd64) KUBEBUILDER_SHA256="102bb0f586dcb50951aded67856483a2ee114057c56475b3cda6051a12832a72" ;; \ + arm64) KUBEBUILDER_SHA256="0a340ea925c801aa71344becdefce96eda6fa0bc92352b9c7bcb36a4f8c56314" ;; \ + ppc64le) KUBEBUILDER_SHA256="74473d094908caad852a77088f64bb64eb4c79497f6695eb5e9e8bc4bacd9409" ;; \ + *) echo "Unsupported kubebuilder architecture: $ARCH" >&2; exit 1 ;; \ + esac; \ + FILE="kubebuilder_linux_$ARCH"; \ + wget --quiet "https://github.com/kubernetes-sigs/kubebuilder/releases/download/v3.2.0/$FILE"; \ + echo "$KUBEBUILDER_SHA256 $FILE" | sha256sum -c -; \ + mv "$FILE" /usr/local/kubebuilder/bin/kubebuilder; \ chmod +x /usr/local/kubebuilder/bin/kubebuilder # get controller-tools @@ -52,26 +62,27 @@ RUN apt-get update && apt-get install -y unzip # cpu = "ppcle_64" # snippet from: https://github.com/protocolbuffers/protobuf/blob/d445953603e66eb8992a39b4e10fcafec8501f24/protobuf_release.bzl#L18-L24 # cpu names: https://github.com/bazelbuild/platforms/blob/main/cpu/BUILD -RUN ARCH=$(go env GOARCH) && \ - if [ "$ARCH" = "s390x" ] ; then \ - ARCH="s390_64"; \ - elif [ "$ARCH" = "arm64" ] ; then \ - ARCH="aarch_64"; \ - elif [ "$ARCH" = "ppc64le" ] ; then \ - ARCH="ppcle_64"; \ - elif [ "$ARCH" = "ppc64" ] ; then \ - ARCH="ppcle_64"; \ - else \ - ARCH=$(uname -m); \ - fi && echo "ARCH=$ARCH" && \ - wget --quiet https://github.com/protocolbuffers/protobuf/releases/download/v25.2/protoc-25.2-linux-$ARCH.zip && \ - unzip protoc-25.2-linux-$ARCH.zip; \ - rm *.zip && \ - mv bin/protoc /usr/bin/protoc && \ - mv include/google /usr/include && \ - chmod a+x /usr/include/google && \ - chmod a+x /usr/include/google/protobuf && \ - chmod a+r -R /usr/include/google && \ +RUN set -eux; \ + GOARCH="$(go env GOARCH)"; \ + case "$GOARCH" in \ + amd64) ARCH="x86_64"; PROTOC_SHA256="78ab9c3288919bdaa6cfcec6127a04813cf8a0ce406afa625e48e816abee2878" ;; \ + 386) ARCH="x86_32"; PROTOC_SHA256="cc1c6e31a9b333c3e6d026aac5fdc1f7d70c6cd8851631505188ca9826acee5a" ;; \ + arm64) ARCH="aarch_64"; PROTOC_SHA256="07683afc764e4efa3fa969d5f049fbc2bdfc6b4e7786a0b233413ac0d8753f6b" ;; \ + ppc64|ppc64le) ARCH="ppcle_64"; PROTOC_SHA256="cea283337101ed08ff6c76a98461b1d871bac21f41dc1dabdfddaa5d99df9339" ;; \ + s390x) ARCH="s390_64"; PROTOC_SHA256="8a13ec6518585f7664d58f929417c9e6d0c4aeedf3bcdd854aeafceb5ef0a389" ;; \ + *) echo "Unsupported protoc architecture: $GOARCH" >&2; exit 1 ;; \ + esac; \ + echo "ARCH=$ARCH"; \ + FILE="protoc-25.2-linux-$ARCH.zip"; \ + wget --quiet "https://github.com/protocolbuffers/protobuf/releases/download/v25.2/$FILE"; \ + echo "$PROTOC_SHA256 $FILE" | sha256sum -c -; \ + unzip "$FILE"; \ + rm "$FILE"; \ + mv bin/protoc /usr/bin/protoc; \ + mv include/google /usr/include; \ + chmod a+x /usr/include/google; \ + chmod a+x /usr/include/google/protobuf; \ + chmod a+r -R /usr/include/google; \ chmod +x /usr/bin/protoc RUN go install google.golang.org/protobuf/cmd/protoc-gen-go@${PROTOC_GEN_GO_VERSION} \ && go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.3.0 @@ -84,17 +95,21 @@ RUN go install google.golang.org/protobuf/cmd/protoc-gen-go@${PROTOC_GEN_GO_VERS # {{- else if eq .Arch "386" }}i386 # {{- else }}{{ .Arch }}{{ end }} # {{- if .Arm }}v{{ .Arm }}{{ end -}} -RUN ARCH=$(go env GOARCH) && \ - if [ "$ARCH" = "amd64" ] ; then \ - ARCH="x86_64"; \ - elif [ "$ARCH" = "386" ] ; then \ - ARCH="i386"; \ - elif [ "$ARCH" = "ppc64le" ] ; then \ - ARCH="ppc64"; \ - fi && \ - wget --quiet "https://github.com/goreleaser/goreleaser/releases/download/v1.26.2/goreleaser_Linux_$ARCH.tar.gz" && \ - tar xvf goreleaser_Linux_$ARCH.tar.gz; \ - mv goreleaser /usr/bin/goreleaser && \ +RUN set -eux; \ + GOARCH="$(go env GOARCH)"; \ + case "$GOARCH" in \ + amd64) ARCH="x86_64"; GORELEASER_SHA256="cfbdf12e3ea20e4c3a209d07311f43c2e0baf20d5cce09bcdc232567e0f34307" ;; \ + 386) ARCH="i386"; GORELEASER_SHA256="21c236575cccd29588182b570b4ffe83ad8fb96cd3b13b2af79feafd8ae37b1b" ;; \ + arm64) ARCH="arm64"; GORELEASER_SHA256="2b984e2932b24be0d638c7dab7357a59d86eb79ca7fee1afd31be5ebb1847cbb" ;; \ + arm) ARCH="armv7"; GORELEASER_SHA256="6db2899885be19f123b36192a42dcfb3bb2b3e1009fec7277517969e96d8a7c6" ;; \ + ppc64|ppc64le) ARCH="ppc64"; GORELEASER_SHA256="76d060ebb8d48e76fde45983f87040fe3ac0ca37c5ace4648a956959b81bfdf0" ;; \ + *) echo "Unsupported goreleaser architecture: $GOARCH" >&2; exit 1 ;; \ + esac; \ + FILE="goreleaser_Linux_$ARCH.tar.gz"; \ + wget --quiet "https://github.com/goreleaser/goreleaser/releases/download/v1.26.2/$FILE"; \ + echo "$GORELEASER_SHA256 $FILE" | sha256sum -c -; \ + tar xvf "$FILE"; \ + mv goreleaser /usr/bin/goreleaser; \ chmod +x /usr/bin/goreleaser # get golangci-lint diff --git a/hack/verify-build-image-tool-checksums.sh b/hack/verify-build-image-tool-checksums.sh new file mode 100755 index 000000000..a11f258ab --- /dev/null +++ b/hack/verify-build-image-tool-checksums.sh @@ -0,0 +1,111 @@ +#!/usr/bin/env bash +# Copyright 2026 the Velero contributors. +# +# 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 -euo pipefail + +ROOT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) +DOCKERFILE="${ROOT_DIR}/hack/build-image/Dockerfile" + +verify_block() { + local tool=$1 + local start=$2 + local end=$3 + local hash_variable=$4 + local install_pattern=$5 + shift 5 + local expected_arches=("$@") + local block + + block=$(awk -v start="${start}" -v end="${end}" ' + $0 ~ start { printing = 1 } + printing { print } + printing && $0 ~ end { exit } + ' "${DOCKERFILE}") + + if [[ -z "${block}" ]]; then + echo "Unable to find ${tool} install block" >&2 + return 1 + fi + + local actual_arches + actual_arches=$(printf '%s\n' "${block}" | + sed -nE "s/^[[:space:]]*([[:alnum:]_|]+)\).*${hash_variable}=\"([[:xdigit:]]+)\".*/\1 \2/p") + + local expected_arch + for expected_arch in "${expected_arches[@]}"; do + if ! printf '%s\n' "${actual_arches}" | awk -v arch="${expected_arch}" ' + $1 == arch && length($2) == 64 && $2 ~ /^[0-9a-f]+$/ { found = 1 } + END { exit !found } + '; then + echo "${tool} is missing a lowercase 64-hex SHA-256 for ${expected_arch}" >&2 + return 1 + fi + done + + local actual_count + actual_count=$(printf '%s\n' "${actual_arches}" | sed '/^$/d' | wc -l | tr -d ' ') + if [[ "${actual_count}" -ne "${#expected_arches[@]}" ]]; then + echo "${tool} architecture mapping changed; update this verification gate" >&2 + printf '%s\n' "${actual_arches}" >&2 + return 1 + fi + + if ! printf '%s\n' "${block}" | grep -Eq '^ \*\).*Unsupported .+ architecture:.+exit 1'; then + echo "${tool} does not fail closed for unknown architectures" >&2 + return 1 + fi + + local download_line checksum_line install_line + download_line=$(printf '%s\n' "${block}" | grep -n 'wget --quiet' | head -1 | cut -d: -f1) + checksum_line=$(printf '%s\n' "${block}" | grep -n "echo \"\$${hash_variable} \$FILE\" | sha256sum -c -" | head -1 | cut -d: -f1) + install_line=$(printf '%s\n' "${block}" | grep -nE "${install_pattern}" | head -1 | cut -d: -f1) + + if [[ -z "${download_line}" || -z "${checksum_line}" || -z "${install_line}" || + "${download_line}" -ge "${checksum_line}" || "${checksum_line}" -ge "${install_line}" ]]; then + echo "${tool} must download, verify, then install/extract in that order" >&2 + return 1 + fi + + if ! printf '%s\n' "${block}" | grep -q '^RUN set -eux;'; then + echo "${tool} install block must use strict shell error handling" >&2 + return 1 + fi +} + +verify_block \ + kubebuilder \ + '^RUN set -eux;.*$' \ + '^# get controller-tools$' \ + KUBEBUILDER_SHA256 \ + 'mv "\$FILE"' \ + amd64 arm64 ppc64le + +verify_block \ + protoc \ + '^# cpu names:' \ + '^RUN go install google.golang.org/protobuf' \ + PROTOC_SHA256 \ + 'unzip "\$FILE"' \ + amd64 386 arm64 'ppc64|ppc64le' s390x + +verify_block \ + goreleaser \ + '^# goreleaser name template' \ + '^# get golangci-lint$' \ + GORELEASER_SHA256 \ + 'tar xvf "\$FILE"' \ + amd64 386 arm64 arm 'ppc64|ppc64le' + +echo "Verified pinned build-tool checksums and fail-closed install ordering"