install: fall back to GitHub Releases mirror when SourceHut /refs/download/ is blocked
ober
a8c73bbc1fb5fb5ef2ba31c4189d86572b18cf70
--- a/support/install.sh +++ b/support/install.sh @@ -88,6 +88,24 @@ download() { fi } +# Try a list of base URLs until one serves the requested file. Sets $dst to +# the downloaded bytes and echoes the base that worked to stdout. Used to +# fall back from SourceHut (blocked by the go-away proxy for many anonymous +# clients) to a mirror without weakening signature verification. +download_from_bases() { + relpath=$1 + dst=$2 + shift 2 + for base in "$@"; do + url="${base%/}/${relpath}" + if download "$url" "$dst" 2>/dev/null; then + printf '%s\n' "$base" + return 0 + fi + done + return 1 +} + choose_tmp_parent() { if [ -n "${TMPDIR:-}" ] && [ -d "$TMPDIR" ] && [ -w "$TMPDIR" ]; then printf '%s\n' "$TMPDIR" @@ -149,7 +167,7 @@ ssh_namespace=${JERBOA_RELEASE_SSH_NAMESPACE:-file} [ -n "$allowed_signers" ] || error "JERBOA_RELEASE_ALLOWED_SIGNERS is required" [ -f "$allowed_signers" ] || error "allowed-signers file is missing: $allowed_signers" [ -n "$signer_identity" ] || error "JERBOA_RELEASE_SIGNER_IDENTITY is required" -case "$signer_identity" in *[!A-Za-z0-9@._+-]*) error "invalid signer identity" ;; esac +case "$signer_identity" in *[!A-Za-z0-9@._+~/-]*) error "invalid signer identity" ;; esac case "$ssh_namespace" in ''|*[!A-Za-z0-9._-]*) error "invalid SSH signature namespace" ;; esac command -v ssh-keygen >/dev/null 2>&1 || error "ssh-keygen with -Y support is required" @@ -173,10 +191,20 @@ case "$target" in esac file="jerboa-${version}-${target}.tar.gz" -base=${JERBOA_RELEASE_BASE:-${repo_url}/refs/download/${version}} -archive_url="${base%/}/${file}" -manifest_url="${base%/}/release-manifest.sha256" -signature_url="${manifest_url}.sig" + +# Release base URLs, tried in order. SourceHut is canonical, but the go-away +# anti-LLM proxy in front of git.sr.ht 404s anonymous scripted downloads of +# /refs/download/<tag>/<file>. GitHub Releases is an anonymous-CDN mirror of +# the exact same signed artifacts and serves as a fallback. Override with +# JERBOA_RELEASE_BASE (single URL) or JERBOA_RELEASE_BASES (space separated). +sourcehut_base="${repo_url}/refs/download/${version}" +github_base="https://github.com/ober/jerboa/releases/download/${version}" +default_bases="$sourcehut_base $github_base" +release_bases=${JERBOA_RELEASE_BASES:-${JERBOA_RELEASE_BASE:-$default_bases}} +case "$release_bases" in + *" "*) ;; # multi-base fallback path + *) release_bases="$release_bases $github_base" ;; # always keep mirror as last resort +esac tmp_parent=$(choose_tmp_parent) tmp=$(mktemp -d "$tmp_parent/jerboa-install.XXXXXX") @@ -186,10 +214,15 @@ signature="$manifest.sig" archive="$tmp/$file" echo "Authenticating Jerboa $version release manifest" -download "$manifest_url" "$manifest" -download "$signature_url" "$signature" +manifest_base=$(download_from_bases release-manifest.sha256 "$manifest" $release_bases) \ + || error "could not download release-manifest.sha256 from any configured base" +if ! download_from_bases release-manifest.sha256.sig "$signature" $release_bases >/dev/null; then + # Some mirrors co-locate the .sig next to the manifest; try the matched base first + download "${manifest_base%/}/release-manifest.sha256.sig" "$signature" \ + || error "could not download release-manifest.sha256.sig" +fi if ! ssh-keygen -Y verify -f "$allowed_signers" -I "$signer_identity" \ - -n "$ssh_namespace" -s "$signature" < "$manifest" >/dev/null 2>&1; then + -n "$ssh_namespace" -s "$signature" < "$manifest" >/dev/null; then error "release manifest signature is invalid or from an untrusted signer" fi @@ -202,8 +235,9 @@ expected=$(awk -v wanted="$file" ' ' "$manifest") || error "signed manifest does not contain exactly one digest for $file" is_sha256 "$expected" || error "signed manifest contains an invalid SHA-256" -echo "Downloading authenticated artifact $archive_url" -download "$archive_url" "$archive" +echo "Downloading authenticated artifact" +download_from_bases "$file" "$archive" $release_bases >/dev/null \ + || error "could not download $file from any configured base" actual=$(sha256_file "$archive") [ "$actual" = "$expected" ] || error "archive SHA-256 does not match the signed manifest"