Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/actions/deploy-prod/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ runs:
# refresh-flux-ghcr-auth.sh uses the host API for a secret-safe,
# per-node RegistryAuthConfig patch and exact image-pull proof.
shell: bash
env:
# Download through the authenticated asset API first (#5942).
GITHUB_TOKEN: ${{ github.token }}
run: |
.github/scripts/setup-talosctl.sh

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ runs:
- name: 🔐 Install verified supply-chain tools
id: setup_supply_chain_tools
shell: bash
env:
# Download through the authenticated asset API first (#5942).
GITHUB_TOKEN: ${{ github.token }}
run: .github/scripts/setup-supply-chain-tools.sh

- name: 🏷️ Create unique staging reference
Expand Down
94 changes: 94 additions & 0 deletions .github/scripts/release-asset-download.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# shellcheck shell=bash
# Download one GitHub release asset, preferring the authenticated asset API (#5942).
#
# WHY THIS EXISTS
# The setup helpers used to fetch every release asset from the public
# `https://github.com/<owner>/<repo>/releases/download/...` URL with no credentials.
# Hosted runners share their outbound addresses, so those requests count as
# anonymous traffic. On 2026-11-09 a GitHub incident that slowed anonymous traffic
# made every one of those downloads return 504 until curl gave up, and the whole
# platform delivery path stopped before any check of our own ran.
#
# When GITHUB_TOKEN is set, this asks the release-asset API for the same bytes
# with the job's token first, and falls back to the anonymous URL if that fails.
# Without a token it goes straight to the anonymous URL, exactly as before.
#
# 🔴 THIS CHOOSES WHERE THE BYTES COME FROM, NEVER WHETHER THEY ARE TRUSTED.
# Every caller still checks the downloaded file against its reviewed SHA-256 pin
# (or its published digests) before installing or running anything. Neither path
# is more trusted than the other: both serve the same release asset.
#
# Each call prints ONE line to stderr per attempt, with fixed fields only:
# release-asset asset=<name> path=<authenticated|anonymous> status=<http code> result=<served|failed|unlisted>
# The status is curl's final HTTP code (000 when no response arrived); `unlisted`
# means the release metadata was read but did not list the asset exactly once. The token,
# request headers and response bodies are never printed. curl does not forward the
# Authorization header when the API redirects to the storage host.
#
# Every local is `rad_`-prefixed: this file is SOURCED, and a caller's readonly
# variable of the same name would otherwise abort it under `set -e`.

# Print a validated three-digit HTTP code, or 000 for anything else.
release_asset_status() { # raw
if [[ "${1-}" =~ ^[0-9]{3}$ ]]; then
printf '%s' "$1"
else
printf '000'
fi
}

release_asset_log() { # asset path status result
printf 'release-asset asset=%s path=%s status=%s result=%s\n' "$1" "$2" "$(release_asset_status "$3")" "$4" >&2
}

# release_asset_download OWNER/REPO TAG ASSET OUTPUT
# Write ASSET from release TAG of OWNER/REPO to OUTPUT. Returns non-zero, with
# OUTPUT removed, when neither path served it.
release_asset_download() {
local rad_repo="${1:?owner/repo required}" rad_tag="${2:?tag required}"
local rad_asset="${3:?asset name required}" rad_output="${4:?output path required}"
local rad_status rad_metadata rad_id
# Transient failures (timeouts, 408, 429 and 5xx) are retried a few times on each
# path (#4549), capped so a long server-requested wait cannot hold the job.
local -a rad_curl=(curl --fail --silent --show-error --location
--proto '=https' --proto-redir '=https'
--retry 5 --retry-delay 3 --retry-max-time 60)

if [[ -n "${GITHUB_TOKEN:-}" ]]; then
local -a rad_auth=(-H "Authorization: Bearer ${GITHUB_TOKEN}" -H "X-GitHub-Api-Version: 2022-11-28")
rad_metadata="$(mktemp "${RUNNER_TEMP:-/tmp}/release-asset.XXXXXX")"
rad_id=''
if rad_status="$("${rad_curl[@]}" "${rad_auth[@]}" -H 'Accept: application/vnd.github+json' \
--write-out '%{http_code}' --output "${rad_metadata}" \
"https://api.github.com/repos/${rad_repo}/releases/tags/${rad_tag}")"; then
rad_id="$(jq -r --arg asset "${rad_asset}" \
'[.assets[]? | select(.name == $asset) | .id | select(type == "number")] | if length == 1 then .[0] else empty end' \
"${rad_metadata}" 2>/dev/null)" || rad_id=''
fi
rm -f -- "${rad_metadata}"
if [[ "${rad_id}" =~ ^[0-9]+$ ]]; then
if rad_status="$("${rad_curl[@]}" "${rad_auth[@]}" -H 'Accept: application/octet-stream' \
--write-out '%{http_code}' --output "${rad_output}" \
"https://api.github.com/repos/${rad_repo}/releases/assets/${rad_id}")"; then
release_asset_log "${rad_asset}" authenticated "${rad_status}" served
return 0
fi
release_asset_log "${rad_asset}" authenticated "${rad_status:-000}" failed
elif [[ "$(release_asset_status "${rad_status:-}")" == 2* ]]; then
# The release was read but does not list this asset exactly once.
release_asset_log "${rad_asset}" authenticated "${rad_status}" unlisted
else
release_asset_log "${rad_asset}" authenticated "${rad_status:-000}" failed
fi
rm -f -- "${rad_output}"
fi

if rad_status="$("${rad_curl[@]}" --write-out '%{http_code}' --output "${rad_output}" \
"https://github.com/${rad_repo}/releases/download/${rad_tag}/${rad_asset}")"; then
release_asset_log "${rad_asset}" anonymous "${rad_status}" served
return 0
fi
release_asset_log "${rad_asset}" anonymous "${rad_status:-000}" failed
rm -f -- "${rad_output}"
return 1
}
11 changes: 7 additions & 4 deletions .github/scripts/setup-ksail.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ if [ -z "${KSAIL_VERSION:-}" ]; then
fi

