diff --git a/README.md b/README.md index fc1aac7..6e7ad8e 100644 --- a/README.md +++ b/README.md @@ -41,8 +41,8 @@ rmm Install the ConnectWise RMM agent omsa Install legacy Dell OMSA on supported PowerEdge x30/x40 hosts glances Install Glances acronis Install the Acronis agent -post-install Run the ProxMenux post-install configuration -proxmenux Install or open ProxMenux +post-install Open the native TAPM host configuration menu +proxmenux Open TAPM host configuration for migration compatibility virtio Download current VirtIO drivers sentinelone Install the SentinelOne agent screenconnect Install the ScreenConnect agent @@ -85,6 +85,21 @@ CPU compatibility detection previews cluster-wide QEMU VM and template changes before applying the ProxCLMC recommendation through the Proxmox CLI. Running VMs are not restarted automatically. +The native TAPM host configuration workflow replaces the former ProxMenux +post-install dependency. It can audit a host, apply the recommended PVE 9 +profile, customize individual items, migrate recognized ProxMenux +configurations, repair ProxMenux's system gzip replacement, and remove +ProxMenux after validation. Every mutating run first creates a timestamped +backup under `/var/backups/ta-proxmenu/post-install`. + +The recommended profile installs only missing diagnostic utilities, preserves +the configured timezone and NTP servers, enables conservative kernel and +network safeguards, retains up to 1 GiB or 30 days of persistent journal +history, verifies logrotate, and uses Proxmox's native pigz support without +replacing `/bin/gzip`. APT remains dual-stack by default; its conditional IPv4 +compatibility check is optional. Global vzdump bandwidth and I/O-priority +changes are available through a separate explicit menu. + VirtIO downloads are managed from a dedicated submenu. The stable release is checked only when that submenu is opened, and curated compatibility ISOs are available for Windows Server 2008, 2008 R2, 2012/R2, and 2016. Downloads are @@ -105,6 +120,7 @@ Run the local validation suite with Bash 4.3 or newer: The suite checks Bash syntax and Git whitespace, runs ShellCheck when it is installed, and exercises Git update states, LXC input/storage selection, -maintenance evacuation routing, HA affinity parsing, and VirtIO filename -validation. Tests use temporary files and mocked Proxmox output; they do not -download installers or change a Proxmox host. +maintenance evacuation routing, HA affinity parsing, host-profile migration +signatures and backups, and VirtIO filename validation. Tests use temporary +files and mocked Proxmox output; they do not download installers or change a +Proxmox host. diff --git a/defaults.inc b/defaults.inc index 066d3e8..5420f39 100755 --- a/defaults.inc +++ b/defaults.inc @@ -3,7 +3,7 @@ action="${1:-}" FOLDER='/opt/idssys/ta-proxmenu' -VERS='2026.7.25-69' +VERS='2026.7.26-1' noupdate=' ' diff --git a/inc/post-install.inc b/inc/post-install.inc new file mode 100644 index 0000000..c2faa10 --- /dev/null +++ b/inc/post-install.inc @@ -0,0 +1,1054 @@ +#!/usr/bin/env bash +# TA-ProxMenu native Proxmox VE 9 host configuration and ProxMenux migration. + +TAPM_POST_BACKUP_BASE='/var/backups/ta-proxmenu/post-install' +TAPM_POST_STATE_DIR='/var/lib/ta-proxmenu/post-install' +TAPM_POST_LAST_BACKUP='' +TAPM_POST_LAST_ERROR='' + +TAPM_POST_PATH() { + printf '%s%s' "${TAPM_HOST_ROOT:-}" "$1" +} + +TAPM_POST_LIVE_ROOT() { + [[ -z "${TAPM_HOST_ROOT:-}" ]] +} + +TAPM_POST_WRITE_FILE() { + local target + local mode="${2:-0644}" + local temporary + + target="$(TAPM_POST_PATH "$1")" + mkdir -p -- "$(dirname "$target")" || return 1 + temporary="$(mktemp "${target}.tapm.XXXXXX")" || return 1 + if ! cat >"$temporary" || ! chmod "$mode" "$temporary" || + ! mv -f -- "$temporary" "$target"; then + rm -f -- "$temporary" + return 1 + fi +} + +TAPM_POST_FILE_NORMALIZED() { + local file="$1" + + [[ -f "$file" ]] || return 1 + tr -d '[:space:]' <"$file" +} + +TAPM_POST_EXACT_APT_LANGUAGES() { + local file + file="$(TAPM_POST_PATH '/etc/apt/apt.conf.d/99-disable-translations')" + [[ "$(TAPM_POST_FILE_NORMALIZED "$file" 2>/dev/null)" == \ + 'Acquire::Languages"none";' ]] +} + +TAPM_POST_EXACT_APT_IPV4() { + local file + file="$(TAPM_POST_PATH '/etc/apt/apt.conf.d/99-force-ipv4')" + [[ "$(TAPM_POST_FILE_NORMALIZED "$file" 2>/dev/null)" == \ + 'Acquire::ForceIPv4"true";' ]] +} + +TAPM_POST_PROXMENUX_JOURNALD() { + local file + file="$(TAPM_POST_PATH '/etc/systemd/journald.conf')" + [[ -f "$file" ]] && + grep -q '^SystemMaxUse=64M$' "$file" && + grep -q '^RuntimeMaxUse=60M$' "$file" && + grep -q '^Seal=no$' "$file" && + grep -q '^MaxLevelStore=info$' "$file" +} + +TAPM_POST_PROXMENUX_LOGROTATE() { + local file + file="$(TAPM_POST_PATH '/etc/logrotate.conf')" + [[ -f "$file" ]] && + grep -q '^# ProxMenux optimized configuration' "$file" && + grep -q '^size 10M$' "$file" && + grep -q '^copytruncate$' "$file" +} + +TAPM_POST_PROXMENUX_GZIP_WRAPPER() { + local file + file="$(TAPM_POST_PATH '/bin/gzip')" + [[ -f "$file" ]] && + grep -q 'GZIP="-1"' "$file" && + grep -q 'exec /usr/bin/pigz' "$file" +} + +TAPM_POST_PROXMENUX_NETWORK() { + local file + file="$(TAPM_POST_PATH '/etc/sysctl.d/99-network.conf')" + [[ -f "$file" ]] && + grep -q '^# ProxMenux - Network tuning' "$file" +} + +TAPM_POST_PROXMENUX_MENU_LAUNCHER() { + local file + local target + + file="$(TAPM_POST_PATH '/usr/local/bin/menu')" + if [[ -L "$file" ]]; then + target="$(readlink "$file" 2>/dev/null)" + [[ "$target" == *proxmenux* ]] + return + fi + [[ -f "$file" ]] && grep -qi 'proxmenux' "$file" +} + +TAPM_POST_PROXMENUX_DETECTED() { + [[ -d "$(TAPM_POST_PATH '/usr/local/share/proxmenux')" || + -e "$(TAPM_POST_PATH '/etc/systemd/system/proxmenux-monitor.service')" || + -e "$(TAPM_POST_PATH '/opt/.PROXMENUX_POST_INSTALL')" ]] || + TAPM_POST_PROXMENUX_MENU_LAUNCHER || + TAPM_POST_PROXMENUX_GZIP_WRAPPER || + TAPM_POST_PROXMENUX_NETWORK || + TAPM_POST_PROXMENUX_JOURNALD || + TAPM_POST_PROXMENUX_LOGROTATE || + TAPM_POST_EXACT_APT_LANGUAGES || + TAPM_POST_EXACT_APT_IPV4 +} + +TAPM_POST_MANAGED_FILES() { + cat <<'EOF' +/etc/sysctl.d/99-ta-proxmenu-kernel-panic.conf +/etc/sysctl.d/99-ta-proxmenu-limits.conf +/etc/systemd/journald.conf.d/99-ta-proxmenu.conf +/etc/sysctl.d/99-ta-proxmenu-memory.conf +/etc/sysctl.d/99-ta-proxmenu-network.conf +/etc/sysctl.d/99-ta-proxmenu-bbr.conf +/etc/modules-load.d/ta-proxmenu-bbr.conf +/etc/apt/apt.conf.d/99-ta-proxmenu-force-ipv4 +EOF +} + +TAPM_POST_MIGRATION_FILES() { + cat <<'EOF' +/etc/systemd/journald.conf +/etc/systemd/journald.conf.bak +/etc/logrotate.conf +/etc/logrotate.conf.bak +/etc/vzdump.conf +/etc/apt/sources.list +/etc/sysctl.d/99-kernelpanic.conf +/etc/sysctl.d/99-maxwatches.conf +/etc/sysctl.d/99-maxkeys.conf +/etc/sysctl.d/99-fs.conf +/etc/sysctl.d/99-swap.conf +/etc/sysctl.d/99-memory.conf +/etc/sysctl.d/99-network.conf +/etc/sysctl.d/99-kernel-bbr.conf +/etc/sysctl.d/99-tcp-fastopen.conf +/etc/security/limits.d/99-limits.conf +/etc/systemd/system.conf +/etc/systemd/user.conf +/etc/pam.d/common-session +/etc/pam.d/runuser-l +/etc/network/interfaces +/root/.profile +/root/.bashrc +/root/.bashrc.bak +/etc/motd +/etc/motd.bak +/etc/apt/apt.conf.d/99-disable-translations +/etc/apt/apt.conf.d/99-force-ipv4 +/usr/local/sbin/proxmenux-fwbr-tune +/etc/systemd/system/proxmenux-fwbr-tune.service +/etc/udev/rules.d/99-proxmenux-fwbr-tune.rules +/etc/systemd/system/proxmenux-monitor.service +/usr/local/bin/menu +/usr/local/share/proxmenux +/root/.config/proxmenux-monitor +/opt/googletrans-env +/var/lib/proxmenux +/opt/.PROXMENUX_POST_INSTALL +/bin/gzip +/bin/gzip.original +/bin/pigzwrapper +EOF +} + +TAPM_POST_ALL_BACKUP_PATHS() { + { + TAPM_POST_MANAGED_FILES + TAPM_POST_MIGRATION_FILES + } | awk '!seen[$0]++' +} + +TAPM_POST_BACKUP() { + local backup_root + local source + local relative_path + local manifest + local checksum + local timestamp + local service + local enabled + local active + + timestamp="$(date +%Y%m%d-%H%M%S)" + backup_root="$(TAPM_POST_PATH "$TAPM_POST_BACKUP_BASE")" + mkdir -p -m 0700 -- "$backup_root" || return 1 + TAPM_POST_LAST_BACKUP="$(mktemp -d "${backup_root}/${timestamp}.XXXXXX")" || + return 1 + chmod 0700 "$TAPM_POST_LAST_BACKUP" || return 1 + manifest="${TAPM_POST_LAST_BACKUP}/manifest.tsv" + printf 'TA-ProxMenu host configuration backup\nCreated: %s\nHost: %s\n' \ + "$(date --iso-8601=seconds 2>/dev/null || date)" \ + "$(hostname 2>/dev/null || printf unknown)" \ + >"${TAPM_POST_LAST_BACKUP}/README.txt" + + while IFS= read -r relative_path; do + [[ -n "$relative_path" ]] || continue + source="$(TAPM_POST_PATH "$relative_path")" + if [[ -e "$source" || -L "$source" ]]; then + mkdir -p -- "${TAPM_POST_LAST_BACKUP}/files$(dirname "$relative_path")" || + return 1 + cp -a -- "$source" \ + "${TAPM_POST_LAST_BACKUP}/files${relative_path}" || return 1 + if [[ -f "$source" && ! -L "$source" ]]; then + checksum="$(sha256sum "$source" | awk '{print $1}')" + else + checksum='-' + fi + printf 'PRESENT\t%s\t%s\n' "$relative_path" "$checksum" >>"$manifest" + else + printf 'ABSENT\t%s\t-\n' "$relative_path" >>"$manifest" + fi + done < <(TAPM_POST_ALL_BACKUP_PATHS) + + if TAPM_POST_LIVE_ROOT; then + for service in \ + proxmenux-monitor.service proxmenux-fwbr-tune.service \ + chrony.service systemd-timesyncd.service logrotate.timer; do + systemctl is-enabled --quiet "$service" 2>/dev/null && + enabled=enabled || enabled=disabled + systemctl is-active --quiet "$service" 2>/dev/null && + active=active || active=inactive + printf '%s\t%s\t%s\n' "$service" "$enabled" "$active" \ + >>"${TAPM_POST_LAST_BACKUP}/services.tsv" + done + fi + + printf '%s\n' "$TAPM_POST_LAST_BACKUP" +} + +TAPM_POST_RESTORE_BACKUP() { + local backup_dir="$1" + local manifest="${backup_dir}/manifest.tsv" + local status + local relative_path + local checksum + local target + local stored + + [[ -d "$backup_dir" && -f "$manifest" ]] || return 1 + while IFS=$'\t' read -r status relative_path checksum; do + [[ "$relative_path" == /* && "$relative_path" != *'..'* ]] || return 1 + target="$(TAPM_POST_PATH "$relative_path")" + stored="${backup_dir}/files${relative_path}" + case "$status" in + PRESENT) + [[ -e "$stored" || -L "$stored" ]] || return 1 + if [[ "$checksum" != '-' ]]; then + [[ -f "$stored" && ! -L "$stored" ]] || return 1 + [[ "$(sha256sum "$stored" | awk '{print $1}')" == "$checksum" ]] || + return 1 + fi + mkdir -p -- "$(dirname "$target")" || return 1 + rm -rf -- "$target" || return 1 + cp -a -- "$stored" "$target" || return 1 + ;; + ABSENT) + rm -rf -- "$target" || return 1 + ;; + *) + return 1 + ;; + esac + done <"$manifest" + + if TAPM_POST_LIVE_ROOT; then + systemctl daemon-reload >/dev/null 2>&1 || true + if [[ -f "${backup_dir}/services.tsv" ]]; then + while IFS=$'\t' read -r relative_path status checksum; do + if [[ "$status" == enabled ]]; then + systemctl enable "$relative_path" >/dev/null 2>&1 || true + else + systemctl disable "$relative_path" >/dev/null 2>&1 || true + fi + if [[ "$checksum" == active ]]; then + systemctl start "$relative_path" >/dev/null 2>&1 || true + else + systemctl stop "$relative_path" >/dev/null 2>&1 || true + fi + done <"${backup_dir}/services.tsv" + fi + sysctl --system >/dev/null 2>&1 || true + systemctl restart systemd-journald.service >/dev/null 2>&1 || true + fi +} + +TAPM_POST_PRECHECK() { + local pve_major + + if TAPM_POST_LIVE_ROOT; then + if (( EUID != 0 )); then + TAPM_POST_LAST_ERROR='This workflow must be run as root.' + return 1 + fi + pve_major="$( + pveversion 2>/dev/null | + sed -n 's/.*pve-manager\\/\\([0-9][0-9]*\\).*/\\1/p' | + head -1 + )" + if [[ "$pve_major" != '9' ]]; then + TAPM_POST_LAST_ERROR='The native TAPM profile currently supports Proxmox VE 9 only.' + return 1 + fi + if ps -eo comm= 2>/dev/null | + grep -Eq '^(vzdump|pigz|gzip|zstd)$'; then + TAPM_POST_LAST_ERROR='A backup or compression process is active; retry after it finishes.' + return 1 + fi + if command -v flock >/dev/null 2>&1 && + ! flock -n /var/lib/dpkg/lock-frontend true 2>/dev/null; then + TAPM_POST_LAST_ERROR='The package manager is currently busy.' + return 1 + fi + fi +} + +TAPM_POST_DEFAULT_SELECTIONS() { + TAPM_POST_DO_UTILITIES=1 + TAPM_POST_DO_TIME=1 + TAPM_POST_DO_PANIC=1 + TAPM_POST_DO_LIMITS=1 + TAPM_POST_DO_JOURNALD=1 + TAPM_POST_DO_LOGROTATE=1 + TAPM_POST_DO_MEMORY=1 + TAPM_POST_DO_NETWORK=1 + TAPM_POST_DO_BBR=1 + TAPM_POST_DO_PIGZ=1 + TAPM_POST_DO_APT_NETWORK=0 +} + +TAPM_POST_PACKAGE_INSTALLED() { + dpkg-query -W -f='${Status}' "$1" 2>/dev/null | + grep -q '^install ok installed$' +} + +TAPM_POST_INSTALL_PACKAGES() { + local -a packages=( + htop btop iftop iotop iperf3 net-tools unzip zip + tmux mtr-tiny dnsutils lsof jq rsync sysstat ethtool + smartmontools nvme-cli + ) + local -a missing=() + local package + local microcode='' + local vendor + + TAPM_POST_LIVE_ROOT || return 0 + for package in "${packages[@]}"; do + TAPM_POST_PACKAGE_INSTALLED "$package" || missing+=("$package") + done + if (( ${#missing[@]} > 0 )); then + if ! DEBIAN_FRONTEND=noninteractive apt-get install -y \ + --no-install-recommends "${missing[@]}"; then + apt-get update || return 1 + DEBIAN_FRONTEND=noninteractive apt-get install -y \ + --no-install-recommends "${missing[@]}" || return 1 + fi + fi + + vendor="$(awk -F: '/vendor_id/ {gsub(/[[:space:]]/, "", $2); print $2; exit}' \ + /proc/cpuinfo 2>/dev/null)" + case "$vendor" in + GenuineIntel) microcode='intel-microcode' ;; + AuthenticAMD) microcode='amd64-microcode' ;; + esac + if [[ -n "$microcode" ]] && ! TAPM_POST_PACKAGE_INSTALLED "$microcode"; then + DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ + "$microcode" || + printf 'Warning: %s was unavailable; continuing.\n' "$microcode" >&2 + fi +} + +TAPM_POST_ENSURE_APT_LAYOUT() { + local sources_list + + sources_list="$(TAPM_POST_PATH '/etc/apt/sources.list')" + if [[ -e "$sources_list" && ! -f "$sources_list" ]]; then + return 1 + fi + mkdir -p -- "$(dirname "$sources_list")" || return 1 + : >"$sources_list" || return 1 + chmod 0644 "$sources_list" || return 1 + if TAPM_POST_LIVE_ROOT; then + apt-get update + fi +} + +TAPM_POST_CONFIGURE_TIME() { + TAPM_POST_LIVE_ROOT || return 0 + + if ! TAPM_POST_PACKAGE_INSTALLED chrony; then + DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ + chrony || return 1 + fi + systemctl enable --now chrony.service || return 1 + if systemctl is-active --quiet systemd-timesyncd.service; then + systemctl disable --now systemd-timesyncd.service >/dev/null 2>&1 || true + fi + chronyc tracking >/dev/null 2>&1 || { + printf 'Warning: chrony is active but has not synchronized yet.\n' >&2 + return 0 + } +} + +TAPM_POST_CONFIGURE_PANIC() { + TAPM_POST_WRITE_FILE '/etc/sysctl.d/99-ta-proxmenu-kernel-panic.conf' <<'EOF' +# Managed by TA-ProxMenu +kernel.panic = 30 +kernel.panic_on_oops = 1 +kernel.hardlockup_panic = 1 +kernel.softlockup_panic = 0 +EOF +} + +TAPM_POST_CONFIGURE_LIMITS() { + TAPM_POST_WRITE_FILE '/etc/sysctl.d/99-ta-proxmenu-limits.conf' <<'EOF' +# Managed by TA-ProxMenu +fs.inotify.max_user_watches = 524288 +fs.inotify.max_user_instances = 1024 +fs.inotify.max_queued_events = 32768 +EOF +} + +TAPM_POST_CONFIGURE_JOURNALD() { + if TAPM_POST_PROXMENUX_JOURNALD; then + printf '[Journal]\n' | + TAPM_POST_WRITE_FILE '/etc/systemd/journald.conf' + fi + TAPM_POST_WRITE_FILE '/etc/systemd/journald.conf.d/99-ta-proxmenu.conf' <<'EOF' +# Managed by TA-ProxMenu +[Journal] +Storage=persistent +Compress=yes +SystemMaxUse=1G +SystemKeepFree=1G +SystemMaxFileSize=64M +MaxRetentionSec=30day +EOF + if TAPM_POST_LIVE_ROOT; then + systemctl restart systemd-journald.service || return 1 + systemd-analyze cat-config systemd/journald.conf 2>/dev/null | + grep -q '^MaxRetentionSec=30day$' || return 1 + fi +} + +TAPM_POST_LOGROTATE_BACKUP_VALID() { + local file="$1" + + [[ -f "$file" ]] || return 1 + ! grep -q '^# ProxMenux optimized configuration' "$file" && + grep -Eq '^[[:space:]]*include[[:space:]]+/etc/logrotate.d' "$file" +} + +TAPM_POST_CONFIGURE_LOGROTATE() { + local backup + + if TAPM_POST_PROXMENUX_LOGROTATE; then + backup="$(TAPM_POST_PATH '/etc/logrotate.conf.bak')" + if TAPM_POST_LOGROTATE_BACKUP_VALID "$backup"; then + cp -a -- "$backup" "$(TAPM_POST_PATH '/etc/logrotate.conf')" || + return 1 + else + TAPM_POST_WRITE_FILE '/etc/logrotate.conf' <<'EOF' +# Debian-compatible baseline restored by TA-ProxMenu +weekly +rotate 4 +create +include /etc/logrotate.d +EOF + fi + fi + if TAPM_POST_LIVE_ROOT; then + systemctl enable --now logrotate.timer >/dev/null 2>&1 || return 1 + logrotate --debug /etc/logrotate.conf >/dev/null 2>&1 || return 1 + fi +} + +TAPM_POST_CONFIGURE_MEMORY() { + TAPM_POST_WRITE_FILE '/etc/sysctl.d/99-ta-proxmenu-memory.conf' <<'EOF' +# Managed by TA-ProxMenu +vm.swappiness = 10 +EOF +} + +TAPM_POST_CONFIGURE_NETWORK() { + TAPM_POST_WRITE_FILE '/etc/sysctl.d/99-ta-proxmenu-network.conf' <<'EOF' +# Managed by TA-ProxMenu +net.ipv4.conf.all.accept_redirects = 0 +net.ipv4.conf.default.accept_redirects = 0 +net.ipv4.conf.all.secure_redirects = 0 +net.ipv4.conf.default.secure_redirects = 0 +net.ipv4.conf.all.accept_source_route = 0 +net.ipv4.conf.default.accept_source_route = 0 +net.ipv4.conf.all.send_redirects = 0 +net.ipv4.conf.default.send_redirects = 0 +net.ipv4.icmp_echo_ignore_broadcasts = 1 +net.ipv4.icmp_ignore_bogus_error_responses = 1 +net.ipv4.tcp_rfc1337 = 1 +net.ipv4.tcp_mtu_probing = 1 +EOF +} + +TAPM_POST_CONFIGURE_BBR() { + TAPM_POST_WRITE_FILE '/etc/sysctl.d/99-ta-proxmenu-bbr.conf' <<'EOF' +# Managed by TA-ProxMenu +net.core.default_qdisc = fq +net.ipv4.tcp_congestion_control = bbr +net.ipv4.tcp_fastopen = 3 +EOF + if TAPM_POST_LIVE_ROOT; then + modprobe tcp_bbr >/dev/null 2>&1 || true + if ! sysctl -n net.ipv4.tcp_available_congestion_control 2>/dev/null | + grep -qw bbr; then + rm -f -- \ + "$(TAPM_POST_PATH '/etc/sysctl.d/99-ta-proxmenu-bbr.conf')" \ + "$(TAPM_POST_PATH '/etc/modules-load.d/ta-proxmenu-bbr.conf')" + printf 'Warning: BBR is unavailable in the running kernel; continuing.\n' >&2 + return 0 + fi + if modinfo tcp_bbr >/dev/null 2>&1; then + printf 'tcp_bbr\n' | + TAPM_POST_WRITE_FILE '/etc/modules-load.d/ta-proxmenu-bbr.conf' + fi + fi +} + +TAPM_POST_REMOVE_COLON_KEY() { + local relative_path="$1" + local key="$2" + local file + local temporary + + file="$(TAPM_POST_PATH "$relative_path")" + [[ -f "$file" ]] || return 0 + temporary="$(mktemp "${file}.tapm.XXXXXX")" || return 1 + if ! awk -v key="$key" ' + $0 ~ "^[[:space:]]*" key "[[:space:]]*:" { next } + { print } + ' "$file" >"$temporary" || + ! { chmod --reference="$file" "$temporary" 2>/dev/null || + chmod 0644 "$temporary"; } || + ! mv -f -- "$temporary" "$file"; then + rm -f -- "$temporary" + return 1 + fi +} + +TAPM_POST_SET_COLON_KEY() { + local relative_path="$1" + local key="$2" + local value="$3" + local file + local temporary + + file="$(TAPM_POST_PATH "$relative_path")" + mkdir -p -- "$(dirname "$file")" || return 1 + touch "$file" || return 1 + temporary="$(mktemp "${file}.tapm.XXXXXX")" || return 1 + if ! awk -v key="$key" -v value="$value" ' + BEGIN { written=0 } + $0 ~ "^[[:space:]]*#?[[:space:]]*" key "[[:space:]]*:" { + if (!written) { + print key ": " value + written=1 + } + next + } + { print } + END { + if (!written) print key ": " value + } + ' "$file" >"$temporary" || + ! chmod 0644 "$temporary" || + ! mv -f -- "$temporary" "$file"; then + rm -f -- "$temporary" + return 1 + fi +} + +TAPM_POST_REPAIR_GZIP() { + local gzip_path + local original + local wrapper + + TAPM_POST_PROXMENUX_GZIP_WRAPPER || return 0 + gzip_path="$(TAPM_POST_PATH '/bin/gzip')" + original="$(TAPM_POST_PATH '/bin/gzip.original')" + wrapper="$(TAPM_POST_PATH '/bin/pigzwrapper')" + + if [[ -x "$original" ]] && + "$original" --version 2>/dev/null | head -1 | grep -qi 'gzip'; then + cp -a -- "$original" "$gzip_path" || return 1 + elif TAPM_POST_LIVE_ROOT; then + DEBIAN_FRONTEND=noninteractive apt-get install --reinstall -y gzip || + return 1 + else + return 1 + fi + + "$gzip_path" --version 2>/dev/null | head -1 | grep -qi 'gzip' || return 1 + if ! TAPM_POST_LIVE_ROOT || + printf 'TA-ProxMenu gzip verification\n' | + "$gzip_path" -c | + "$gzip_path" -dc | + grep -q '^TA-ProxMenu gzip verification$'; then + rm -f -- "$wrapper" "$original" + else + return 1 + fi +} + +TAPM_POST_CONFIGURE_PIGZ() { + if TAPM_POST_LIVE_ROOT && ! TAPM_POST_PACKAGE_INSTALLED pigz; then + DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ + pigz || return 1 + fi + TAPM_POST_SET_COLON_KEY '/etc/vzdump.conf' pigz 1 +} + +TAPM_POST_APT_NETWORK_CHECK() { + local tapm_ipv4_file='/etc/apt/apt.conf.d/99-ta-proxmenu-force-ipv4' + + TAPM_POST_LIVE_ROOT || return 0 + rm -f -- "$(TAPM_POST_PATH "$tapm_ipv4_file")" + if apt-get update; then + return 0 + fi + if apt-get -o Acquire::ForceIPv4=true update; then + printf 'Acquire::ForceIPv4 "true";\n' | + TAPM_POST_WRITE_FILE "$tapm_ipv4_file" + return + fi + return 1 +} + +TAPM_POST_REMOVE_FILE_IF_MATCHES() { + local relative_path="$1" + local pattern="$2" + local file + + file="$(TAPM_POST_PATH "$relative_path")" + [[ -f "$file" ]] || return 0 + grep -q -- "$pattern" "$file" || return 0 + rm -f -- "$file" +} + +TAPM_POST_PROXMENUX_TOOL_REGISTERED() { + local tool="$1" + local registry + + registry="$(TAPM_POST_PATH '/usr/local/share/proxmenux/installed_tools.json')" + [[ -f "$registry" ]] && grep -q "\"${tool}\"" "$registry" +} + +TAPM_POST_COLON_VALUE_IS() { + local relative_path="$1" + local key="$2" + local expected="$3" + local file + + file="$(TAPM_POST_PATH "$relative_path")" + [[ -f "$file" ]] && + awk -F: -v key="$key" -v expected="$expected" ' + $1 ~ "^[[:space:]]*" key "[[:space:]]*$" { + gsub(/[[:space:]]/, "", $2) + found=($2 == expected) + } + END { exit !found } + ' "$file" +} + +TAPM_POST_REMOVE_EXACT_LINE() { + local relative_path="$1" + local line="$2" + local file + local temporary + + file="$(TAPM_POST_PATH "$relative_path")" + [[ -f "$file" ]] || return 0 + temporary="$(mktemp "${file}.tapm.XXXXXX")" || return 1 + if ! awk -v line="$line" '$0 != line { print }' "$file" >"$temporary" || + ! { chmod --reference="$file" "$temporary" 2>/dev/null || + chmod 0644 "$temporary"; } || + ! mv -f -- "$temporary" "$file"; then + rm -f -- "$temporary" + return 1 + fi +} + +TAPM_POST_RESTORE_FWBR_RUNTIME_DEFAULTS() { + local interface_path + local default_value + local interface_name + + TAPM_POST_LIVE_ROOT || return 0 + default_value="$(cat /proc/sys/net/ipv4/conf/default/rp_filter 2>/dev/null)" || + return 0 + for interface_path in /proc/sys/net/ipv4/conf/*; do + [[ -d "$interface_path" ]] || continue + interface_name="${interface_path##*/}" + case "$interface_name" in + fwbr*|fwln*|fwpr*|tap*) + [[ -w "${interface_path}/rp_filter" ]] && + printf '%s\n' "$default_value" >"${interface_path}/rp_filter" + ;; + esac + done +} + +TAPM_POST_CLEAN_PROXMENUX_SETTINGS() { + local cleanup_status=0 + local file + local limits_were_proxmenux=0 + local vzdump_was_proxmenux=0 + + file="$(TAPM_POST_PATH '/etc/security/limits.d/99-limits.conf')" + [[ -f "$file" ]] && grep -q '# ProxMenux configuration' "$file" && + limits_were_proxmenux=1 + if TAPM_POST_PROXMENUX_TOOL_REGISTERED vzdump_speed || + { [[ -e "$(TAPM_POST_PATH '/opt/.PROXMENUX_POST_INSTALL')" ]] && + TAPM_POST_COLON_VALUE_IS /etc/vzdump.conf bwlimit 0 && + TAPM_POST_COLON_VALUE_IS /etc/vzdump.conf ionice 5; }; then + vzdump_was_proxmenux=1 + fi + + if TAPM_POST_EXACT_APT_LANGUAGES; then + rm -f -- "$(TAPM_POST_PATH '/etc/apt/apt.conf.d/99-disable-translations')" || + cleanup_status=1 + fi + if TAPM_POST_EXACT_APT_IPV4; then + rm -f -- "$(TAPM_POST_PATH '/etc/apt/apt.conf.d/99-force-ipv4')" || + cleanup_status=1 + fi + + TAPM_POST_REMOVE_FILE_IF_MATCHES '/etc/sysctl.d/99-kernelpanic.conf' \ + '^kernel.core_pattern = /var/crash/' || cleanup_status=1 + TAPM_POST_REMOVE_FILE_IF_MATCHES '/etc/sysctl.d/99-maxwatches.conf' \ + 'fs.inotify.max_user_instances = 1048576' || cleanup_status=1 + TAPM_POST_REMOVE_FILE_IF_MATCHES '/etc/sysctl.d/99-maxkeys.conf' \ + 'kernel.keys.maxkeys=1000000' || cleanup_status=1 + TAPM_POST_REMOVE_FILE_IF_MATCHES '/etc/sysctl.d/99-fs.conf' \ + 'fs.aio-max-nr = 1048576' || cleanup_status=1 + TAPM_POST_REMOVE_FILE_IF_MATCHES '/etc/sysctl.d/99-swap.conf' \ + '# ProxMenux configuration' || cleanup_status=1 + TAPM_POST_REMOVE_FILE_IF_MATCHES '/etc/sysctl.d/99-memory.conf' \ + '^# Balanced Memory Optimization' || cleanup_status=1 + TAPM_POST_REMOVE_FILE_IF_MATCHES '/etc/sysctl.d/99-network.conf' \ + '^# ProxMenux - Network tuning' || cleanup_status=1 + TAPM_POST_REMOVE_FILE_IF_MATCHES '/etc/sysctl.d/99-kernel-bbr.conf' \ + 'net.ipv4.tcp_congestion_control = bbr' || cleanup_status=1 + TAPM_POST_REMOVE_FILE_IF_MATCHES '/etc/sysctl.d/99-tcp-fastopen.conf' \ + 'net.ipv4.tcp_fastopen = 3' || cleanup_status=1 + TAPM_POST_REMOVE_FILE_IF_MATCHES '/etc/security/limits.d/99-limits.conf' \ + '# ProxMenux configuration' || cleanup_status=1 + + if (( limits_were_proxmenux == 1 )); then + TAPM_POST_REMOVE_EXACT_LINE '/etc/systemd/system.conf' \ + 'DefaultLimitNOFILE=1048576' || cleanup_status=1 + TAPM_POST_REMOVE_EXACT_LINE '/etc/systemd/user.conf' \ + 'DefaultLimitNOFILE=1048576' || cleanup_status=1 + TAPM_POST_REMOVE_EXACT_LINE '/etc/pam.d/common-session' \ + 'session required pam_limits.so' || cleanup_status=1 + TAPM_POST_REMOVE_EXACT_LINE '/etc/pam.d/runuser-l' \ + 'session required pam_limits.so' || cleanup_status=1 + TAPM_POST_REMOVE_EXACT_LINE '/root/.profile' 'ulimit -n 1048576' || + cleanup_status=1 + fi + if (( vzdump_was_proxmenux == 1 )); then + TAPM_POST_REMOVE_COLON_KEY '/etc/vzdump.conf' bwlimit || cleanup_status=1 + TAPM_POST_REMOVE_COLON_KEY '/etc/vzdump.conf' ionice || cleanup_status=1 + fi + + if TAPM_POST_LIVE_ROOT; then + systemctl disable --now proxmenux-fwbr-tune.service >/dev/null 2>&1 || true + fi + rm -f -- \ + "$(TAPM_POST_PATH '/usr/local/sbin/proxmenux-fwbr-tune')" \ + "$(TAPM_POST_PATH '/etc/systemd/system/proxmenux-fwbr-tune.service')" \ + "$(TAPM_POST_PATH '/etc/udev/rules.d/99-proxmenux-fwbr-tune.rules')" || + cleanup_status=1 + TAPM_POST_RESTORE_FWBR_RUNTIME_DEFAULTS || cleanup_status=1 + + file="$(TAPM_POST_PATH '/etc/network/interfaces')" + if [[ -f "$file" ]]; then + awk ' + $0 == "source /etc/network/interfaces.d/*" { + if (seen++) next + } + { print } + ' "$file" >"${file}.tapm" && + { chmod --reference="$file" "${file}.tapm" 2>/dev/null || + chmod 0644 "${file}.tapm"; } && + mv -f -- "${file}.tapm" "$file" || cleanup_status=1 + fi + return "$cleanup_status" +} + +TAPM_POST_REMOVE_PROXMENUX_APP() { + local bashrc_backup + local cleanup_status=0 + local had_app=0 + local motd_backup + local menu_launcher + + [[ -d "$(TAPM_POST_PATH '/usr/local/share/proxmenux')" ]] && had_app=1 + + if TAPM_POST_LIVE_ROOT; then + systemctl disable --now proxmenux-monitor.service >/dev/null 2>&1 || true + fi + rm -f -- \ + "$(TAPM_POST_PATH '/etc/systemd/system/proxmenux-monitor.service')" \ + "$(TAPM_POST_PATH '/opt/.PROXMENUX_POST_INSTALL')" || cleanup_status=1 + menu_launcher="$(TAPM_POST_PATH '/usr/local/bin/menu')" + if TAPM_POST_PROXMENUX_MENU_LAUNCHER; then + rm -f -- "$menu_launcher" || cleanup_status=1 + fi + rm -rf -- \ + "$(TAPM_POST_PATH '/usr/local/share/proxmenux')" \ + "$(TAPM_POST_PATH '/root/.config/proxmenux-monitor')" \ + "$(TAPM_POST_PATH '/var/lib/proxmenux')" || cleanup_status=1 + if (( had_app == 1 )); then + rm -rf -- "$(TAPM_POST_PATH '/opt/googletrans-env')" || cleanup_status=1 + fi + + bashrc_backup="$(TAPM_POST_PATH '/root/.bashrc.bak')" + if [[ -f "$bashrc_backup" ]] && + grep -qi 'proxmenux' "$(TAPM_POST_PATH '/root/.bashrc')" 2>/dev/null; then + mv -f -- "$bashrc_backup" "$(TAPM_POST_PATH '/root/.bashrc')" || + cleanup_status=1 + fi + motd_backup="$(TAPM_POST_PATH '/etc/motd.bak')" + if [[ -f "$motd_backup" ]] && + grep -qi 'proxmenux' "$(TAPM_POST_PATH '/etc/motd')" 2>/dev/null; then + mv -f -- "$motd_backup" "$(TAPM_POST_PATH '/etc/motd')" || + cleanup_status=1 + else + TAPM_POST_REMOVE_EXACT_LINE '/etc/motd' \ + 'This system is optimised by: ProxMenux' || cleanup_status=1 + fi + if TAPM_POST_LIVE_ROOT; then + systemctl daemon-reload >/dev/null 2>&1 || true + systemctl reset-failed >/dev/null 2>&1 || true + fi + return "$cleanup_status" +} + +TAPM_POST_APPLY_SYSCTL() { + TAPM_POST_LIVE_ROOT || return 0 + sysctl --system >/dev/null +} + +TAPM_POST_STATE() { + local status="$1" + local step="$2" + local state_dir + + state_dir="$(TAPM_POST_PATH "$TAPM_POST_STATE_DIR")" + mkdir -p -m 0750 -- "$state_dir" || return 1 + { + printf 'status=%s\n' "$status" + printf 'step=%s\n' "$step" + printf 'updated=%s\n' "$(date --iso-8601=seconds 2>/dev/null || date)" + printf 'backup=%s\n' "$TAPM_POST_LAST_BACKUP" + } >"${state_dir}/state" +} + +TAPM_POST_RUN_STEP() { + local label="$1" + shift + + printf ' - %s...\n' "$label" + TAPM_POST_STATE in_progress "$label" || return 1 + if "$@"; then + printf ' OK\n' + return 0 + fi + TAPM_POST_LAST_ERROR="$label failed." + TAPM_POST_STATE failed "$label" || true + return 1 +} + +TAPM_POST_SAVE_REPORT() { + local mode="$1" + local state_dir + + state_dir="$(TAPM_POST_PATH "$TAPM_POST_STATE_DIR")" + mkdir -p -m 0750 -- "$state_dir" || return 1 + { + printf 'TA-ProxMenu host configuration report\n' + printf 'Completed: %s\n' "$(date --iso-8601=seconds 2>/dev/null || date)" + printf 'Mode: %s\n' "$mode" + printf 'Host: %s\n' "$(hostname 2>/dev/null || printf unknown)" + printf 'Backup: %s\n' "$TAPM_POST_LAST_BACKUP" + printf 'ProxMenux detected after completion: ' + TAPM_POST_PROXMENUX_DETECTED && printf 'yes\n' || printf 'no\n' + printf '\nSelected profile:\n' + printf ' Utilities: %s\n' "$TAPM_POST_DO_UTILITIES" + printf ' Time synchronization: %s\n' "$TAPM_POST_DO_TIME" + printf ' Kernel panic recovery: %s\n' "$TAPM_POST_DO_PANIC" + printf ' Inotify limits: %s\n' "$TAPM_POST_DO_LIMITS" + printf ' Journald: %s\n' "$TAPM_POST_DO_JOURNALD" + printf ' Log rotation: %s\n' "$TAPM_POST_DO_LOGROTATE" + printf ' Memory behavior: %s\n' "$TAPM_POST_DO_MEMORY" + printf ' Network safeguards: %s\n' "$TAPM_POST_DO_NETWORK" + printf ' BBR/TCP Fast Open: %s\n' "$TAPM_POST_DO_BBR" + printf ' Native pigz: %s\n' "$TAPM_POST_DO_PIGZ" + printf ' APT network check: %s\n' "$TAPM_POST_DO_APT_NETWORK" + } >"${state_dir}/last-report.txt" + TAPM_POST_STATE complete complete +} + +TAPM_POST_APPLY_PROFILE() { + local mode="${1:-apply}" + local migrate=0 + + [[ "$mode" == migrate ]] && migrate=1 + TAPM_POST_LAST_ERROR='' + TAPM_POST_PRECHECK || return 1 + if (( migrate == 1 )) && ! TAPM_POST_PROXMENUX_DETECTED; then + TAPM_POST_LAST_ERROR='No ProxMenux installation or recognized artifacts were detected.' + return 1 + fi + TAPM_POST_BACKUP >/dev/null || { + TAPM_POST_LAST_ERROR='The configuration backup could not be created.' + return 1 + } + + TAPM_POST_RUN_STEP 'Validating the TAPM-managed APT source layout' \ + TAPM_POST_ENSURE_APT_LAYOUT || return 1 + if (( migrate == 1 )); then + TAPM_POST_RUN_STEP 'Repairing the ProxMenux gzip replacement' \ + TAPM_POST_REPAIR_GZIP || return 1 + fi + (( TAPM_POST_DO_UTILITIES == 0 )) || + TAPM_POST_RUN_STEP 'Installing missing system utilities' \ + TAPM_POST_INSTALL_PACKAGES || return 1 + (( TAPM_POST_DO_TIME == 0 )) || + TAPM_POST_RUN_STEP 'Configuring time synchronization' \ + TAPM_POST_CONFIGURE_TIME || return 1 + (( TAPM_POST_DO_PANIC == 0 )) || + TAPM_POST_RUN_STEP 'Configuring kernel panic recovery' \ + TAPM_POST_CONFIGURE_PANIC || return 1 + (( TAPM_POST_DO_LIMITS == 0 )) || + TAPM_POST_RUN_STEP 'Configuring conservative inotify limits' \ + TAPM_POST_CONFIGURE_LIMITS || return 1 + (( TAPM_POST_DO_JOURNALD == 0 )) || + TAPM_POST_RUN_STEP 'Configuring journald retention' \ + TAPM_POST_CONFIGURE_JOURNALD || return 1 + (( TAPM_POST_DO_LOGROTATE == 0 )) || + TAPM_POST_RUN_STEP 'Verifying and repairing log rotation' \ + TAPM_POST_CONFIGURE_LOGROTATE || return 1 + (( TAPM_POST_DO_MEMORY == 0 )) || + TAPM_POST_RUN_STEP 'Configuring host memory behavior' \ + TAPM_POST_CONFIGURE_MEMORY || return 1 + (( TAPM_POST_DO_NETWORK == 0 )) || + TAPM_POST_RUN_STEP 'Applying network safeguards' \ + TAPM_POST_CONFIGURE_NETWORK || return 1 + (( TAPM_POST_DO_BBR == 0 )) || + TAPM_POST_RUN_STEP 'Enabling BBR and TCP Fast Open' \ + TAPM_POST_CONFIGURE_BBR || return 1 + (( TAPM_POST_DO_PIGZ == 0 )) || + TAPM_POST_RUN_STEP 'Enabling native parallel gzip for vzdump' \ + TAPM_POST_CONFIGURE_PIGZ || return 1 + (( TAPM_POST_DO_APT_NETWORK == 0 )) || + TAPM_POST_RUN_STEP 'Checking APT network compatibility' \ + TAPM_POST_APT_NETWORK_CHECK || return 1 + + if (( migrate == 1 )); then + TAPM_POST_RUN_STEP 'Removing recognized ProxMenux settings' \ + TAPM_POST_CLEAN_PROXMENUX_SETTINGS || return 1 + # Reassert TAPM files after removing the older ProxMenux equivalents. + (( TAPM_POST_DO_PANIC == 0 )) || TAPM_POST_CONFIGURE_PANIC || return 1 + (( TAPM_POST_DO_LIMITS == 0 )) || TAPM_POST_CONFIGURE_LIMITS || return 1 + (( TAPM_POST_DO_MEMORY == 0 )) || TAPM_POST_CONFIGURE_MEMORY || return 1 + (( TAPM_POST_DO_NETWORK == 0 )) || TAPM_POST_CONFIGURE_NETWORK || return 1 + (( TAPM_POST_DO_BBR == 0 )) || TAPM_POST_CONFIGURE_BBR || return 1 + fi + + TAPM_POST_RUN_STEP 'Applying and validating kernel settings' \ + TAPM_POST_APPLY_SYSCTL || return 1 + if (( migrate == 1 )); then + TAPM_POST_RUN_STEP 'Removing the ProxMenux application' \ + TAPM_POST_REMOVE_PROXMENUX_APP || return 1 + fi + TAPM_POST_SAVE_REPORT "$mode" || return 1 +} + +TAPM_POST_AUDIT() { + local state_file + local report_file + + printf 'TA-ProxMenu host configuration audit\n\n' + printf 'ProxMenux installation/artifacts: ' + TAPM_POST_PROXMENUX_DETECTED && printf 'detected\n' || printf 'not detected\n' + printf 'ProxMenux gzip replacement: ' + TAPM_POST_PROXMENUX_GZIP_WRAPPER && printf 'detected - migration required\n' || + printf 'not detected\n' + printf 'ProxMenux journald replacement: ' + TAPM_POST_PROXMENUX_JOURNALD && printf 'detected\n' || printf 'not detected\n' + printf 'ProxMenux logrotate replacement: ' + TAPM_POST_PROXMENUX_LOGROTATE && printf 'detected\n' || printf 'not detected\n' + printf 'APT language suppression: ' + TAPM_POST_EXACT_APT_LANGUAGES && printf 'enabled by ProxMenux\n' || + printf 'not detected\n' + printf 'APT forced IPv4: ' + TAPM_POST_EXACT_APT_IPV4 && printf 'enabled by ProxMenux\n' || + printf 'not detected\n' + + printf '\nTAPM managed files:\n' + while IFS= read -r report_file; do + printf ' %-58s %s\n' "$report_file" \ + "$([[ -e "$(TAPM_POST_PATH "$report_file")" ]] && + printf configured || printf absent)" + done < <(TAPM_POST_MANAGED_FILES) + + state_file="$(TAPM_POST_PATH "$TAPM_POST_STATE_DIR/state")" + if [[ -f "$state_file" ]]; then + printf '\nLast TAPM state:\n' + sed 's/^/ /' "$state_file" + fi +} + +TAPM_POST_MIGRATION_PLAN() { + printf '\nRecognized migration plan:\n' + printf ' %-34s %s\n' 'ProxMenux application' \ + "$([[ -d "$(TAPM_POST_PATH '/usr/local/share/proxmenux')" ]] && + printf 'remove after validation' || printf 'not detected')" + printf ' %-34s %s\n' 'ProxMenux Monitor service' \ + "$([[ -e "$(TAPM_POST_PATH '/etc/systemd/system/proxmenux-monitor.service')" ]] && + printf 'stop and remove' || printf 'not detected')" + printf ' %-34s %s\n' '/bin/gzip replacement' \ + "$(TAPM_POST_PROXMENUX_GZIP_WRAPPER && + printf 'repair and verify' || printf 'not detected')" + printf ' %-34s %s\n' 'journald replacement' \ + "$(TAPM_POST_PROXMENUX_JOURNALD && + printf 'replace with TAPM drop-in' || printf 'not detected')" + printf ' %-34s %s\n' 'logrotate replacement' \ + "$(TAPM_POST_PROXMENUX_LOGROTATE && + printf 'restore or repair' || printf 'not detected')" + printf ' %-34s %s\n' 'APT language suppression' \ + "$(TAPM_POST_EXACT_APT_LANGUAGES && + printf 'remove' || printf 'not detected')" + printf ' %-34s %s\n' 'Unconditional APT IPv4' \ + "$(TAPM_POST_EXACT_APT_IPV4 && + printf 'remove' || printf 'not detected')" + printf ' %-34s %s\n' 'Shared dependencies' 'retain' + printf ' %-34s %s\n' 'Unknown administrator files' 'retain and report' +} diff --git a/proxmenu-scripts.sh b/proxmenu-scripts.sh index 74add7e..2cd36a3 100755 --- a/proxmenu-scripts.sh +++ b/proxmenu-scripts.sh @@ -7,6 +7,7 @@ source /opt/idssys/defaults/default.inc source /opt/idssys/ta-proxmenu/defaults.inc source /opt/idssys/ta-proxmenu/inc/git-update.inc source /opt/idssys/ta-proxmenu/inc/ha-status.inc +source /opt/idssys/ta-proxmenu/inc/post-install.inc source /opt/idssys/ta-proxmenu/inc/deploy-iso-nfs-lxc.sh source /opt/idssys/ta-proxmenu/inc/deploy-pulse-lxc.sh source /opt/idssys/ta-proxmenu/inc/virtio-helpers.inc @@ -280,97 +281,6 @@ INSTALL_ACRONIS() { FINISH_ACTION } -INSTALL_PROXMENUX() { - local installer - local temp_dir - - if ! TAPM_CREATE_TEMP_DIR proxmenux; then - FINISH_FAILED_ACTION - return - fi - temp_dir="$TAPM_TEMP_DIR" - installer="${temp_dir}/install.sh" - - if ! TAPM_DOWNLOAD_HTTPS \ - 'https://raw.githubusercontent.com/MacRimi/ProxMenux/main/install_proxmenux.sh' \ - "$installer" 'ProxMenux installer' || - ! bash "$installer" || - [[ ! -x /usr/local/bin/menu ]]; then - TAPM_CLEAN_TEMP_DIR "$temp_dir" - echo -e "${idsCL[LightRed]}ProxMenux installation failed or could not be verified.${idsCL[Default]}" - FINISH_FAILED_ACTION - return - fi - - TAPM_CLEAN_TEMP_DIR "$temp_dir" - /usr/local/bin/menu -} - -PROXMENUX_POST_INSTALL() { - local backup_dir='/var/backups/ta-proxmenu' - local backup_file='' - local post_install_dir='/usr/local/share/proxmenux/scripts/post_install' - local post_install_script="${post_install_dir}/customizable_post_install.sh" - local timestamp - - if [[ ! -f "$post_install_script" ]]; then - INSTALL_PROXMENUX - [[ -f "$post_install_script" ]] || return 1 - fi - - if ! bash "$post_install_script"; then - echo -e "${idsCL[LightRed]}The ProxMenux post-install script failed. A completion marker was not created.${idsCL[Default]}" - FINISH_FAILED_ACTION - return - fi - - if [[ -e /etc/apt/sources.list && ! -f /etc/apt/sources.list ]]; then - echo -e "${idsCL[LightRed]}/etc/apt/sources.list is not a regular file and was not changed.${idsCL[Default]}" - FINISH_FAILED_ACTION - return - fi - - if [[ -s /etc/apt/sources.list ]]; then - timestamp="$(date +%Y%m%d-%H%M%S)" - if ! install -d -m 0700 "$backup_dir" || - ! backup_file="$(mktemp "${backup_dir}/sources.list.proxmenux.${timestamp}.XXXXXX")" || - ! install -m 0600 /etc/apt/sources.list "$backup_file"; then - [[ -n "$backup_file" ]] && rm -f -- "$backup_file" - echo -e "${idsCL[LightRed]}Could not back up /etc/apt/sources.list; it was not cleared.${idsCL[Default]}" - FINISH_FAILED_ACTION - return - fi - fi - - if ! : >/etc/apt/sources.list || - ! chmod 0644 /etc/apt/sources.list; then - echo -e "${idsCL[LightRed]}Could not enforce an empty /etc/apt/sources.list.${idsCL[Default]}" - [[ -n "$backup_file" ]] && - echo "Backup retained at: ${backup_file}" - FINISH_FAILED_ACTION - return - fi - - if ! apt-get update; then - echo -e "${idsCL[LightRed]}APT repository validation failed after ProxMenux post-install.${idsCL[Default]}" - [[ -n "$backup_file" ]] && - echo "Removed sources were retained at: ${backup_file}" - FINISH_FAILED_ACTION - return - fi - - if ! touch /opt/.PROXMENUX_POST_INSTALL; then - echo -e "${idsCL[LightRed]}Post-install succeeded, but the completion marker could not be created.${idsCL[Default]}" - FINISH_FAILED_ACTION - return - fi - - echo -e "\n${idsCL[Green]}ProxMenux post-install completed and APT sources were validated.${idsCL[Default]}" - [[ -n "$backup_file" ]] && - echo "ProxMenux additions to sources.list were saved at: ${backup_file}" - FINISH_ACTION -} - INSTALL_GLANCES() { echo if ! DEBIAN_FRONTEND=noninteractive apt-get install glances -y || @@ -1860,6 +1770,389 @@ SELECT_MENU() { done } +TAPM_POST_PROFILE_SUMMARY() { + echo + echo -e " ${idsCL[LightCyan]}Recommended TAPM host profile${idsCL[Default]}" + echo + echo " [x] System utilities and CPU-appropriate microcode" + echo " [x] Chrony time synchronization (existing timezone preserved)" + echo " [x] Kernel panic recovery (30 seconds)" + echo " [x] Conservative inotify limits" + echo " [x] Persistent journald (1 GiB / 30 days)" + echo " [x] Verify and repair log rotation" + echo " [x] Host memory behavior (swappiness 10)" + echo " [x] Network safeguards" + echo " [x] BBR and TCP Fast Open" + echo " [x] Native parallel gzip support for vzdump" + echo " [ ] APT network compatibility / conditional IPv4 (optional)" + echo + echo " Global vzdump bandwidth and I/O-priority overrides are not applied." +} + +TAPM_POST_FAILURE_RECOVERY() { + local backup_dir="$1" + local mode="$2" + local -a labels=( + "Retry the selected profile" + "Restore the pre-change backup" + "Stop and return to the menu" + ) + local -a values=(retry restore stop) + + while true; do + echo + [[ -n "$backup_dir" ]] && echo " Pre-change backup: ${backup_dir}" + SELECT_MENU "Host Configuration Recovery" labels values + case "$MENU_SELECTION" in + retry) + if TAPM_POST_APPLY_PROFILE "$mode"; then + echo -e "\n${idsCL[Green]}The profile retry completed successfully.${idsCL[Default]}" + echo " New backup: ${TAPM_POST_LAST_BACKUP}" + return 0 + fi + echo -e "\n${idsCL[LightRed]}${TAPM_POST_LAST_ERROR:-The retry failed.}${idsCL[Default]}" + ;; + restore) + if [[ -n "$backup_dir" ]] && + TAPM_POST_RESTORE_BACKUP "$backup_dir"; then + echo -e "\n${idsCL[Green]}The pre-change configuration was restored.${idsCL[Default]}" + return 0 + fi + echo -e "\n${idsCL[LightRed]}The pre-change backup could not be restored.${idsCL[Default]}" + ;; + stop|back) return 1 ;; + quit) EXIT1; exit 0 ;; + esac + done +} + +TAPM_POST_APPLY_RECOMMENDED() { + local backup_dir + local confirmation + + TAPM_POST_DEFAULT_SELECTIONS + TAPM_POST_PROFILE_SUMMARY + read -r -p " Apply this profile to the current host (type yes to continue)? " \ + confirmation + [[ "${confirmation,,}" == yes ]] || return + + if TAPM_POST_APPLY_PROFILE apply; then + echo -e "\n${idsCL[Green]}The TAPM host profile was applied and verified.${idsCL[Default]}" + echo " Backup: ${TAPM_POST_LAST_BACKUP}" + FINISH_ACTION + return + fi + echo -e "\n${idsCL[LightRed]}${TAPM_POST_LAST_ERROR:-Host profile application failed.}${idsCL[Default]}" + backup_dir="$TAPM_POST_LAST_BACKUP" + if TAPM_POST_FAILURE_RECOVERY "$backup_dir" apply; then + FINISH_ACTION + else + FINISH_FAILED_ACTION + fi +} + +TAPM_POST_MIGRATE_PROXMENUX() { + local backup_dir + local confirmation + + if ! TAPM_POST_PROXMENUX_DETECTED; then + echo -e "\n${idsCL[LightYellow]}No ProxMenux installation or recognized artifacts were detected.${idsCL[Default]}" + FINISH_FAILED_ACTION + return + fi + TAPM_POST_DEFAULT_SELECTIONS + TAPM_POST_PROFILE_SUMMARY + TAPM_POST_MIGRATION_PLAN + echo + echo -e " ${idsCL[LightYellow]}Migration will repair recognized ProxMenux settings, restore gzip," + echo -e " apply the TAPM profile, and then remove ProxMenux itself.${idsCL[Default]}" + echo " Shared packages such as dialog, jq, curl, Git, and Python are retained." + echo + read -r -p " Migrate this host and remove ProxMenux (type yes to continue)? " \ + confirmation + [[ "${confirmation,,}" == yes ]] || return + + if TAPM_POST_APPLY_PROFILE migrate; then + echo -e "\n${idsCL[Green]}The host was migrated to the TAPM profile and ProxMenux was removed.${idsCL[Default]}" + echo " Backup: ${TAPM_POST_LAST_BACKUP}" + FINISH_ACTION + return + fi + echo -e "\n${idsCL[LightRed]}${TAPM_POST_LAST_ERROR:-ProxMenux migration failed.}${idsCL[Default]}" + echo " ProxMenux removal is the final migration step; earlier failures leave it installed." + backup_dir="$TAPM_POST_LAST_BACKUP" + if TAPM_POST_FAILURE_RECOVERY "$backup_dir" migrate; then + FINISH_ACTION + else + FINISH_FAILED_ACTION + fi +} + +TAPM_POST_FLAG_LABEL() { + local value="$1" + local label="$2" + + (( value == 1 )) && printf '[x] %s' "$label" || printf '[ ] %s' "$label" +} + +TAPM_POST_TOGGLE_FLAG() { + local variable_name="$1" + local current_value="${!variable_name}" + + (( current_value == 1 )) && + printf -v "$variable_name" '%d' 0 || + printf -v "$variable_name" '%d' 1 +} + +TAPM_POST_CUSTOMIZE() { + local -a labels + local -a values + + TAPM_POST_DEFAULT_SELECTIONS + while true; do + labels=( + "$(TAPM_POST_FLAG_LABEL "$TAPM_POST_DO_UTILITIES" "System utilities")" + "$(TAPM_POST_FLAG_LABEL "$TAPM_POST_DO_TIME" "Time synchronization")" + "$(TAPM_POST_FLAG_LABEL "$TAPM_POST_DO_PANIC" "Kernel panic recovery")" + "$(TAPM_POST_FLAG_LABEL "$TAPM_POST_DO_LIMITS" "Conservative inotify limits")" + "$(TAPM_POST_FLAG_LABEL "$TAPM_POST_DO_JOURNALD" "Journald — 1 GiB / 30 days")" + "$(TAPM_POST_FLAG_LABEL "$TAPM_POST_DO_LOGROTATE" "Verify and repair log rotation")" + "$(TAPM_POST_FLAG_LABEL "$TAPM_POST_DO_MEMORY" "Host memory behavior")" + "$(TAPM_POST_FLAG_LABEL "$TAPM_POST_DO_NETWORK" "Network safeguards")" + "$(TAPM_POST_FLAG_LABEL "$TAPM_POST_DO_BBR" "BBR and TCP Fast Open")" + "$(TAPM_POST_FLAG_LABEL "$TAPM_POST_DO_PIGZ" "Native parallel gzip for vzdump")" + "$(TAPM_POST_FLAG_LABEL "$TAPM_POST_DO_APT_NETWORK" "APT network compatibility check")" + "Apply selected profile" + ) + values=( + utilities time panic limits journald logrotate memory network bbr pigz + apt_network apply + ) + SELECT_MENU "Customize TAPM Host Profile" labels values + case "$MENU_SELECTION" in + utilities) TAPM_POST_TOGGLE_FLAG TAPM_POST_DO_UTILITIES ;; + time) TAPM_POST_TOGGLE_FLAG TAPM_POST_DO_TIME ;; + panic) TAPM_POST_TOGGLE_FLAG TAPM_POST_DO_PANIC ;; + limits) TAPM_POST_TOGGLE_FLAG TAPM_POST_DO_LIMITS ;; + journald) TAPM_POST_TOGGLE_FLAG TAPM_POST_DO_JOURNALD ;; + logrotate) TAPM_POST_TOGGLE_FLAG TAPM_POST_DO_LOGROTATE ;; + memory) TAPM_POST_TOGGLE_FLAG TAPM_POST_DO_MEMORY ;; + network) TAPM_POST_TOGGLE_FLAG TAPM_POST_DO_NETWORK ;; + bbr) TAPM_POST_TOGGLE_FLAG TAPM_POST_DO_BBR ;; + pigz) TAPM_POST_TOGGLE_FLAG TAPM_POST_DO_PIGZ ;; + apt_network) TAPM_POST_TOGGLE_FLAG TAPM_POST_DO_APT_NETWORK ;; + apply) + local backup_dir + local confirmation + echo + read -r -p " Apply the selected profile (type yes to continue)? " \ + confirmation + [[ "${confirmation,,}" == yes ]] || continue + if TAPM_POST_APPLY_PROFILE custom; then + echo -e "\n${idsCL[Green]}The custom TAPM profile was applied.${idsCL[Default]}" + echo " Backup: ${TAPM_POST_LAST_BACKUP}" + FINISH_ACTION + return + fi + echo -e "\n${idsCL[LightRed]}${TAPM_POST_LAST_ERROR:-Custom profile application failed.}${idsCL[Default]}" + backup_dir="$TAPM_POST_LAST_BACKUP" + if TAPM_POST_FAILURE_RECOVERY "$backup_dir" custom; then + FINISH_ACTION + else + FINISH_FAILED_ACTION + fi + return + ;; + back) return ;; + quit) EXIT1; exit 0 ;; + esac + done +} + +TAPM_POST_SHOW_AUDIT() { + MENU_HEADER + echo + TAPM_POST_AUDIT + FINISH_ACTION +} + +TAPM_POST_SHOW_REPORT() { + local report='/var/lib/ta-proxmenu/post-install/last-report.txt' + + MENU_HEADER + echo + if [[ -f "$report" ]]; then + cat "$report" + else + echo -e " ${idsCL[LightYellow]}No completed TAPM host configuration report is available.${idsCL[Default]}" + fi + FINISH_ACTION +} + +TAPM_POST_RESTORE_MENU() { + local backup_base='/var/backups/ta-proxmenu/post-install' + local -a labels=() + local -a values=() + local backup_dir + local confirmation + + if [[ -d "$backup_base" ]]; then + while IFS= read -r backup_dir; do + [[ -f "${backup_dir}/manifest.tsv" ]] || continue + labels+=("${backup_dir##*/}") + values+=("$backup_dir") + done < <(find "$backup_base" -mindepth 1 -maxdepth 1 -type d | + sort -r) + fi + if (( ${#labels[@]} == 0 )); then + echo -e "\n${idsCL[LightYellow]}No TAPM host configuration backups were found.${idsCL[Default]}" + FINISH_FAILED_ACTION + return + fi + + SELECT_MENU "Restore TAPM Host Configuration Backup" labels values + case "$MENU_SELECTION" in + back) return ;; + quit) EXIT1; exit 0 ;; + esac + backup_dir="$MENU_SELECTION" + echo + echo " Selected backup: $backup_dir" + echo " Restoring may also reinstate files or services removed during migration." + read -r -p " Restore this backup (type restore to continue)? " confirmation + [[ "${confirmation,,}" == restore ]] || return + if TAPM_POST_RESTORE_BACKUP "$backup_dir"; then + echo -e "\n${idsCL[Green]}The selected configuration backup was restored.${idsCL[Default]}" + FINISH_ACTION + return + fi + echo -e "\n${idsCL[LightRed]}The selected backup could not be fully restored.${idsCL[Default]}" + FINISH_FAILED_ACTION +} + +TAPM_POST_VZDUMP_APPLY() { + local mode="$1" + local bandwidth="${2:-}" + local ionice="${3:-}" + + TAPM_POST_LAST_ERROR='' + if ! TAPM_POST_PRECHECK; then + echo -e "\n${idsCL[LightRed]}$TAPM_POST_LAST_ERROR${idsCL[Default]}" + FINISH_FAILED_ACTION + return + fi + if ! TAPM_POST_BACKUP >/dev/null; then + echo -e "\n${idsCL[LightRed]}The vzdump configuration backup failed.${idsCL[Default]}" + FINISH_FAILED_ACTION + return + fi + case "$mode" in + defaults) + TAPM_POST_REMOVE_COLON_KEY /etc/vzdump.conf bwlimit && + TAPM_POST_REMOVE_COLON_KEY /etc/vzdump.conf ionice + ;; + maximum) + TAPM_POST_SET_COLON_KEY /etc/vzdump.conf bwlimit 0 && + TAPM_POST_SET_COLON_KEY /etc/vzdump.conf ionice 5 + ;; + custom) + TAPM_POST_SET_COLON_KEY /etc/vzdump.conf bwlimit "$bandwidth" && + TAPM_POST_SET_COLON_KEY /etc/vzdump.conf ionice "$ionice" + ;; + esac + if (( $? == 0 )); then + echo -e "\n${idsCL[Green]}vzdump performance settings were updated.${idsCL[Default]}" + echo " Backup: ${TAPM_POST_LAST_BACKUP}" + FINISH_ACTION + return + fi + echo -e "\n${idsCL[LightRed]}vzdump performance settings could not be updated.${idsCL[Default]}" + FINISH_FAILED_ACTION +} + +TAPM_POST_VZDUMP_MENU() { + local -a labels=( + "Use Proxmox defaults — recommended" + "Maximum throughput — unlimited, ionice 5" + "Set a custom bandwidth limit" + "Restore a TAPM configuration backup" + ) + local -a values=(defaults maximum custom restore) + local confirmation + local bandwidth_mib + local bandwidth_kib + local ionice + + SELECT_MENU "Configure vzdump Performance" labels values + case "$MENU_SELECTION" in + defaults) + TAPM_POST_VZDUMP_APPLY defaults + ;; + maximum) + echo + echo -e "${idsCL[LightYellow]}This can saturate shared storage and increase guest latency.${idsCL[Default]}" + read -r -p " Enable maximum throughput (type yes to continue)? " confirmation + [[ "${confirmation,,}" == yes ]] && + TAPM_POST_VZDUMP_APPLY maximum + ;; + custom) + echo + while true; do + read -r -p " Bandwidth limit in MiB/s (0 means unlimited): " bandwidth_mib + [[ "$bandwidth_mib" =~ ^[0-9]+$ ]] && break + echo " Enter a nonnegative whole number." + done + while true; do + read -r -p " I/O priority [0-8, Proxmox default is 7]: " ionice + [[ "$ionice" =~ ^[0-8]$ ]] && break + echo " Enter a value from 0 through 8." + done + bandwidth_kib=$((bandwidth_mib * 1024)) + TAPM_POST_VZDUMP_APPLY custom "$bandwidth_kib" "$ionice" + ;; + restore) TAPM_POST_RESTORE_MENU ;; + back) return ;; + quit) EXIT1; exit 0 ;; + esac +} + +TAPM_HOST_CONFIGURATION_MENU() { + local -a labels + local -a values + + while true; do + labels=( + "Audit current host" + "Apply/repair recommended TAPM profile" + "Customize TAPM profile" + ) + values=(audit apply customize) + if TAPM_POST_PROXMENUX_DETECTED; then + labels+=("** Migrate from ProxMenux — RECOMMENDED **") + values+=(migrate) + fi + labels+=( + "Configure vzdump performance" + "Restore a TAPM configuration backup" + "View last configuration report" + ) + values+=(vzdump restore report) + + SELECT_MENU "TAPM Host Configuration" labels values + case "$MENU_SELECTION" in + audit) TAPM_POST_SHOW_AUDIT ;; + apply) TAPM_POST_APPLY_RECOMMENDED ;; + customize) TAPM_POST_CUSTOMIZE ;; + migrate) TAPM_POST_MIGRATE_PROXMENUX ;; + vzdump) TAPM_POST_VZDUMP_MENU ;; + restore) TAPM_POST_RESTORE_MENU ;; + report) TAPM_POST_SHOW_REPORT ;; + back) return ;; + quit) EXIT1; exit 0 ;; + esac + done +} + SHOW_ABOUT() { MENU_HEADER echo @@ -1877,10 +2170,10 @@ HOST_SETUP_MENU() { local -a values while true; do - labels=("Run ProxMenux post-install configuration") + labels=("TAPM Host Configuration") values=("post_install") - [ -f /opt/.PROXMENUX_POST_INSTALL ] && - labels[0]="Run ProxMenux post-install configuration (previously run)" + TAPM_POST_PROXMENUX_DETECTED && + labels[0]="TAPM Host Configuration — ProxMenux migration recommended" labels+=("Detect CPU model for live migrations") values+=("cpu") @@ -1902,7 +2195,7 @@ HOST_SETUP_MENU() { SELECT_MENU "Host Setup" labels values case "$MENU_SELECTION" in - post_install) PROXMENUX_POST_INSTALL;; + post_install) TAPM_HOST_CONFIGURATION_MENU;; cpu) DETECT_CPU;; virtio) VIRTIO_MENU;; glances) INSTALL_GLANCES;; @@ -2285,14 +2578,7 @@ if (( ACTION_REQUESTED == 1 )); then omsa) INSTALL_OMSA;; glances) INSTALL_GLANCES;; acronis) INSTALL_ACRONIS;; - post-install|post_install) PROXMENUX_POST_INSTALL;; - proxmenux) - if [[ ! -f /usr/local/bin/menu ]]; then - INSTALL_PROXMENUX - else - /usr/local/bin/menu - fi - ;; + post-install|post_install|proxmenux) TAPM_HOST_CONFIGURATION_MENU;; virtio) VIRTIO_MENU;; sentinelone|s1) INSTALL_S1;; screenconnect) INSTALL_SCREENCONNECT;; diff --git a/tests/test-post-install.sh b/tests/test-post-install.sh new file mode 100644 index 0000000..331a903 --- /dev/null +++ b/tests/test-post-install.sh @@ -0,0 +1,168 @@ +#!/usr/bin/env bash +set -u -o pipefail + +TEST_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +source "${TEST_ROOT}/tests/testlib.sh" +source "${TEST_ROOT}/inc/post-install.inc" + +POST_TEST_ROOT="$(mktemp -d /tmp/tapm-post-install.XXXXXX)" +TAPM_HOST_ROOT="$POST_TEST_ROOT" + +cleanup_post_install_tests() { + if [[ "$POST_TEST_ROOT" == /tmp/tapm-post-install.* && + -d "$POST_TEST_ROOT" ]]; then + rm -rf -- "$POST_TEST_ROOT" + fi +} +trap cleanup_post_install_tests EXIT + +mkdir -p \ + "${POST_TEST_ROOT}/etc/apt/apt.conf.d" \ + "${POST_TEST_ROOT}/etc/systemd" \ + "${POST_TEST_ROOT}/etc/logrotate.d" \ + "${POST_TEST_ROOT}/etc/sysctl.d" \ + "${POST_TEST_ROOT}/etc/network" \ + "${POST_TEST_ROOT}/etc" \ + "${POST_TEST_ROOT}/bin" + +printf 'Acquire::Languages "none";\n' \ + >"${POST_TEST_ROOT}/etc/apt/apt.conf.d/99-disable-translations" +assert_success "ProxMenux APT language setting recognized" \ + TAPM_POST_EXACT_APT_LANGUAGES + +printf 'Acquire::Languages "en";\n' \ + >"${POST_TEST_ROOT}/etc/apt/apt.conf.d/99-disable-translations" +assert_failure "custom APT language setting preserved" \ + TAPM_POST_EXACT_APT_LANGUAGES + +cat >"${POST_TEST_ROOT}/etc/systemd/journald.conf" <<'EOF' +[Journal] +Storage=persistent +Seal=no +SystemMaxUse=64M +RuntimeMaxUse=60M +MaxLevelStore=info +EOF +assert_success "ProxMenux journald replacement recognized" \ + TAPM_POST_PROXMENUX_JOURNALD + +cat >"${POST_TEST_ROOT}/etc/logrotate.conf" <<'EOF' +# ProxMenux optimized configuration (Log2RAM-friendly) +daily +rotate 7 +size 10M +copytruncate +include /etc/logrotate.d +EOF +assert_success "ProxMenux logrotate replacement recognized" \ + TAPM_POST_PROXMENUX_LOGROTATE + +cat >"${POST_TEST_ROOT}/bin/gzip" <<'EOF' +#!/bin/sh +GZIP="-1" +exec /usr/bin/pigz "$@" +EOF +cat >"${POST_TEST_ROOT}/bin/gzip.original" <<'EOF' +#!/bin/sh +if [ "${1:-}" = "--version" ]; then + printf 'gzip 1.13\n' +else + cat +fi +EOF +cp "${POST_TEST_ROOT}/bin/gzip" "${POST_TEST_ROOT}/bin/pigzwrapper" +chmod 0755 \ + "${POST_TEST_ROOT}/bin/gzip" \ + "${POST_TEST_ROOT}/bin/gzip.original" \ + "${POST_TEST_ROOT}/bin/pigzwrapper" +assert_success "ProxMenux gzip wrapper recognized" \ + TAPM_POST_PROXMENUX_GZIP_WRAPPER +assert_success "ProxMenux gzip wrapper repaired from valid original" \ + TAPM_POST_REPAIR_GZIP +assert_failure "repaired gzip is no longer recognized as wrapper" \ + TAPM_POST_PROXMENUX_GZIP_WRAPPER +assert_failure "obsolete gzip original removed after verification" \ + test -e "${POST_TEST_ROOT}/bin/gzip.original" + +assert_success "kernel panic profile written" TAPM_POST_CONFIGURE_PANIC +assert_success "inotify profile written" TAPM_POST_CONFIGURE_LIMITS +assert_success "memory profile written" TAPM_POST_CONFIGURE_MEMORY +assert_success "network profile written" TAPM_POST_CONFIGURE_NETWORK +assert_success "BBR profile written" TAPM_POST_CONFIGURE_BBR +assert_equal 30 \ + "$(awk '/kernel.panic =/ {print $3}' \ + "${POST_TEST_ROOT}/etc/sysctl.d/99-ta-proxmenu-kernel-panic.conf")" \ + "kernel panic delay" +assert_equal 524288 \ + "$(awk '/max_user_watches/ {print $3}' \ + "${POST_TEST_ROOT}/etc/sysctl.d/99-ta-proxmenu-limits.conf")" \ + "inotify watch limit" +assert_equal 10 \ + "$(awk '/vm.swappiness/ {print $3}' \ + "${POST_TEST_ROOT}/etc/sysctl.d/99-ta-proxmenu-memory.conf")" \ + "host swappiness" + +cat >"${POST_TEST_ROOT}/etc/vzdump.conf" <<'EOF' +# existing setting +bwlimit: 50000 +pigz: 4 +pigz: 8 +EOF +assert_success "vzdump key replacement" \ + TAPM_POST_SET_COLON_KEY /etc/vzdump.conf pigz 1 +assert_equal 1 \ + "$(awk -F: '/^pigz:/ {gsub(/[[:space:]]/, "", $2); print $2}' \ + "${POST_TEST_ROOT}/etc/vzdump.conf")" \ + "vzdump pigz setting is unique" +assert_success "vzdump key removal" \ + TAPM_POST_REMOVE_COLON_KEY /etc/vzdump.conf bwlimit +assert_failure "vzdump bandwidth setting removed" \ + grep -q '^bwlimit:' "${POST_TEST_ROOT}/etc/vzdump.conf" + +printf 'deb should-be-backed-up\n' \ + >"${POST_TEST_ROOT}/etc/apt/sources.list" +TAPM_POST_BACKUP_BASE='/var/backups/ta-proxmenu/post-install' +test_post_backup() { + TAPM_POST_BACKUP >/dev/null +} +assert_success "host configuration backup created" test_post_backup +printf 'changed\n' >"${POST_TEST_ROOT}/etc/apt/sources.list" +assert_success "host configuration backup restored" \ + TAPM_POST_RESTORE_BACKUP "$TAPM_POST_LAST_BACKUP" +assert_equal 'deb should-be-backed-up' \ + "$(cat "${POST_TEST_ROOT}/etc/apt/sources.list")" \ + "restored APT sources" + +assert_success "APT source layout enforced in fixture root" \ + TAPM_POST_ENSURE_APT_LAYOUT +assert_equal 0 \ + "$(wc -c <"${POST_TEST_ROOT}/etc/apt/sources.list" | tr -d ' ')" \ + "main APT sources list is empty" + +mkdir -p \ + "${POST_TEST_ROOT}/etc/security/limits.d" \ + "${POST_TEST_ROOT}/usr/local/share/proxmenux" \ + "${POST_TEST_ROOT}/usr/local/bin" +cat >"${POST_TEST_ROOT}/etc/sysctl.d/99-network.conf" <<'EOF' +# Custom administrator network profile +net.ipv4.ip_forward = 1 +EOF +cat >"${POST_TEST_ROOT}/etc/security/limits.d/99-limits.conf" <<'EOF' +# ProxMenux configuration +* soft nofile 1048576 +EOF +cat >"${POST_TEST_ROOT}/usr/local/bin/menu" <<'EOF' +#!/bin/sh +exec /usr/local/share/proxmenux/menu.sh +EOF +assert_success "recognized ProxMenux settings cleaned" \ + TAPM_POST_CLEAN_PROXMENUX_SETTINGS +assert_success "custom similarly named network profile retained" \ + test -f "${POST_TEST_ROOT}/etc/sysctl.d/99-network.conf" +assert_failure "recognized ProxMenux limits profile removed" \ + test -e "${POST_TEST_ROOT}/etc/security/limits.d/99-limits.conf" +assert_success "ProxMenux application removed" TAPM_POST_REMOVE_PROXMENUX_APP +assert_failure "ProxMenux menu launcher removed" \ + test -e "${POST_TEST_ROOT}/usr/local/bin/menu" + +finish_tests