Files
David Schroeder 53a6e91218 update
2026-07-26 01:20:41 -05:00

1055 lines
32 KiB
Bash

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