From 6ff1e953344dbef2b8564c7141acdb9af0a9bda6 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Wed, 29 Jul 2026 14:11:08 +0200 Subject: [PATCH] core: Harden remote func bootstrapping with dns issues (#16146) * Harden remote func bootstrapping Replace bare `source <(curl ...)` calls with a `_bootstrap_source` helper that validates both the download exit code and the presence of a probe function after sourcing. This prevents silent failures when DNS is not yet available in a freshly booted container or when a download fails mid-stream. Applies to build.func, install.func, and alpine-install.func. Also adds retry flags and a connect timeout to curl/wget. * Refactor bootstrap error handling with helper Extract repeated fatal-error logic into a `_bootstrap_die` helper across alpine-install.func, build.func, and install.func. The helper uses `msg_error` when available, falling back to stderr, then exits with code 115. Inline error blocks are replaced with cleaner one-liner `|| _bootstrap_die` calls. --- misc/alpine-install.func | 25 +++++++++++++++++++++-- misc/build.func | 43 +++++++++++++++++++++++++++++----------- misc/install.func | 25 +++++++++++++++++++++-- 3 files changed, 77 insertions(+), 16 deletions(-) diff --git a/misc/alpine-install.func b/misc/alpine-install.func index 695a55b1d..9dd3548d5 100644 --- a/misc/alpine-install.func +++ b/misc/alpine-install.func @@ -10,8 +10,29 @@ fi # must write local failure artifacts instead of talking to the telemetry API # (the host is the single telemetry reporter). export TELEMETRY_CONTEXT="container" -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/core.func) -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/error_handler.func) +# A freshly booted container may not have DNS up yet. `source <(curl ...)` hides +# curl's exit code, so a failed download would silently source an empty stream. +_bootstrap_die() { + if declare -f msg_error >/dev/null 2>&1; then + msg_error "$1" + else + echo "FATAL: $1" >&2 + fi + exit 115 +} + +_bootstrap_source() { + local url="$1" probe="$2" content + content=$(curl -fsSL --connect-timeout 10 --retry 5 --retry-connrefused --retry-delay 2 "$url") || + _bootstrap_die "Failed to download ${url##*/}" + source /dev/stdin <<<"$content" + declare -f "$probe" >/dev/null 2>&1 || + _bootstrap_die "${url##*/} loaded but incomplete (missing ${probe})" +} + +_FUNC_BASE="https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc" +_bootstrap_source "$_FUNC_BASE/core.func" load_functions +_bootstrap_source "$_FUNC_BASE/error_handler.func" catch_errors load_functions catch_errors diff --git a/misc/build.func b/misc/build.func index 8246da3f8..561f0f62b 100644 --- a/misc/build.func +++ b/misc/build.func @@ -88,19 +88,38 @@ variables() { fi } -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) +# `source <(curl ...)` hides the download exit code, so a failed fetch would +# silently source an empty stream and leave the helpers undefined. +_bootstrap_die() { + if declare -f msg_error >/dev/null 2>&1; then + msg_error "$1" + else + echo "FATAL: $1" >&2 + fi + exit 115 +} -if command -v curl >/dev/null 2>&1; then - source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/core.func) - source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/error_handler.func) - load_functions - catch_errors -elif command -v wget >/dev/null 2>&1; then - source <(wget -qO- https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/core.func) - source <(wget -qO- https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/error_handler.func) - load_functions - catch_errors -fi +_bootstrap_source() { + local url="$1" probe="$2" content="" + if command -v curl >/dev/null 2>&1; then + content=$(curl -fsSL --connect-timeout 10 --retry 5 --retry-connrefused --retry-delay 2 "$url") || content="" + elif command -v wget >/dev/null 2>&1; then + content=$(wget -qO- --tries=5 --timeout=10 "$url") || content="" + else + _bootstrap_die "Neither curl nor wget available" + fi + [[ -n "$content" ]] || _bootstrap_die "Failed to download ${url##*/}" + source /dev/stdin <<<"$content" + declare -f "$probe" >/dev/null 2>&1 || + _bootstrap_die "${url##*/} loaded but incomplete (missing ${probe})" +} + +_FUNC_BASE="https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc" +_bootstrap_source "$_FUNC_BASE/api.func" post_to_api +_bootstrap_source "$_FUNC_BASE/core.func" load_functions +_bootstrap_source "$_FUNC_BASE/error_handler.func" catch_errors +load_functions +catch_errors # ============================================================================== # SECTION 2: PRE-FLIGHT CHECKS & SYSTEM VALIDATION diff --git a/misc/install.func b/misc/install.func index 48d8b07be..922f24993 100644 --- a/misc/install.func +++ b/misc/install.func @@ -37,8 +37,29 @@ fi # (the host is the single telemetry reporter). export TELEMETRY_CONTEXT="container" -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/core.func) -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/error_handler.func) +# A freshly booted container may not have DNS up yet. `source <(curl ...)` hides +# curl's exit code, so a failed download would silently source an empty stream. +_bootstrap_die() { + if declare -f msg_error >/dev/null 2>&1; then + msg_error "$1" + else + echo "FATAL: $1" >&2 + fi + exit 115 +} + +_bootstrap_source() { + local url="$1" probe="$2" content + content=$(curl -fsSL --connect-timeout 10 --retry 5 --retry-connrefused --retry-delay 2 "$url") || + _bootstrap_die "Failed to download ${url##*/}" + source /dev/stdin <<<"$content" + declare -f "$probe" >/dev/null 2>&1 || + _bootstrap_die "${url##*/} loaded but incomplete (missing ${probe})" +} + +_FUNC_BASE="https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc" +_bootstrap_source "$_FUNC_BASE/core.func" load_functions +_bootstrap_source "$_FUNC_BASE/error_handler.func" catch_errors load_functions catch_errors