Compare commits

..

10 Commits

Author SHA1 Message Date
CanbiZ (MickLesk)
b68d65c64d feat(preflight): add package repository reachability check
- Test Debian (deb.debian.org, security.debian.org), Ubuntu (archive.ubuntu.com,
  security.ubuntu.com) and Alpine (dl-cdn.alpinelinux.org) repos via HTTP HEAD
- Selects target repos based on var_os and var_version from the CT script
- Warning only (not a hard fail) to support local mirrors and apt-cacher-ng setups
- Shows which repo host failed and with what HTTP status code
- Catches connectivity issues before container creation and apt-get/apk failures
2026-03-03 14:56:47 +01:00
CanbiZ (MickLesk)
5f9f283fa6 feat(preflight): add enterprise repo subscription check
- New preflight_repo_access() warns if enterprise repos are active without subscription
- Scans /etc/apt/sources.list.d/ for enterprise.proxmox.com entries
- Tests HTTP access (detects 401/403 Unauthorized)
- Warning only — not a blocker (packages come from pve-no-subscription repo)
2026-03-03 14:42:46 +01:00
CanbiZ (MickLesk)
f81d0b5188 refactor(preflight): move to install_script, clean UX flow
- Move run_preflight from build_container() into install_script() after header_info
- Shows: Header → preflight checks → 2s pause → clear (via next header_info call)
- On failure: show summary + exit cleanly (no ERR trap)
- Change all return 1 → return 0 in preflight functions (prevents ERR trap under set -Ee)
- Remove PREFLIGHT_DONE guard from build_container (no longer needed)
2026-03-03 14:09:46 +01:00
community-scripts-pr-app[bot]
42e546904f Update .app files (#12504)
Co-authored-by: GitHub Actions <github-actions[bot]@users.noreply.github.com>
2026-03-03 11:05:11 +01:00
community-scripts-pr-app[bot]
4045824bf1 Update CHANGELOG.md (#12506)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-03 10:03:23 +00:00
community-scripts-pr-app[bot]
738cbfd1ae Update CHANGELOG.md (#12505)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-03 10:03:06 +00:00
community-scripts-pr-app[bot]
278c3cc2d8 Update date in json (#12503)
Co-authored-by: GitHub Actions <github-actions[bot]@users.noreply.github.com>
2026-03-03 10:02:59 +00:00
CanbiZ (MickLesk)
14a7ac2618 Tinyauth: v5 Support & add Debian Version (#12501) 2026-03-03 11:02:38 +01:00
community-scripts-pr-app[bot]
a7699361c1 Update CHANGELOG.md (#12502)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-03 09:26:38 +00:00
Copilot
82a0893036 Remove Unifi Network Server scripts (dead APT repo) (#12500)
* Initial plan

* remove Unifi (not unifi-os-server) CT, install, JSON, and header files

Co-authored-by: MickLesk <47820557+MickLesk@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: MickLesk <47820557+MickLesk@users.noreply.github.com>
2026-03-03 10:26:15 +01:00
8 changed files with 749 additions and 40 deletions

View File

@@ -412,6 +412,14 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
## 2026-03-03
### 🆕 New Scripts
- Tinyauth: v5 Support & add Debian Version [@MickLesk](https://github.com/MickLesk) ([#12501](https://github.com/community-scripts/ProxmoxVE/pull/12501))
### 🗑️ Deleted Scripts
- Remove Unifi Network Server scripts (dead APT repo) [@Copilot](https://github.com/Copilot) ([#12500](https://github.com/community-scripts/ProxmoxVE/pull/12500))
### 🌐 Website
- #### 🐞 Bug Fixes

View File

@@ -35,6 +35,20 @@ function update_script() {
$STD service tinyauth stop
msg_ok "Service Stopped"
if [[ -f /opt/tinyauth/.env ]] && ! grep -q "^TINYAUTH_" /opt/tinyauth/.env; then
msg_info "Migrating .env to v5 format"
sed -i \
-e 's/^DATABASE_PATH=/TINYAUTH_DATABASE_PATH=/' \
-e 's/^USERS=/TINYAUTH_AUTH_USERS=/' \
-e "s/^USERS='/TINYAUTH_AUTH_USERS='/" \
-e 's/^APP_URL=/TINYAUTH_APPURL=/' \
-e 's/^SECRET=/TINYAUTH_AUTH_SECRET=/' \
-e 's/^PORT=/TINYAUTH_SERVER_PORT=/' \
-e 's/^ADDRESS=/TINYAUTH_SERVER_ADDRESS=/' \
/opt/tinyauth/.env
msg_ok "Migrated .env to v5 format"
fi
msg_info "Updating Tinyauth"
rm -f /opt/tinyauth/tinyauth
curl -fsSL "https://github.com/steveiliop56/tinyauth/releases/download/v${RELEASE}/tinyauth-amd64" -o /opt/tinyauth/tinyauth

6
ct/headers/tinyauth Normal file
View File

@@ -0,0 +1,6 @@
_______ __ __
/_ __(_)___ __ ______ ___ __/ /_/ /_
/ / / / __ \/ / / / __ `/ / / / __/ __ \
/ / / / / / / /_/ / /_/ / /_/ / /_/ / / /
/_/ /_/_/ /_/\__, /\__,_/\__,_/\__/_/ /_/
/____/

53
ct/tinyauth.sh Normal file
View File

@@ -0,0 +1,53 @@
#!/usr/bin/env bash
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func)
# Copyright (c) 2021-2026 community-scripts ORG
# Author: MickLesk (CanbiZ)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://github.com/steveiliop56/tinyauth
APP="Tinyauth"
var_tags="${var_tags:-auth}"
var_cpu="${var_cpu:-1}"
var_ram="${var_ram:-512}"
var_disk="${var_disk:-4}"
var_os="${var_os:-debian}"
var_version="${var_version:-13}"
var_unprivileged="${var_unprivileged:-1}"
header_info "$APP"
variables
color
catch_errors
function update_script() {
header_info
check_container_storage
check_container_resources
if [[ ! -d /opt/tinyauth ]]; then
msg_error "No ${APP} Installation Found!"
exit
fi
if check_for_gh_release "tinyauth" "steveiliop56/tinyauth"; then
msg_info "Stopping Service"
systemctl stop tinyauth
msg_ok "Stopped Service"
fetch_and_deploy_gh_release "tinyauth" "steveiliop56/tinyauth" "singlefile" "latest" "/opt/tinyauth" "tinyauth-amd64"
msg_info "Starting Service"
systemctl start tinyauth
msg_ok "Started Service"
msg_ok "Updated successfully!"
fi
exit
}
start
build_container
description
msg_ok "Completed successfully!\n"
echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
echo -e "${INFO}${YW} Access it using the following URL:${CL}"
echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}"

View File

@@ -1,10 +1,10 @@
{
"name": "Alpine-Tinyauth",
"slug": "alpine-tinyauth",
"name": "Tinyauth",
"slug": "tinyauth",
"categories": [
6
],
"date_created": "2025-05-06",
"date_created": "2026-03-03",
"type": "ct",
"updateable": true,
"privileged": false,
@@ -17,13 +17,13 @@
"install_methods": [
{
"type": "default",
"script": "ct/alpine-tinyauth.sh",
"script": "ct/tinyauth.sh",
"resources": {
"cpu": 1,
"ram": 256,
"hdd": 2,
"os": "alpine",
"version": "3.23"
"ram": 512,
"hdd": 4,
"os": "debian",
"version": "13"
}
},
{

View File

@@ -36,9 +36,9 @@ msg_ok "Installed Tinyauth"
read -r -p "${TAB3}Enter your Tinyauth subdomain (e.g. https://tinyauth.example.com): " app_url
cat <<EOF >/opt/tinyauth/.env
DATABASE_PATH=/opt/tinyauth/database.db
USERS='${USER}'
APP_URL=${app_url}
TINYAUTH_DATABASE_PATH=/opt/tinyauth/database.db
TINYAUTH_AUTH_USERS='${USER}'
TINYAUTH_APPURL=${app_url}
EOF
msg_info "Creating Service"

View File

@@ -0,0 +1,62 @@
#!/usr/bin/env bash
# Copyright (c) 2021-2026 community-scripts ORG
# Author: MickLesk (CanbiZ)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://github.com/steveiliop56/tinyauth
source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
color
verb_ip6
catch_errors
setting_up_container
network_check
update_os
msg_info "Installing Dependencies"
$STD apt install -y \
openssl \
apache2-utils
msg_ok "Installed Dependencies"
fetch_and_deploy_gh_release "tinyauth" "steveiliop56/tinyauth" "singlefile" "latest" "/opt/tinyauth" "tinyauth-amd64"
msg_info "Setting up Tinyauth"
PASS=$(openssl rand -base64 8 | tr -dc 'a-zA-Z0-9' | head -c 8)
USER=$(htpasswd -Bbn "tinyauth" "${PASS}")
cat <<EOF >/opt/tinyauth/credentials.txt
Tinyauth Credentials
Username: tinyauth
Password: ${PASS}
EOF
msg_ok "Set up Tinyauth"
read -r -p "${TAB3}Enter your Tinyauth subdomain (e.g. https://tinyauth.example.com): " app_url
msg_info "Creating Service"
cat <<EOF >/opt/tinyauth/.env
TINYAUTH_DATABASE_PATH=/opt/tinyauth/database.db
TINYAUTH_AUTH_USERS='${USER}'
TINYAUTH_APPURL=${app_url}
EOF
cat <<EOF >/etc/systemd/system/tinyauth.service
[Unit]
Description=Tinyauth Service
After=network.target
[Service]
Type=simple
EnvironmentFile=/opt/tinyauth/.env
ExecStart=/opt/tinyauth/tinyauth
WorkingDirectory=/opt/tinyauth
Restart=on-failure
[Install]
WantedBy=multi-user.target
EOF
systemctl enable -q --now tinyauth
msg_ok "Created Service"
motd_ssh
customize
cleanup_lxc

View File

@@ -100,58 +100,624 @@ fi
# ==============================================================================
# SECTION 2: PRE-FLIGHT CHECKS & SYSTEM VALIDATION
# ==============================================================================
#
# Runs comprehensive system checks BEFORE container creation to catch common
# issues early. This prevents users from going through the entire configuration
# menu only to have creation fail due to a system-level problem.
#
# Checks performed (via run_preflight):
# - Kernel: keyring limits (maxkeys/maxbytes for UID 100000)
# - Storage: rootdir support, vztmpl support, available space
# - Network: bridge availability, DNS resolution
# - Repos: enterprise repo subscription validation
# - Cluster: quorum status (if clustered)
# - Proxmox: LXC stack health, container ID availability
# - Template: download server reachability
#
# Design:
# - All checks run and results are collected (no exit on first failure)
# - Clear, actionable error messages with suggested fixes
# - Reports "aborted" status to telemetry (not "failed")
# - Uses existing exit codes for consistency with error_handler/api.func
#
# ==============================================================================
# --- Preflight tracking globals ---
PREFLIGHT_PASSED=0
PREFLIGHT_FAILED=0
PREFLIGHT_WARNINGS=0
PREFLIGHT_FAILURES=()
PREFLIGHT_EXIT_CODE=0
# ------------------------------------------------------------------------------
# maxkeys_check()
# preflight_pass() / preflight_fail() / preflight_warn()
#
# - Track individual check results
# - preflight_fail stores message + exit_code for summary
# ------------------------------------------------------------------------------
preflight_pass() {
local msg="$1"
((PREFLIGHT_PASSED++)) || true
echo -e " ${CM} ${GN}${msg}${CL}"
}
preflight_fail() {
local msg="$1"
local exit_code="${2:-1}"
((PREFLIGHT_FAILED++)) || true
PREFLIGHT_FAILURES+=("${exit_code}|${msg}")
[[ "$PREFLIGHT_EXIT_CODE" -eq 0 ]] && PREFLIGHT_EXIT_CODE="$exit_code"
echo -e " ${CROSS} ${RD}${msg}${CL}"
}
preflight_warn() {
local msg="$1"
((PREFLIGHT_WARNINGS++)) || true
echo -e " ${INFO} ${YW}${msg}${CL}"
}
# ------------------------------------------------------------------------------
# preflight_maxkeys()
#
# - Reads kernel keyring limits (maxkeys, maxbytes)
# - Checks current usage for LXC user (UID 100000)
# - Warns if usage is close to limits and suggests sysctl tuning
# - Exits if thresholds are exceeded
# - https://cleveruptime.com/docs/files/proc-key-users | https://docs.kernel.org/security/keys/core.html
# - https://cleveruptime.com/docs/files/proc-key-users
# - https://docs.kernel.org/security/keys/core.html
# ------------------------------------------------------------------------------
maxkeys_check() {
# Read kernel parameters
preflight_maxkeys() {
local per_user_maxkeys per_user_maxbytes
per_user_maxkeys=$(cat /proc/sys/kernel/keys/maxkeys 2>/dev/null || echo 0)
per_user_maxbytes=$(cat /proc/sys/kernel/keys/maxbytes 2>/dev/null || echo 0)
# Exit if kernel parameters are unavailable
if [[ "$per_user_maxkeys" -eq 0 || "$per_user_maxbytes" -eq 0 ]]; then
msg_error "Unable to read kernel key parameters. Ensure proper permissions."
exit 107
preflight_fail "Unable to read kernel key parameters" 107
echo -e " ${TAB}${INFO} Ensure proper permissions to /proc/sys/kernel/keys/"
return 0
fi
# Fetch key usage for user ID 100000 (typical for containers)
local used_lxc_keys used_lxc_bytes
used_lxc_keys=$(awk '/100000:/ {print $2}' /proc/key-users 2>/dev/null || echo 0)
used_lxc_bytes=$(awk '/100000:/ {split($5, a, "/"); print a[1]}' /proc/key-users 2>/dev/null || echo 0)
# Calculate thresholds and suggested new limits
threshold_keys=$((per_user_maxkeys - 100))
threshold_bytes=$((per_user_maxbytes - 1000))
new_limit_keys=$((per_user_maxkeys * 2))
new_limit_bytes=$((per_user_maxbytes * 2))
local threshold_keys=$((per_user_maxkeys - 100))
local threshold_bytes=$((per_user_maxbytes - 1000))
local new_limit_keys=$((per_user_maxkeys * 2))
local new_limit_bytes=$((per_user_maxbytes * 2))
# Check if key or byte usage is near limits
failure=0
local failure=0
if [[ "$used_lxc_keys" -gt "$threshold_keys" ]]; then
msg_warn "Key usage is near the limit (${used_lxc_keys}/${per_user_maxkeys})"
echo -e "${INFO} Suggested action: Set ${GN}kernel.keys.maxkeys=${new_limit_keys}${CL} in ${BOLD}/etc/sysctl.d/98-community-scripts.conf${CL}."
failure=1
fi
if [[ "$used_lxc_bytes" -gt "$threshold_bytes" ]]; then
msg_warn "Key byte usage is near the limit (${used_lxc_bytes}/${per_user_maxbytes})"
echo -e "${INFO} Suggested action: Set ${GN}kernel.keys.maxbytes=${new_limit_bytes}${CL} in ${BOLD}/etc/sysctl.d/98-community-scripts.conf${CL}."
failure=1
fi
# Provide next steps if issues are detected
if [[ "$failure" -eq 1 ]]; then
msg_error "Kernel key limits exceeded - see suggestions above"
exit 108
preflight_fail "Kernel key limits near threshold (keys: ${used_lxc_keys}/${per_user_maxkeys}, bytes: ${used_lxc_bytes}/${per_user_maxbytes})" 108
echo -e " ${TAB}${INFO} Set ${GN}kernel.keys.maxkeys=${new_limit_keys}${CL} and ${GN}kernel.keys.maxbytes=${new_limit_bytes}${CL}"
echo -e " ${TAB}${INFO} in ${BOLD}/etc/sysctl.d/98-community-scripts.conf${CL}, then run: ${GN}sysctl --system${CL}"
return 0
fi
# Silent success - only show errors if they exist
preflight_pass "Kernel key limits OK (keys: ${used_lxc_keys}/${per_user_maxkeys})"
return 0
}
# ------------------------------------------------------------------------------
# preflight_storage_rootdir()
#
# - Verifies at least one storage supports 'rootdir' content type
# - Without this, no LXC container can be created
# ------------------------------------------------------------------------------
preflight_storage_rootdir() {
local count
count=$(pvesm status -content rootdir 2>/dev/null | awk 'NR>1 {count++} END {print count+0}')
if [[ "$count" -eq 0 ]]; then
preflight_fail "No storage with 'rootdir' support found" 119
echo -e " ${TAB}${INFO} Enable 'rootdir' content on a storage in Datacenter → Storage"
return 0
fi
preflight_pass "Storage with 'rootdir' support available (${count} storage(s))"
return 0
}
# ------------------------------------------------------------------------------
# preflight_storage_vztmpl()
#
# - Verifies at least one storage supports 'vztmpl' content type
# - Required for downloading and storing OS templates
# ------------------------------------------------------------------------------
preflight_storage_vztmpl() {
local count
count=$(pvesm status -content vztmpl 2>/dev/null | awk 'NR>1 {count++} END {print count+0}')
if [[ "$count" -eq 0 ]]; then
preflight_fail "No storage with 'vztmpl' support found" 120
echo -e " ${TAB}${INFO} Enable 'vztmpl' content on a storage in Datacenter → Storage"
return 0
fi
preflight_pass "Storage with 'vztmpl' support available (${count} storage(s))"
return 0
}
# ------------------------------------------------------------------------------
# preflight_storage_space()
#
# - Checks if any rootdir-capable storage has enough free space
# - Uses the app-declared var_disk as minimum requirement
# ------------------------------------------------------------------------------
preflight_storage_space() {
local required_gb="${var_disk:-4}"
local required_kb=$((required_gb * 1024 * 1024))
local has_enough=0
local best_storage=""
local best_free=0
while read -r storage_name _ _ _ _ free_kb _; do
[[ -z "$storage_name" || -z "$free_kb" ]] && continue
[[ "$free_kb" == "0" ]] && continue
if [[ "$free_kb" -ge "$required_kb" ]]; then
has_enough=1
if [[ "$free_kb" -gt "$best_free" ]]; then
best_free="$free_kb"
best_storage="$storage_name"
fi
fi
done < <(pvesm status -content rootdir 2>/dev/null | awk 'NR>1')
if [[ "$has_enough" -eq 0 ]]; then
preflight_fail "No storage has enough space (need ${required_gb}GB for ${APP})" 214
echo -e " ${TAB}${INFO} Free up disk space or add a new storage with sufficient capacity"
return 0
fi
local best_free_fmt
best_free_fmt=$(numfmt --to=iec --from-unit=1024 --suffix=B --format %.1f "$best_free" 2>/dev/null || echo "${best_free}KB")
preflight_pass "Sufficient storage space (${best_storage}: ${best_free_fmt} free, need ${required_gb}GB)"
return 0
}
# ------------------------------------------------------------------------------
# preflight_network_bridge()
#
# - Checks if at least one network bridge exists (vmbr*)
# - Verifies vmbr0 specifically (default bridge used by most scripts)
# ------------------------------------------------------------------------------
preflight_network_bridge() {
local bridges
bridges=$(ip -o link show type bridge 2>/dev/null | grep -oE 'vmbr[0-9]+' | sort -u)
if [[ -z "$bridges" ]]; then
preflight_fail "No network bridge (vmbr*) found" 116
echo -e " ${TAB}${INFO} Create a bridge in Network → Create → Linux Bridge"
return 0
fi
if echo "$bridges" | grep -qx "vmbr0"; then
preflight_pass "Default network bridge vmbr0 available"
else
local first_bridge
first_bridge=$(echo "$bridges" | head -1)
preflight_warn "Default bridge vmbr0 not found, but ${first_bridge} is available"
echo -e " ${TAB}${INFO} Scripts default to vmbr0 — use Advanced Settings to select ${first_bridge}"
fi
return 0
}
# ------------------------------------------------------------------------------
# preflight_dns_resolution()
#
# - Tests if DNS resolution works (required for template downloads)
# - Tries multiple hosts to avoid false positives
# ------------------------------------------------------------------------------
preflight_dns_resolution() {
local test_hosts=("download.proxmox.com" "raw.githubusercontent.com" "community-scripts.org")
local resolved=0
for host in "${test_hosts[@]}"; do
if getent hosts "$host" &>/dev/null; then
resolved=1
break
fi
done
if [[ "$resolved" -eq 0 ]]; then
for host in "${test_hosts[@]}"; do
if command -v nslookup &>/dev/null && nslookup "$host" &>/dev/null; then
resolved=1
break
fi
done
fi
if [[ "$resolved" -eq 0 ]]; then
preflight_fail "DNS resolution failed — cannot reach template servers" 222
echo -e " ${TAB}${INFO} Check /etc/resolv.conf and network connectivity"
return 0
fi
preflight_pass "DNS resolution working"
return 0
}
# ------------------------------------------------------------------------------
# preflight_repo_access()
#
# - Checks if Proxmox enterprise repos are enabled without a valid subscription
# - Scans /etc/apt/sources.list.d/ for enterprise.proxmox.com entries
# - Tests HTTP access to detect 401 Unauthorized
# - Warning only (not a blocker — packages come from no-subscription repo)
# ------------------------------------------------------------------------------
preflight_repo_access() {
local enterprise_files
enterprise_files=$(grep -rlE '^\s*deb\s+https://enterprise\.proxmox\.com' /etc/apt/sources.list.d/ 2>/dev/null || true)
if [[ -z "$enterprise_files" ]]; then
preflight_pass "No enterprise repositories enabled"
return 0
fi
# Enterprise repo found — test if subscription is valid
local http_code
http_code=$(curl -sS -o /dev/null -w "%{http_code}" -m 5 "https://enterprise.proxmox.com/debian/pve/dists/" 2>/dev/null) || http_code="000"
if [[ "$http_code" == "401" || "$http_code" == "403" ]]; then
preflight_warn "Enterprise repo enabled without valid subscription (HTTP ${http_code})"
echo -e " ${TAB}${INFO} apt-get update will show '401 Unauthorized' errors"
echo -e " ${TAB}${INFO} Disable in ${GN}/etc/apt/sources.list.d/${CL} or add a subscription key"
return 0
fi
if [[ "$http_code" =~ ^2[0-9]{2}$ ]]; then
preflight_pass "Enterprise repository accessible (subscription valid)"
else
preflight_warn "Enterprise repo check inconclusive (HTTP ${http_code})"
fi
return 0
}
# ------------------------------------------------------------------------------
# preflight_cluster_quorum()
#
# - Checks cluster quorum status (only if node is part of a cluster)
# - Skipped on standalone nodes
# ------------------------------------------------------------------------------
preflight_cluster_quorum() {
if [[ ! -f /etc/pve/corosync.conf ]]; then
preflight_pass "Standalone node (no cluster quorum needed)"
return 0
fi
if pvecm status 2>/dev/null | awk -F':' '/^Quorate/ { exit ($2 ~ /Yes/) ? 0 : 1 }'; then
preflight_pass "Cluster is quorate"
return 0
fi
preflight_fail "Cluster is not quorate — container operations will fail" 210
echo -e " ${TAB}${INFO} Ensure all cluster nodes are running, or configure a QDevice"
return 0
}
# ------------------------------------------------------------------------------
# preflight_lxc_stack()
#
# - Validates pve-container and lxc-pve packages are installed
# - Checks for available updates (informational only)
# ------------------------------------------------------------------------------
preflight_lxc_stack() {
local pve_container_ver lxc_pve_ver
pve_container_ver=$(dpkg-query -W -f='${Version}\n' pve-container 2>/dev/null || echo "")
lxc_pve_ver=$(dpkg-query -W -f='${Version}\n' lxc-pve 2>/dev/null || echo "")
if [[ -z "$pve_container_ver" ]]; then
preflight_fail "Package 'pve-container' is not installed" 231
echo -e " ${TAB}${INFO} Run: apt-get install pve-container"
return 0
fi
if [[ -z "$lxc_pve_ver" ]]; then
preflight_fail "Package 'lxc-pve' is not installed" 231
echo -e " ${TAB}${INFO} Run: apt-get install lxc-pve"
return 0
fi
local pve_container_cand lxc_pve_cand
pve_container_cand=$(apt-cache policy pve-container 2>/dev/null | awk '/Candidate:/ {print $2}') || true
lxc_pve_cand=$(apt-cache policy lxc-pve 2>/dev/null | awk '/Candidate:/ {print $2}') || true
local update_available=0
if [[ -n "$pve_container_cand" && "$pve_container_cand" != "none" ]]; then
if dpkg --compare-versions "$pve_container_cand" gt "$pve_container_ver" 2>/dev/null; then
update_available=1
fi
fi
if [[ -n "$lxc_pve_cand" && "$lxc_pve_cand" != "none" ]]; then
if dpkg --compare-versions "$lxc_pve_cand" gt "$lxc_pve_ver" 2>/dev/null; then
update_available=1
fi
fi
if [[ "$update_available" -eq 1 ]]; then
preflight_warn "LXC stack update available (current: pve-container=${pve_container_ver}, lxc-pve=${lxc_pve_ver})"
echo -e " ${TAB}${INFO} An upgrade will be offered during container creation if needed"
else
preflight_pass "LXC stack is up to date (pve-container=${pve_container_ver})"
fi
return 0
}
# ------------------------------------------------------------------------------
# preflight_container_id()
#
# - Verifies that container IDs can be allocated
# - Uses pvesh /cluster/nextid (cluster-aware)
# ------------------------------------------------------------------------------
preflight_container_id() {
local nextid
nextid=$(pvesh get /cluster/nextid 2>/dev/null) || true
if [[ -z "$nextid" || ! "$nextid" =~ ^[0-9]+$ ]]; then
preflight_fail "Cannot allocate container ID (pvesh /cluster/nextid failed)" 109
echo -e " ${TAB}${INFO} Check Proxmox cluster health and datacenter.cfg ID ranges"
return 0
fi
preflight_pass "Container IDs available (next: ${nextid})"
return 0
}
# ------------------------------------------------------------------------------
# preflight_template_connectivity()
#
# - Tests connectivity to the Proxmox template download server
# - Warns but does not fail (local templates may be available)
# ------------------------------------------------------------------------------
preflight_template_connectivity() {
local http_code
http_code=$(curl -sS -o /dev/null -w "%{http_code}" -m 5 "http://download.proxmox.com/images/system/" 2>/dev/null) || http_code="000"
if [[ "$http_code" =~ ^2[0-9]{2}$ || "$http_code" =~ ^3[0-9]{2}$ ]]; then
preflight_pass "Template server reachable (download.proxmox.com)"
return 0
fi
local local_count=0
while read -r storage_name _; do
[[ -z "$storage_name" ]] && continue
local count
count=$(pveam list "$storage_name" 2>/dev/null | awk 'NR>1' | wc -l)
local_count=$((local_count + count))
done < <(pvesm status -content vztmpl 2>/dev/null | awk 'NR>1 {print $1}')
if [[ "$local_count" -gt 0 ]]; then
preflight_warn "Template server unreachable, but ${local_count} local template(s) available"
return 0
fi
preflight_fail "Template server unreachable and no local templates available" 222
echo -e " ${TAB}${INFO} Check internet connectivity or manually upload templates"
return 0
}
# ------------------------------------------------------------------------------
# preflight_template_available()
#
# - Validates that a template exists for the configured var_os/var_version
# - Checks both local templates and the online pveam catalog
# - Fails if no matching template can be found anywhere
# ------------------------------------------------------------------------------
preflight_template_available() {
local os="${var_os:-}"
local version="${var_version:-}"
# Skip if os/version not set (e.g. Alpine scripts set them differently)
if [[ -z "$os" || -z "$version" ]]; then
preflight_pass "Template check skipped (OS/version not configured yet)"
return 0
fi
local search_pattern="${os}-${version}"
# Check local templates first
local local_match=0
while read -r storage_name _; do
[[ -z "$storage_name" ]] && continue
if pveam list "$storage_name" 2>/dev/null | awk '{print $1}' | grep -qE "^${storage_name}:vztmpl/${search_pattern}"; then
local_match=1
break
fi
done < <(pvesm status -content vztmpl 2>/dev/null | awk 'NR>1 {print $1}')
if [[ "$local_match" -eq 1 ]]; then
preflight_pass "Template available locally for ${os} ${version}"
return 0
fi
# Check online catalog
local online_match=0
if pveam available -section system 2>/dev/null | awk '{print $2}' | grep -qE "^${search_pattern}[.-]"; then
online_match=1
fi
if [[ "$online_match" -eq 1 ]]; then
preflight_pass "Template available online for ${os} ${version}"
return 0
fi
# Gather available versions for the hint
local available_versions
available_versions=$(
pveam available -section system 2>/dev/null |
awk '{print $2}' |
grep -oE "^${os}-[0-9]+(\.[0-9]+)?" |
sed "s/^${os}-//" |
sort -uV 2>/dev/null | tr '\n' ', ' | sed 's/,$//' | sed 's/,/, /g'
)
preflight_fail "No template found for ${os} ${version}" 225
if [[ -n "$available_versions" ]]; then
echo -e " ${TAB}${INFO} Available ${os} versions: ${GN}${available_versions}${CL}"
fi
echo -e " ${TAB}${INFO} Check var_version in your CT script or use an available version"
return 0
}
# ------------------------------------------------------------------------------
# preflight_package_repos()
#
# - Tests connectivity to OS package repositories from the host
# - Selects repos based on var_os (debian, ubuntu, alpine)
# - Uses HTTP HEAD requests to verify the repo index is reachable
# - Warning only (user may use local mirrors, apt-cacher-ng, etc.)
# ------------------------------------------------------------------------------
preflight_package_repos() {
local os="${var_os:-debian}"
local version="${var_version:-}"
local -a repo_urls=()
local repo_label=""
case "$os" in
debian)
repo_label="Debian"
repo_urls=(
"http://deb.debian.org/debian/dists/${version:-bookworm}/Release"
"http://security.debian.org/debian-security/dists/${version:-bookworm}-security/Release"
)
;;
ubuntu)
repo_label="Ubuntu"
repo_urls=(
"http://archive.ubuntu.com/ubuntu/dists/${version:-jammy}/Release"
"http://security.ubuntu.com/ubuntu/dists/${version:-jammy}-security/Release"
)
;;
alpine)
repo_label="Alpine"
# Alpine versions use x.y format (e.g. 3.20)
local alpine_branch="v${version:-3.20}"
repo_urls=(
"https://dl-cdn.alpinelinux.org/alpine/${alpine_branch}/main/x86_64/APKINDEX.tar.gz"
)
;;
*)
preflight_pass "Package repo check skipped (OS: ${os})"
return 0
;;
esac
local all_ok=true
local failed_urls=()
for url in "${repo_urls[@]}"; do
local http_code
http_code=$(curl -sS -o /dev/null -w "%{http_code}" -m 8 --head "$url" 2>/dev/null) || http_code="000"
# Accept 200, 3xx redirects, and 405 (HEAD not allowed but server is up)
if [[ ! "$http_code" =~ ^(2[0-9]{2}|3[0-9]{2}|405)$ ]]; then
all_ok=false
# Extract hostname for readable output
local host
host=$(echo "$url" | sed -E 's|https?://([^/]+).*|\1|')
failed_urls+=("${host} (HTTP ${http_code})")
fi
done
if [[ "$all_ok" == true ]]; then
preflight_pass "${repo_label} package repositories reachable"
else
local fail_summary
fail_summary=$(printf '%s, ' "${failed_urls[@]}")
fail_summary="${fail_summary%, }"
preflight_warn "${repo_label} package repository not fully reachable: ${fail_summary}"
echo -e " ${TAB}${INFO} Container may fail during ${GN}apt-get update${CL} / ${GN}apk update${CL}"
echo -e " ${TAB}${INFO} Check firewall rules, proxy settings, or DNS from this host"
fi
return 0
}
# ------------------------------------------------------------------------------
# run_preflight()
#
# - Executes all preflight checks and collects results
# - Displays a summary with pass/fail/warn counts
# - On failure: reports to telemetry with "aborted" status and exits cleanly
# - On success: brief pause (2s) then returns (caller shows next screen)
# - Called from install_script() after header_info()
# ------------------------------------------------------------------------------
run_preflight() {
# Reset counters
PREFLIGHT_PASSED=0
PREFLIGHT_FAILED=0
PREFLIGHT_WARNINGS=0
PREFLIGHT_FAILURES=()
PREFLIGHT_EXIT_CODE=0
echo -e "${INFO}${BOLD}${DGN} Running pre-flight checks...${CL}"
echo ""
# --- Kernel checks ---
preflight_maxkeys
# --- Storage checks ---
preflight_storage_rootdir
preflight_storage_vztmpl
preflight_storage_space
# --- Network checks ---
preflight_network_bridge
preflight_dns_resolution
# --- Repository checks ---
preflight_repo_access
# --- Proxmox/Cluster checks ---
preflight_cluster_quorum
preflight_lxc_stack
preflight_container_id
# --- Template availability ---
preflight_template_connectivity
preflight_template_available
# --- Package repository checks ---
preflight_package_repos
echo ""
# --- Summary ---
if [[ "$PREFLIGHT_FAILED" -gt 0 ]]; then
echo -e "${CROSS}${BOLD}${RD} Pre-flight failed: ${PREFLIGHT_FAILED} error(s), ${PREFLIGHT_WARNINGS} warning(s), ${PREFLIGHT_PASSED} passed${CL}"
echo ""
echo -e "${INFO}${BOLD}${DGN} Failure details:${CL}"
for failure in "${PREFLIGHT_FAILURES[@]}"; do
local code="${failure%%|*}"
local msg="${failure#*|}"
echo -e " ${CROSS} [Exit ${code}] ${msg}"
done
echo ""
echo -e "${INFO} Please resolve the above issues before creating a container."
echo -e "${INFO} Documentation: ${BL}https://community-scripts.github.io/ProxmoxVE/${CL}"
# Report to telemetry (if consent was given)
post_preflight_to_api
exit "$PREFLIGHT_EXIT_CODE"
fi
# Success — brief pause so user can see results, then clear for next screen
if [[ "$PREFLIGHT_WARNINGS" -gt 0 ]]; then
echo -e "${CM}${BOLD}${GN} Pre-flight passed with ${PREFLIGHT_WARNINGS} warning(s) (${PREFLIGHT_PASSED} checks passed)${CL}"
else
echo -e "${CM}${BOLD}${GN} All pre-flight checks passed (${PREFLIGHT_PASSED}/${PREFLIGHT_PASSED})${CL}"
fi
sleep 2
}
# ==============================================================================
@@ -1851,7 +2417,7 @@ advanced_settings() {
# ═══════════════════════════════════════════════════════════════════════════
# STEP 2: Root Password
# ═══════════════════════════════════════════════════════════════════════<EFBFBD><EFBFBD><EFBFBD>══
# ═════════════════════════════<EFBFBD><EFBFBD><EFBFBD>══════════════════════════════════════════<EFBFBD><EFBFBD><EFBFBD>══
2)
if PW1=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \
--title "ROOT PASSWORD" \
@@ -2921,7 +3487,7 @@ echo_default() {
# install_script()
#
# - Main entrypoint for installation mode
# - Runs safety checks (pve_check, root_check, maxkeys_check, diagnostics_check)
# - Runs safety checks (pve_check, root_check, diagnostics_check, run_preflight)
# - Builds interactive menu (Default, Verbose, Advanced, My Defaults, App Defaults, Diagnostics, Storage, Exit)
# - Applies chosen settings and triggers container build
# ------------------------------------------------------------------------------
@@ -2931,7 +3497,6 @@ install_script() {
root_check
arch_check
ssh_check
maxkeys_check
diagnostics_check
if systemctl is-active -q ping-instances.service; then
@@ -2951,8 +3516,9 @@ install_script() {
fi
[[ "${timezone:-}" == Etc/* ]] && timezone="host" # pct doesn't accept Etc/* zones
# Show APP Header
# Show APP Header + run preflight checks
header_info
run_preflight
# --- Support CLI argument as direct preset (default, advanced, …) ---
CHOICE="${mode:-${1:-}}"