asset_name="ksail_${KSAIL_VERSION}_linux_amd64.tar.gz"
release_base="https://github.com/devantler-tech/ksail/releases/download/v${KSAIL_VERSION}"
api_url="https://api.github.com/repos/devantler-tech/ksail/releases/tags/v${KSAIL_VERSION}"
tarball="${RUNNER_TEMP:-/tmp}/${asset_name}"
checksums="${RUNNER_TEMP:-/tmp}/ksail_${KSAIL_VERSION}_checksums.txt"
Expand All @@ -22,13 +21,17 @@ fi
# because one momentary error from the release host would otherwise fail the job and,
# in a merge-group deploy, evict the pull request (#4549). Every byte is still checked
# against both digest sources below, and a persistent failure still stops here. The
# total is capped so a long server-requested wait cannot hold the job.
# total is capped so a long server-requested wait cannot hold the job. The two release
# assets use the authenticated asset API first when GITHUB_TOKEN is set, and fall back
# to the public URL (#5942).
download() {
curl -fsSL --retry 5 --retry-delay 3 --retry-max-time 60 "$@"
}
# shellcheck source=.github/scripts/release-asset-download.sh
source "$(dirname "${BASH_SOURCE[0]}")/release-asset-download.sh"

download "${release_base}/${asset_name}" -o "${tarball}"
download "${release_base}/ksail_${KSAIL_VERSION}_checksums.txt" -o "${checksums}"
release_asset_download devantler-tech/ksail "v${KSAIL_VERSION}" "${asset_name}" "${tarball}"
release_asset_download devantler-tech/ksail "v${KSAIL_VERSION}" "ksail_${KSAIL_VERSION}_checksums.txt" "${checksums}"
download "${curl_headers[@]}" "${api_url}" -o "${release_json}"

manifest_digest=$(awk -v asset="${asset_name}" '$2 == asset {print $1}' "${checksums}")
Expand Down
18 changes: 9 additions & 9 deletions .github/scripts/setup-supply-chain-tools.sh
Original file line number Diff line number Diff line change
Expand Up @@ -41,17 +41,17 @@ verify_digest() {
}

# Release downloads retry curl's transient failures (timeouts, 408, 429 and 5xx) a few times,
# because one momentary error from the release host would otherwise fail the job. Every byte
# is still checked against the reviewed pin below, and a persistent failure still stops here.
download() {
curl --fail --silent --show-error --location --proto '=https' --proto-redir '=https' \
--retry 5 --retry-delay 3 "$1" --output "$2"
}
# because one momentary error from the release host would otherwise fail the job. With
# GITHUB_TOKEN set they use the authenticated asset API first and fall back to the public
# URL (#5942). Every byte is still checked against the reviewed pin below, and a
# persistent failure still stops here.
# shellcheck source=.github/scripts/release-asset-download.sh
source "$(dirname "${BASH_SOURCE[0]}")/release-asset-download.sh"

