mirror of
https://github.com/community-scripts/ProxmoxVE.git
synced 2026-03-30 14:52:59 +02:00
Improve robustness of package updates and installs by adding CDN/mirror fallback logic for Alpine and Debian/Ubuntu workflows. - misc/alpine-install.func: update_os() now attempts apk -U upgrade and, on failure, iterates a shuffled list of alternate Alpine mirrors, writes /etc/apk/repositories, tests the mirror and falls back with clear warnings/errors if none succeed. - misc/build.func: when apk add inside an Alpine container fails, run an in-container mirror scan that rewrites /etc/apk/repositories and retries apk update/add against multiple mirrors. Added a check to inject public DNS (8.8.8.8/1.1.1.1) into containers when APT repo DNS resolution fails. - misc/build.func (Debian/Ubuntu path): expanded apt-get failure handling to detect the failing mirror, then run a multi-phase mirror scanner (global → primary → regional), detect hash/SSL errors, and prompt the user for a custom mirror if automatic attempts fail. - misc/install.func: added apt_update_safe() implementing the apt-get update fallback logic (mirror lists, regional selection by timezone, reachable scanning, hash/SSL detection, and user prompt), returning failure if all mirrors fail. These changes aim to mitigate CDN outages, broken CDNs/mirrors, or DNS issues during container builds and OS updates, improving reliability and providing clear messages and fallbacks when automatic fixes are exhausted.
241 lines
8.9 KiB
Bash
241 lines
8.9 KiB
Bash
# Copyright (c) 2021-2026 community-scripts ORG
|
|
# Author: tteck (tteckster)
|
|
# Co-Author: MickLesk
|
|
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
|
|
|
if ! command -v curl >/dev/null 2>&1; then
|
|
apk update && apk add curl >/dev/null 2>&1
|
|
fi
|
|
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
|
|
|
|
# Persist diagnostics setting inside container (exported from build.func)
|
|
# so addon scripts running later can find the user's choice
|
|
if [[ ! -f /usr/local/community-scripts/diagnostics ]]; then
|
|
mkdir -p /usr/local/community-scripts
|
|
echo "DIAGNOSTICS=${DIAGNOSTICS:-no}" >/usr/local/community-scripts/diagnostics
|
|
fi
|
|
|
|
# Get LXC IP address (must be called INSIDE container, after network is up)
|
|
get_lxc_ip
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# post_progress_to_api()
|
|
#
|
|
# - Lightweight progress ping from inside the container
|
|
# - Updates the existing telemetry record status
|
|
# - Arguments:
|
|
# * $1: status (optional, default: "configuring")
|
|
# - Signals that the installation is actively progressing (not stuck)
|
|
# - Fire-and-forget: never blocks or fails the script
|
|
# - Only executes if DIAGNOSTICS=yes and RANDOM_UUID is set
|
|
# ------------------------------------------------------------------------------
|
|
post_progress_to_api() {
|
|
command -v curl &>/dev/null || return 0
|
|
[[ "${DIAGNOSTICS:-no}" == "no" ]] && return 0
|
|
[[ -z "${RANDOM_UUID:-}" ]] && return 0
|
|
|
|
local progress_status="${1:-configuring}"
|
|
|
|
curl -fsS -m 5 -X POST "https://telemetry.community-scripts.org/telemetry" \
|
|
-H "Content-Type: application/json" \
|
|
-d "{\"random_id\":\"${RANDOM_UUID}\",\"execution_id\":\"${EXECUTION_ID:-${RANDOM_UUID}}\",\"type\":\"lxc\",\"nsapp\":\"${app:-unknown}\",\"status\":\"${progress_status}\"}" &>/dev/null || true
|
|
}
|
|
|
|
# This function enables IPv6 if it's not disabled and sets verbose mode
|
|
verb_ip6() {
|
|
set_std_mode # Set STD mode based on VERBOSE
|
|
|
|
if [ "${IPV6_METHOD:-}" = "disable" ]; then
|
|
msg_info "Disabling IPv6 (this may affect some services)"
|
|
$STD sysctl -w net.ipv6.conf.all.disable_ipv6=1
|
|
$STD sysctl -w net.ipv6.conf.default.disable_ipv6=1
|
|
$STD sysctl -w net.ipv6.conf.lo.disable_ipv6=1
|
|
mkdir -p /etc/sysctl.d
|
|
$STD tee /etc/sysctl.d/99-disable-ipv6.conf >/dev/null <<EOF
|
|
net.ipv6.conf.all.disable_ipv6 = 1
|
|
net.ipv6.conf.default.disable_ipv6 = 1
|
|
net.ipv6.conf.lo.disable_ipv6 = 1
|
|
EOF
|
|
$STD rc-update add sysctl default
|
|
msg_ok "Disabled IPv6"
|
|
fi
|
|
}
|
|
|
|
# This function sets up the Container OS by generating the locale, setting the timezone, and checking the network connection
|
|
setting_up_container() {
|
|
msg_info "Setting up Container OS"
|
|
while [ $i -gt 0 ]; do
|
|
if [ "$(ip addr show | grep 'inet ' | grep -v '127.0.0.1' | awk '{print $2}' | cut -d'/' -f1)" != "" ]; then
|
|
break
|
|
fi
|
|
echo 1>&2 -en "${CROSS}${RD} No Network! "
|
|
sleep $RETRY_EVERY
|
|
i=$((i - 1))
|
|
done
|
|
|
|
if [ "$(ip addr show | grep 'inet ' | grep -v '127.0.0.1' | awk '{print $2}' | cut -d'/' -f1)" = "" ]; then
|
|
echo 1>&2 -e "\n${CROSS}${RD} No Network After $RETRY_NUM Tries${CL}"
|
|
echo -e "${NETWORK}Check Network Settings"
|
|
exit 121
|
|
fi
|
|
msg_ok "Set up Container OS"
|
|
msg_ok "Network Connected: ${BL}$(ip addr show | grep 'inet ' | awk '{print $2}' | cut -d'/' -f1 | tail -n1)${CL}"
|
|
post_progress_to_api
|
|
}
|
|
|
|
# This function checks the network connection by pinging a known IP address and prompts the user to continue if the internet is not connected
|
|
network_check() {
|
|
set +e
|
|
trap - ERR
|
|
ipv4_connected=false
|
|
|
|
# Check IPv4 connectivity to Cloudflare, Google & Quad9 DNS servers
|
|
if ping -c 1 -W 1 1.1.1.1 &>/dev/null || ping -c 1 -W 1 8.8.8.8 &>/dev/null || ping -c 1 -W 1 9.9.9.9 &>/dev/null; then
|
|
msg_ok "IPv4 Internet Connected"
|
|
ipv4_connected=true
|
|
else
|
|
msg_error "IPv4 Internet Not Connected"
|
|
fi
|
|
|
|
if [[ $ipv4_connected == false ]]; then
|
|
read -r -p "No Internet detected, would you like to continue anyway? <y/N> " prompt
|
|
if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then
|
|
echo -e "${INFO}${RD}Expect Issues Without Internet${CL}"
|
|
else
|
|
echo -e "${NETWORK}Check Network Settings"
|
|
exit 122
|
|
fi
|
|
fi
|
|
|
|
# DNS resolution checks for GitHub-related domains
|
|
GIT_HOSTS=("github.com" "raw.githubusercontent.com" "api.github.com" "git.community-scripts.org")
|
|
GIT_STATUS="Git DNS:"
|
|
DNS_FAILED=false
|
|
|
|
for HOST in "${GIT_HOSTS[@]}"; do
|
|
RESOLVEDIP=$(getent hosts "$HOST" | awk '{ print $1 }' | grep -E '(^([0-9]{1,3}\.){3}[0-9]{1,3}$)|(^[a-fA-F0-9:]+$)' | head -n1)
|
|
if [[ -z "$RESOLVEDIP" ]]; then
|
|
GIT_STATUS+="$HOST:($DNSFAIL)"
|
|
DNS_FAILED=true
|
|
else
|
|
GIT_STATUS+=" $HOST:($DNSOK)"
|
|
fi
|
|
done
|
|
|
|
if [[ "$DNS_FAILED" == true ]]; then
|
|
fatal "$GIT_STATUS"
|
|
else
|
|
msg_ok "$GIT_STATUS"
|
|
fi
|
|
|
|
set -e
|
|
trap 'error_handler $LINENO "$BASH_COMMAND"' ERR
|
|
}
|
|
|
|
# This function updates the Container OS by running apk upgrade with mirror fallback
|
|
update_os() {
|
|
msg_info "Updating Container OS"
|
|
if ! $STD apk -U upgrade; then
|
|
msg_warn "apk update failed (dl-cdn.alpinelinux.org), trying alternate mirrors..."
|
|
local alpine_mirrors="mirror.init7.net ftp.halifax.rwth-aachen.de mirrors.edge.kernel.org alpine.mirror.wearetriple.com mirror.leaseweb.com uk.alpinelinux.org dl-2.alpinelinux.org dl-4.alpinelinux.org"
|
|
local apk_ok=false
|
|
for m in $(printf '%s\n' $alpine_mirrors | shuf); do
|
|
if timeout 2 bash -c "echo >/dev/tcp/$m/80" 2>/dev/null; then
|
|
msg_info "Attempting mirror: ${m}"
|
|
cat <<EOF >/etc/apk/repositories
|
|
http://$m/alpine/latest-stable/main
|
|
http://$m/alpine/latest-stable/community
|
|
EOF
|
|
if $STD apk -U upgrade; then
|
|
msg_ok "CDN set to ${m}: tests passed"
|
|
apk_ok=true
|
|
break
|
|
else
|
|
msg_warn "Mirror ${m} failed"
|
|
fi
|
|
fi
|
|
done
|
|
if [[ "$apk_ok" != true ]]; then
|
|
msg_error "All Alpine mirrors failed. Check network or try again later."
|
|
exit 1
|
|
fi
|
|
fi
|
|
local tools_content
|
|
tools_content=$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/tools.func) || {
|
|
msg_error "Failed to download tools.func"
|
|
exit 115
|
|
}
|
|
source /dev/stdin <<<"$tools_content"
|
|
if ! declare -f fetch_and_deploy_gh_release >/dev/null 2>&1; then
|
|
msg_error "tools.func loaded but incomplete — missing expected functions"
|
|
exit 115
|
|
fi
|
|
msg_ok "Updated Container OS"
|
|
post_progress_to_api
|
|
}
|
|
|
|
# This function modifies the message of the day (motd) and SSH settings
|
|
motd_ssh() {
|
|
echo "export TERM='xterm-256color'" >>/root/.bashrc
|
|
|
|
PROFILE_FILE="/etc/profile.d/00_lxc-details.sh"
|
|
echo "echo -e \"\"" >"$PROFILE_FILE"
|
|
echo -e "echo -e \"${BOLD}${APPLICATION} LXC Container${CL}"\" >>"$PROFILE_FILE"
|
|
echo -e "echo -e \"${TAB}${GATEWAY}${YW} Provided by: ${GN}community-scripts ORG ${YW}| GitHub: ${GN}https://github.com/community-scripts/ProxmoxVE${CL}\"" >>"$PROFILE_FILE"
|
|
echo "echo \"\"" >>"$PROFILE_FILE"
|
|
echo -e "echo -e \"${TAB}${OS}${YW} OS: ${GN}\$(grep ^NAME /etc/os-release | cut -d= -f2 | tr -d '\"') - Version: \$(grep ^VERSION_ID /etc/os-release | cut -d= -f2 | tr -d '\"')${CL}\"" >>"$PROFILE_FILE"
|
|
echo -e "echo -e \"${TAB}${HOSTNAME}${YW} Hostname: ${GN}\$(hostname)${CL}\"" >>"$PROFILE_FILE"
|
|
echo -e "echo -e \"${TAB}${INFO}${YW} IP Address: ${GN}\$(ip -4 addr show eth0 | awk '/inet / {print \$2}' | cut -d/ -f1 | head -n 1)${CL}\"" >>"$PROFILE_FILE"
|
|
|
|
# Configure SSH if enabled
|
|
if [[ "${SSH_ROOT}" == "yes" ]]; then
|
|
# Enable sshd service
|
|
$STD rc-update add sshd
|
|
# Allow root login via SSH
|
|
sed -i "s/#PermitRootLogin prohibit-password/PermitRootLogin yes/g" /etc/ssh/sshd_config
|
|
# Start the sshd service
|
|
$STD /etc/init.d/sshd start
|
|
fi
|
|
post_progress_to_api
|
|
}
|
|
|
|
# Validate Timezone for some LXC's
|
|
validate_tz() {
|
|
[[ -f "/usr/share/zoneinfo/$1" ]]
|
|
}
|
|
|
|
# This function customizes the container and enables passwordless login for the root user
|
|
customize() {
|
|
if [[ "$PASSWORD" == "" ]]; then
|
|
msg_info "Customizing Container"
|
|
passwd -d root >/dev/null 2>&1
|
|
|
|
# Ensure agetty is available
|
|
apk add --no-cache --force-broken-world util-linux >/dev/null 2>&1
|
|
|
|
# Create persistent autologin boot script
|
|
mkdir -p /etc/local.d
|
|
cat <<'EOF' >/etc/local.d/autologin.start
|
|
#!/bin/sh
|
|
sed -i 's|^tty1::respawn:.*|tty1::respawn:/sbin/agetty --autologin root --noclear tty1 38400 linux|' /etc/inittab
|
|
kill -HUP 1
|
|
EOF
|
|
touch /root/.hushlogin
|
|
|
|
chmod +x /etc/local.d/autologin.start
|
|
rc-update add local >/dev/null 2>&1
|
|
|
|
# Apply autologin immediately for current session
|
|
/etc/local.d/autologin.start
|
|
|
|
msg_ok "Customized Container"
|
|
fi
|
|
|
|
echo "bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/${app}.sh)\"" >/usr/bin/update
|
|
chmod +x /usr/bin/update
|
|
post_progress_to_api
|
|
}
|