#!/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' }