if [[ ${1:-} == trivy-boundary ]]; then
: "${RUNNER_TEMP:?RUNNER_TEMP must name the runner temporary directory}"
trivy_dir="${RUNNER_TEMP}/platform-validation-tools"
download "https://github.com/aquasecurity/trivy/releases/download/v${TRIVY_VERSION}/trivy_${TRIVY_VERSION}_Linux-64bit.tar.gz" \
release_asset_download aquasecurity/trivy "v${TRIVY_VERSION}" "trivy_${TRIVY_VERSION}_Linux-64bit.tar.gz" \
"${download_dir}/trivy.tar.gz"
verify_digest trivy "${TRIVY_SHA256}" "${download_dir}/trivy.tar.gz"
tar -xzf "${download_dir}/trivy.tar.gz" -C "${download_dir}" trivy
Expand All @@ -62,11 +62,11 @@ if [[ ${1:-} == trivy-boundary ]]; then
exit 0
fi

download "https://github.com/sigstore/cosign/releases/download/v${COSIGN_VERSION}/cosign-linux-amd64" \
release_asset_download sigstore/cosign "v${COSIGN_VERSION}" cosign-linux-amd64 \
"${download_dir}/cosign"
verify_digest cosign "${COSIGN_SHA256}" "${download_dir}/cosign"

download "https://github.com/anchore/syft/releases/download/v${SYFT_VERSION}/syft_${SYFT_VERSION}_linux_amd64.tar.gz" \
release_asset_download anchore/syft "v${SYFT_VERSION}" "syft_${SYFT_VERSION}_linux_amd64.tar.gz" \
"${download_dir}/syft.tar.gz"
verify_digest syft "${SYFT_SHA256}" "${download_dir}/syft.tar.gz"

Expand Down
9 changes: 6 additions & 3 deletions .github/scripts/setup-talosctl.sh
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,12 @@ trap 'rm -f "${target}"' EXIT

# The download retries curl's transient failures (timeouts, 408, 429 and 5xx) a few
# times, because one momentary error from the release host would otherwise fail the job
# (#4549). The bytes are still checked against the pin above, and a persistent failure
# still stops here.
curl -fsSL --retry 5 --retry-delay 3 --retry-max-time 60 "${release_base}/${asset_name}" -o "${target}"
# (#4549). With GITHUB_TOKEN set it uses the authenticated asset API first and falls
# back to the public URL (#5942). The bytes are still checked against the pin above,
# and a persistent failure still stops here.
# shellcheck source=.github/scripts/release-asset-download.sh
source "$(dirname "${BASH_SOURCE[0]}")/release-asset-download.sh"
release_asset_download siderolabs/talos "v${TALOS_VERSION}" "${asset_name}" "${target}"

actual_digest=$(sha256sum "${target}" | cut -d' ' -f1)

Expand Down
27 changes: 24 additions & 3 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ jobs:
shellcheck scripts/tests/test-webapp-compiled-scan.sh
bash scripts/tests/test-webapp-compiled-scan.sh
- name: 🔏 Install reviewed public-image verification tools
env:
# Download through the authenticated asset API first (#5942).
GITHUB_TOKEN: ${{ github.token }}
run: bash .github/scripts/setup-supply-chain-tools.sh
- name: Validate the hosted runtime script
run: |
Expand Down Expand Up @@ -245,6 +248,8 @@ jobs:
- 'scripts/tests/test-setup-talosctl.sh'
- '.github/scripts/setup-supply-chain-tools.sh'
- 'scripts/tests/test-setup-supply-chain-tools.sh'
- '.github/scripts/release-asset-download.sh'
- 'scripts/tests/test-release-asset-download.sh'
- '.github/actions/deploy-prod/action.yml'
- '.github/workflows/dr-rebuild.yaml'
- '.github/workflows/validate-image-verifier-liveness.yaml'
Expand Down Expand Up @@ -844,6 +849,7 @@ jobs:
- 'ksail.prod.yaml'
- 'ksail.yaml'
- '.github/scripts/setup-talosctl.sh'
- '.github/scripts/release-asset-download.sh'
- 'scripts/validate-talos-kubernetes-compatibility/**'
- '.github/actions/deploy-prod/**'
- 'go.mod'
Expand Down Expand Up @@ -2252,7 +2258,7 @@ jobs:
- name: 🔐 Validate KSail installer integrity guard
if: needs.changes.outputs.k8s == 'true'
run: |
shellcheck .github/scripts/setup-ksail.sh scripts/tests/test-setup-ksail.sh
shellcheck -x .github/scripts/setup-ksail.sh scripts/tests/test-setup-ksail.sh
bash scripts/tests/test-setup-ksail.sh

- name: 🔐 Validate Cilium SNI hostname coverage
Expand All @@ -2264,17 +2270,26 @@ jobs:
- name: 🔐 Validate talosctl installer integrity guard
if: needs.changes.outputs.k8s == 'true'
run: |
shellcheck .github/scripts/setup-talosctl.sh scripts/tests/test-setup-talosctl.sh
shellcheck -x .github/scripts/setup-talosctl.sh scripts/tests/test-setup-talosctl.sh
bash scripts/tests/test-setup-talosctl.sh

- name: 🔐 Validate supply-chain tool integrity
if: needs.changes.outputs.k8s == 'true'
run: |
shellcheck .github/scripts/setup-supply-chain-tools.sh scripts/tests/test-setup-supply-chain-tools.sh
shellcheck -x .github/scripts/setup-supply-chain-tools.sh scripts/tests/test-setup-supply-chain-tools.sh
bash scripts/tests/test-setup-supply-chain-tools.sh

- name: 🔐 Validate the shared release-asset download
if: needs.changes.outputs.k8s == 'true'
run: |
shellcheck .github/scripts/release-asset-download.sh scripts/tests/test-release-asset-download.sh
bash scripts/tests/test-release-asset-download.sh

- name: 🔐 Exercise verified supply-chain tools
if: needs.changes.outputs.k8s == 'true'
env:
# Download through the authenticated asset API first (#5942).
GITHUB_TOKEN: ${{ github.token }}
run: |
.github/scripts/setup-supply-chain-tools.sh
/usr/local/bin/syft scan dir:. --output cyclonedx-json=tool-integrity.cdx.json
Expand Down Expand Up @@ -2325,6 +2340,9 @@ jobs:

- name: Install checksum-verified Trivy for disposition boundaries
if: needs.changes.outputs.k8s == 'true'
env:
# Download through the authenticated asset API first (#5942).
GITHUB_TOKEN: ${{ github.token }}
run: bash .github/scripts/setup-supply-chain-tools.sh trivy-boundary

- name: 🧱 Validate vendored-operator disposition boundary
Expand Down Expand Up @@ -3870,6 +3888,9 @@ jobs:
persist-credentials: false

- name: ⚙️ Setup talosctl
env:
# Download through the authenticated asset API first (#5942).
GITHUB_TOKEN: ${{ github.token }}
run: |
.github/scripts/setup-talosctl.sh

Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/publish-dex-security-image.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ on:
- go.mod
- go.sum
- .github/scripts/setup-supply-chain-tools.sh
- .github/scripts/release-asset-download.sh
- k8s/bases/infrastructure/controllers/dex/DexSecurity.Dockerfile
- k8s/bases/infrastructure/controllers/dex/dex-v2.46.1-network.patch
- k8s/bases/infrastructure/controllers/dex/IMAGE-NOTICE
Expand All @@ -22,6 +23,7 @@ on:
- go.mod
- go.sum
- .github/scripts/setup-supply-chain-tools.sh
- .github/scripts/release-asset-download.sh
- k8s/bases/infrastructure/controllers/dex/DexSecurity.Dockerfile
- k8s/bases/infrastructure/controllers/dex/dex-v2.46.1-network.patch
- k8s/bases/infrastructure/controllers/dex/IMAGE-NOTICE
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/publish-longhorn-v1-retirement.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ name: Publish Longhorn V1 Retirement Image
paths:
- .github/workflows/publish-longhorn-v1-retirement.yaml
- .github/scripts/setup-supply-chain-tools.sh
- .github/scripts/release-asset-download.sh
- k8s/providers/hetzner/infrastructure/controllers/longhorn/InstanceManagerRetirement.Dockerfile
- k8s/providers/hetzner/infrastructure/controllers/longhorn/instance-manager-v1.13.0-retirement.patch
push:
branches: [main]
paths:
- .github/workflows/publish-longhorn-v1-retirement.yaml
- .github/scripts/setup-supply-chain-tools.sh
- .github/scripts/release-asset-download.sh
- k8s/providers/hetzner/infrastructure/controllers/longhorn/InstanceManagerRetirement.Dockerfile
- k8s/providers/hetzner/infrastructure/controllers/longhorn/instance-manager-v1.13.0-retirement.patch

Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/validate-main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ jobs:
shellcheck scripts/tests/test-webapp-compiled-scan.sh
bash scripts/tests/test-webapp-compiled-scan.sh
- name: 🔏 Install reviewed public-image verification tools
env:
# Download through the authenticated asset API first (#5942).
GITHUB_TOKEN: ${{ github.token }}
run: bash .github/scripts/setup-supply-chain-tools.sh
- name: Validate the hosted runtime script
run: |
Expand Down
Loading
Loading