From 48b545d7312468d663ed8f8ebf9257fade74b7e5 Mon Sep 17 00:00:00 2001 From: MacRimi Date: Sun, 6 Jul 2025 12:21:29 +0200 Subject: [PATCH] new script post-install automated --- scripts/auto_post_install.sh | 854 +++++ scripts/post_install/auto_post_install.sh | 854 +++++ .../post_install/customizable_post_install.sh | 3211 +++++++++++++++++ scripts/post_install/uninstall-tools.sh | 606 ++++ scripts/uninstall-tools.sh | 577 ++- 5 files changed, 6021 insertions(+), 81 deletions(-) create mode 100644 scripts/auto_post_install.sh create mode 100644 scripts/post_install/auto_post_install.sh create mode 100644 scripts/post_install/customizable_post_install.sh create mode 100644 scripts/post_install/uninstall-tools.sh diff --git a/scripts/auto_post_install.sh b/scripts/auto_post_install.sh new file mode 100644 index 0000000..abd8fa5 --- /dev/null +++ b/scripts/auto_post_install.sh @@ -0,0 +1,854 @@ +#!/bin/bash +# ========================================================== +# ProxMenux - Complete Post-Installation Script with Registration +# ========================================================== +# Author : MacRimi +# Copyright : (c) 2024 MacRimi +# License : MIT (https://raw.githubusercontent.com/MacRimi/ProxMenux/main/LICENSE) +# Version : 1.0 +# Last Updated: 06/07/2025 +# ========================================================== +# Description: +# +# The script performs system optimizations including: +# - Repository configuration and system upgrades +# - Subscription banner removal and UI enhancements +# - Advanced memory management and kernel optimizations +# - Network stack tuning and security hardening +# - Storage optimizations including log2ram for SSD protection +# - System limits increases and entropy generation improvements +# - Journald and logrotate optimizations for better log management +# - Security enhancements including RPC disabling and time synchronization +# - Bash environment customization and system monitoring setup +# +# Key Features: +# - Zero-interaction automation: Runs completely unattended +# - Intelligent hardware detection: Automatically detects SSD/NVMe for log2ram +# - RAM-aware configurations: Adjusts settings based on available system memory +# - Comprehensive error handling: Robust installation with fallback mechanisms +# - Registration system: Tracks installed optimizations for easy management +# - Reboot management: Intelligently handles reboot requirements +# - Translation support: Multi-language compatible through ProxMenux framework +# - Rollback compatibility: All optimizations can be reversed using the uninstall script +# +# This script is based on the post-install script cutotomizable +# ========================================================== + + +# Configuration +REPO_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main" +BASE_DIR="/usr/local/share/proxmenux" +UTILS_FILE="$BASE_DIR/utils.sh" +VENV_PATH="/opt/googletrans-env" +TOOLS_JSON="/usr/local/share/proxmenux/installed_tools.json" + +if [[ -f "$UTILS_FILE" ]]; then + source "$UTILS_FILE" +fi + +load_language +initialize_cache + +# Global variables +OS_CODENAME="$(grep "VERSION_CODENAME=" /etc/os-release | cut -d"=" -f 2 | xargs)" +RAM_SIZE_GB=$(( $(vmstat -s | grep -i "total memory" | xargs | cut -d" " -f 1) / 1024 / 1000)) +NECESSARY_REBOOT=0 +SCRIPT_TITLE="Customizable post-installation optimization script" + +# ========================================================== +# Tool registration system +ensure_tools_json() { + [ -f "$TOOLS_JSON" ] || echo "{}" > "$TOOLS_JSON" +} + +register_tool() { + local tool="$1" + local state="$2" + ensure_tools_json + jq --arg t "$tool" --argjson v "$state" '.[$t]=$v' "$TOOLS_JSON" > "$TOOLS_JSON.tmp" && mv "$TOOLS_JSON.tmp" "$TOOLS_JSON" +} + +# ========================================================== +lvm_repair_check() { + msg_info "$(translate "Checking and repairing old LVM PV headers (if needed)...")" + pvs_output=$(LC_ALL=C pvs -v 2>&1 | grep "old PV header") + if [ -z "$pvs_output" ]; then + msg_ok "$(translate "No PVs with old headers found.")" + register_tool "lvm_repair" true + return + fi + + declare -A vg_map + while read -r line; do + pv=$(echo "$line" | grep -o '/dev/[^ ]*') + vg=$(pvs -o vg_name --noheadings "$pv" | awk '{print $1}') + if [ -n "$vg" ]; then + vg_map["$vg"]=1 + fi + done <<< "$pvs_output" + + for vg in "${!vg_map[@]}"; do + msg_warn "$(translate "Old PV header(s) found in VG $vg. Updating metadata...")" + vgck --updatemetadata "$vg" + vgchange -ay "$vg" + if [ $? -ne 0 ]; then + msg_warn "$(translate "Metadata update failed for VG $vg. Review manually.")" + else + msg_ok "$(translate "Metadata updated successfully for VG $vg")" + fi + done + + msg_ok "$(translate "LVM PV headers check completed")" + +} + +# ========================================================== +cleanup_duplicate_repos() { + local sources_file="/etc/apt/sources.list" + local temp_file=$(mktemp) + local cleaned_count=0 + declare -A seen_repos + + while IFS= read -r line || [[ -n "$line" ]]; do + if [[ "$line" =~ ^[[:space:]]*# ]] || [[ -z "$line" ]]; then + echo "$line" >> "$temp_file" + continue + fi + + if [[ "$line" =~ ^deb ]]; then + read -r _ url dist components <<< "$line" + local key="${url}_${dist}" + if [[ -v "seen_repos[$key]" ]]; then + echo "# $line" >> "$temp_file" + cleaned_count=$((cleaned_count + 1)) + else + echo "$line" >> "$temp_file" + seen_repos[$key]="$components" + fi + else + echo "$line" >> "$temp_file" + fi + done < "$sources_file" + + mv "$temp_file" "$sources_file" + chmod 644 "$sources_file" + + + local pve_files=(/etc/apt/sources.list.d/*proxmox*.list /etc/apt/sources.list.d/*pve*.list) + local pve_content="deb http://download.proxmox.com/debian/pve ${OS_CODENAME} pve-no-subscription" + local pve_public_repo="/etc/apt/sources.list.d/pve-public-repo.list" + local pve_public_repo_exists=false + + if [ -f "$pve_public_repo" ] && grep -q "^deb.*pve-no-subscription" "$pve_public_repo"; then + pve_public_repo_exists=true + fi + + for file in "${pve_files[@]}"; do + if [ -f "$file" ] && grep -q "^deb.*pve-no-subscription" "$file"; then + if ! $pve_public_repo_exists && [[ "$file" == "$pve_public_repo" ]]; then + sed -i 's/^# *deb/deb/' "$file" + pve_public_repo_exists=true + elif [[ "$file" != "$pve_public_repo" ]]; then + sed -i 's/^deb/# deb/' "$file" + cleaned_count=$((cleaned_count + 1)) + fi + fi + done + + apt update + +} + + + +apt_upgrade() { + + + NECESSARY_REBOOT=1 + + + if [ -f /etc/apt/sources.list.d/pve-enterprise.list ] && grep -q "^deb" /etc/apt/sources.list.d/pve-enterprise.list; then + msg_info "$(translate "Disabling enterprise Proxmox repository...")" + sed -i "s/^deb/#deb/g" /etc/apt/sources.list.d/pve-enterprise.list + msg_ok "$(translate "Enterprise Proxmox repository disabled")" + fi + + + if [ -f /etc/apt/sources.list.d/ceph.list ] && grep -q "^deb" /etc/apt/sources.list.d/ceph.list; then + msg_info "$(translate "Disabling enterprise Proxmox Ceph repository...")" + sed -i "s/^deb/#deb/g" /etc/apt/sources.list.d/ceph.list + msg_ok "$(translate "Enterprise Proxmox Ceph repository disabled")" + fi + + + if [ ! -f /etc/apt/sources.list.d/pve-public-repo.list ] || ! grep -q "pve-no-subscription" /etc/apt/sources.list.d/pve-public-repo.list; then + msg_info "$(translate "Enabling free public Proxmox repository...")" + echo "deb http://download.proxmox.com/debian/pve ${OS_CODENAME} pve-no-subscription" > /etc/apt/sources.list.d/pve-public-repo.list + msg_ok "$(translate "Free public Proxmox repository enabled")" + fi + + + + sources_file="/etc/apt/sources.list" + need_update=false + + + sed -i 's|ftp.es.debian.org|deb.debian.org|g' "$sources_file" + + + if grep -q "^deb http://security.debian.org ${OS_CODENAME}-security main contrib" "$sources_file"; then + sed -i "s|^deb http://security.debian.org ${OS_CODENAME}-security main contrib|deb http://security.debian.org/debian-security ${OS_CODENAME}-security main contrib non-free non-free-firmware|" "$sources_file" + msg_ok "$(translate "Replaced security repository with full version")" + need_update=true + fi + + + if ! grep -q "deb http://security.debian.org/debian-security ${OS_CODENAME}-security" "$sources_file"; then + echo "deb http://security.debian.org/debian-security ${OS_CODENAME}-security main contrib non-free non-free-firmware" >> "$sources_file" + need_update=true + fi + + + if ! grep -q "deb http://deb.debian.org/debian ${OS_CODENAME} " "$sources_file"; then + echo "deb http://deb.debian.org/debian ${OS_CODENAME} main contrib non-free non-free-firmware" >> "$sources_file" + need_update=true + fi + + + if ! grep -q "deb http://deb.debian.org/debian ${OS_CODENAME}-updates" "$sources_file"; then + echo "deb http://deb.debian.org/debian ${OS_CODENAME}-updates main contrib non-free non-free-firmware" >> "$sources_file" + need_update=true + fi + + msg_ok "$(translate "Debian repositories configured correctly")" + +# =================================================== + + + if [ ! -f /etc/apt/apt.conf.d/no-bookworm-firmware.conf ]; then + msg_info "$(translate "Disabling non-free firmware warnings...")" + echo 'APT::Get::Update::SourceListWarnings::NonFreeFirmware "false";' > /etc/apt/apt.conf.d/no-bookworm-firmware.conf + msg_ok "$(translate "Non-free firmware warnings disabled")" + fi + + + msg_info "$(translate "Updating package lists...")" + if apt-get update > /dev/null 2>&1; then + msg_ok "$(translate "Package lists updated")" + else + msg_error "$(translate "Failed to update package lists")" + return 1 + fi + + + msg_info "$(translate "Removing conflicting utilities...")" + if /usr/bin/env DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::='--force-confdef' purge ntp openntpd systemd-timesyncd > /dev/null 2>&1; then + msg_ok "$(translate "Conflicting utilities removed")" + else + msg_error "$(translate "Failed to remove conflicting utilities")" + fi + + + + msg_info "$(translate "Performing packages upgrade...")" + apt-get install pv -y > /dev/null 2>&1 + total_packages=$(apt-get -s dist-upgrade | grep "^Inst" | wc -l) + + if [ "$total_packages" -eq 0 ]; then + total_packages=1 + fi + msg_ok "$(translate "Packages upgrade successfull")" + tput civis + tput sc + + + ( + /usr/bin/env DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::='--force-confdef' dist-upgrade 2>&1 | \ + while IFS= read -r line; do + if [[ "$line" =~ ^(Setting up|Unpacking|Preparing to unpack|Processing triggers for) ]]; then + + package_name=$(echo "$line" | sed -E 's/.*(Setting up|Unpacking|Preparing to unpack|Processing triggers for) ([^ ]+).*/\2/') + + + [ -z "$package_name" ] && package_name="$(translate "Unknown")" + + + tput rc + tput ed + + + row=$(( $(tput lines) - 6 )) + tput cup $row 0; echo "$(translate "Installing packages...")" + tput cup $((row + 1)) 0; echo "──────────────────────────────────────────────" + tput cup $((row + 2)) 0; echo "Package: $package_name" + tput cup $((row + 3)) 0; echo "Progress: [ ] 0%" + tput cup $((row + 4)) 0; echo "──────────────────────────────────────────────" + + + for i in $(seq 1 10); do + progress=$((i * 10)) + tput cup $((row + 3)) 9 + printf "[%-50s] %3d%%" "$(printf "#%.0s" $(seq 1 $((progress/2))))" "$progress" + + done + fi + done + ) + + if [ $? -eq 0 ]; then + tput rc + tput ed + msg_ok "$(translate "System upgrade completed")" + fi + + + + msg_info "$(translate "Installing additional Proxmox packages...")" + if /usr/bin/env DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::='--force-confdef' install zfsutils-linux proxmox-backup-restore-image chrony > /dev/null 2>&1; then + msg_ok "$(translate "Additional Proxmox packages installed")" + else + msg_error "$(translate "Failed to install additional Proxmox packages")" + fi + + lvm_repair_check + + cleanup_duplicate_repos + + msg_ok "$(translate "Proxmox update completed")" + +} + +# ========================================================== +remove_subscription_banner() { + msg_info "$(translate "Removing Proxmox subscription nag banner...")" + local JS_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js" + local GZ_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js.gz" + local APT_HOOK="/etc/apt/apt.conf.d/no-nag-script" + + if [[ ! -f "$APT_HOOK" ]]; then + cat <<'EOF' > "$APT_HOOK" +DPkg::Post-Invoke { "dpkg -V proxmox-widget-toolkit | grep -q '/proxmoxlib\.js$'; if [ $? -eq 1 ]; then { echo 'Removing subscription nag from UI...'; sed -i '/.*data\.status.*{/{s/\!//;s/active/NoMoreNagging/;s/Active/NoMoreNagging/}' /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js; rm -f /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js.gz; }; fi"; }; +EOF + fi + + if [[ -f "$JS_FILE" ]]; then + sed -i '/.*data\.status.*{/{s/\!//;s/active/NoMoreNagging/;s/Active/NoMoreNagging/}' "$JS_FILE" + [[ -f "$GZ_FILE" ]] && rm -f "$GZ_FILE" + touch "$JS_FILE" + fi + + apt --reinstall install proxmox-widget-toolkit -y > /dev/null 2>&1 + + msg_ok "$(translate "Subscription nag banner removed successfully")" + register_tool "subscription_banner" true +} + +# ========================================================== + + +configure_time_sync() { + msg_info "$(translate "Configuring system time settings...")" + + this_ip=$(dig +short myip.opendns.com @resolver1.opendns.com) + if [ -z "$this_ip" ]; then + msg_warn "$(translate "Failed to obtain public IP address")" + timezone="UTC" + else + + timezone=$(curl -s "https://ipapi.co/${this_ip}/timezone") + if [ -z "$timezone" ]; then + msg_warn "$(translate "Failed to determine timezone from IP address")" + timezone="UTC" + else + msg_ok "$(translate "Found timezone $timezone for IP $this_ip")" + fi + fi + + msg_info "$(translate "Enabling automatic time synchronization...")" + if timedatectl set-ntp true; then + msg_ok "$(translate "Time settings configured - Timezone:") $timezone" + register_tool "time_sync" true + else + msg_error "$(translate "Failed to enable automatic time synchronization")" + fi + +} + +# ========================================================== +skip_apt_languages() { + msg_info "$(translate "Configuring APT to skip downloading additional languages...")" + local default_locale="" + + if [ -f /etc/default/locale ]; then + default_locale=$(grep '^LANG=' /etc/default/locale | cut -d= -f2 | tr -d '"') + elif [ -f /etc/environment ]; then + default_locale=$(grep '^LANG=' /etc/environment | cut -d= -f2 | tr -d '"') + fi + + default_locale="${default_locale:-en_US.UTF-8}" + local normalized_locale=$(echo "$default_locale" | tr 'A-Z' 'a-z' | sed 's/utf-8/utf8/;s/-/_/') + + if ! locale -a | grep -qi "^$normalized_locale$"; then + if ! grep -qE "^${default_locale}[[:space:]]+UTF-8" /etc/locale.gen; then + echo "$default_locale UTF-8" >> /etc/locale.gen + fi + locale-gen "$default_locale" > /dev/null 2>&1 + fi + + echo 'Acquire::Languages "none";' > /etc/apt/apt.conf.d/99-disable-translations + + msg_ok "$(translate "APT configured to skip additional languages")" + register_tool "apt_languages" true +} + +# ========================================================== +optimize_journald() { + msg_info "$(translate "Limiting size and optimizing journald...")" + NECESSARY_REBOOT=1 + + cat < /etc/systemd/journald.conf +[Journal] +Storage=persistent +SplitMode=none +RateLimitInterval=0 +RateLimitIntervalSec=0 +RateLimitBurst=0 +ForwardToSyslog=no +ForwardToWall=yes +Seal=no +Compress=yes +SystemMaxUse=64M +RuntimeMaxUse=60M +MaxLevelStore=warning +MaxLevelSyslog=warning +MaxLevelKMsg=warning +MaxLevelConsole=notice +MaxLevelWall=crit +EOF + + systemctl restart systemd-journald.service > /dev/null 2>&1 + journalctl --vacuum-size=64M --vacuum-time=1d > /dev/null 2>&1 + journalctl --rotate > /dev/null 2>&1 + + msg_ok "$(translate "Journald optimized - Max size: 64M")" + register_tool "journald" true +} + +# ========================================================== +optimize_logrotate() { + msg_info "$(translate "Optimizing logrotate configuration...")" + local logrotate_conf="/etc/logrotate.conf" + local backup_conf="${logrotate_conf}.bak" + + if ! grep -q "# ProxMenux optimized configuration" "$logrotate_conf"; then + cp "$logrotate_conf" "$backup_conf" + cat < "$logrotate_conf" +# ProxMenux optimized configuration +daily +su root adm +rotate 7 +create +compress +size=10M +delaycompress +copytruncate +include /etc/logrotate.d +EOF + systemctl restart logrotate > /dev/null 2>&1 + fi + + msg_ok "$(translate "Logrotate optimization completed")" + register_tool "logrotate" true +} + +# ========================================================== +increase_system_limits() { + msg_info "$(translate "Increasing various system limits...")" + NECESSARY_REBOOT=1 + + + cat > /etc/sysctl.d/99-maxwatches.conf << EOF +# ProxMenux configuration +fs.inotify.max_user_watches = 1048576 +fs.inotify.max_user_instances = 1048576 +fs.inotify.max_queued_events = 1048576 +EOF + + + cat > /etc/security/limits.d/99-limits.conf << EOF +# ProxMenux configuration +* soft nproc 1048576 +* hard nproc 1048576 +* soft nofile 1048576 +* hard nofile 1048576 +root soft nproc unlimited +root hard nproc unlimited +root soft nofile unlimited +root hard nofile unlimited +EOF + + + cat > /etc/sysctl.d/99-maxkeys.conf << EOF +# ProxMenux configuration +kernel.keys.root_maxkeys=1000000 +kernel.keys.maxkeys=1000000 +EOF + + + for file in /etc/systemd/system.conf /etc/systemd/user.conf; do + if ! grep -q "^DefaultLimitNOFILE=" "$file"; then + echo "DefaultLimitNOFILE=256000" >> "$file" + fi + done + + + for file in /etc/pam.d/common-session /etc/pam.d/runuser-l; do + if ! grep -q "^session required pam_limits.so" "$file"; then + echo 'session required pam_limits.so' >> "$file" + fi + done + + + if ! grep -q "ulimit -n 256000" /root/.profile; then + echo "ulimit -n 256000" >> /root/.profile + fi + + + cat > /etc/sysctl.d/99-swap.conf << EOF +# ProxMenux configuration +vm.swappiness = 10 +vm.vfs_cache_pressure = 100 +EOF + + + cat > /etc/sysctl.d/99-fs.conf << EOF +# ProxMenux configuration +fs.nr_open = 12000000 +fs.file-max = 9223372036854775807 +fs.aio-max-nr = 1048576 +EOF + + msg_ok "$(translate "System limits increase completed.")" + register_tool "system_limits" true +} + +# ========================================================== +configure_entropy() { + msg_info "$(translate "Configuring entropy generation to prevent slowdowns...")" + + /usr/bin/env DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::='--force-confdef' install haveged > /dev/null 2>&1 + + cat < /etc/default/haveged +# -w sets low entropy watermark (in bits) +DAEMON_ARGS="-w 1024" +EOF + + systemctl daemon-reload > /dev/null 2>&1 + systemctl enable haveged > /dev/null 2>&1 + + msg_ok "$(translate "Entropy generation configuration completed")" + register_tool "entropy" true +} + +# ========================================================== +optimize_memory_settings() { + msg_info "$(translate "Optimizing memory settings...")" + NECESSARY_REBOOT=1 + + cat < /etc/sysctl.d/99-memory.conf +# Balanced Memory Optimization +vm.swappiness = 10 +vm.dirty_ratio = 15 +vm.dirty_background_ratio = 5 +vm.overcommit_memory = 1 +vm.max_map_count = 65530 +EOF + + if [ -f /proc/sys/vm/compaction_proactiveness ]; then + echo "vm.compaction_proactiveness = 20" >> /etc/sysctl.d/99-memory.conf + fi + + msg_ok "$(translate "Memory optimization completed.")" + register_tool "memory_settings" true +} + +# ========================================================== +configure_kernel_panic() { + msg_info "$(translate "Configuring kernel panic behavior")" + NECESSARY_REBOOT=1 + + cat < /etc/sysctl.d/99-kernelpanic.conf +# Enable restart on kernel panic, kernel oops and hardlockup +kernel.core_pattern = /var/crash/core.%t.%p +kernel.panic = 10 +kernel.panic_on_oops = 1 +kernel.hardlockup_panic = 1 +EOF + + msg_ok "$(translate "Kernel panic behavior configuration completed")" + register_tool "kernel_panic" true +} + +# ========================================================== +force_apt_ipv4() { + msg_info "$(translate "Configuring APT to use IPv4...")" + + echo 'Acquire::ForceIPv4 "true";' > /etc/apt/apt.conf.d/99-force-ipv4 + + msg_ok "$(translate "APT IPv4 configuration completed")" + register_tool "apt_ipv4" true +} + +# ========================================================== +apply_network_optimizations() { + msg_info "$(translate "Optimizing network settings...")" + NECESSARY_REBOOT=1 + + cat < /etc/sysctl.d/99-network.conf +net.core.netdev_max_backlog=8192 +net.core.optmem_max=8192 +net.core.rmem_max=16777216 +net.core.somaxconn=8151 +net.core.wmem_max=16777216 +net.ipv4.conf.all.accept_redirects = 0 +net.ipv4.conf.all.accept_source_route = 0 +net.ipv4.conf.all.log_martians = 0 +net.ipv4.conf.all.rp_filter = 1 +net.ipv4.conf.all.secure_redirects = 0 +net.ipv4.conf.all.send_redirects = 0 +net.ipv4.conf.default.accept_redirects = 0 +net.ipv4.conf.default.accept_source_route = 0 +net.ipv4.conf.default.log_martians = 0 +net.ipv4.conf.default.rp_filter = 1 +net.ipv4.conf.default.secure_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.ip_local_port_range=1024 65535 +net.ipv4.tcp_base_mss = 1024 +net.ipv4.tcp_challenge_ack_limit = 999999999 +net.ipv4.tcp_fin_timeout=10 +net.ipv4.tcp_keepalive_intvl=30 +net.ipv4.tcp_keepalive_probes=3 +net.ipv4.tcp_keepalive_time=240 +net.ipv4.tcp_limit_output_bytes=65536 +net.ipv4.tcp_max_syn_backlog=8192 +net.ipv4.tcp_max_tw_buckets = 1440000 +net.ipv4.tcp_mtu_probing = 1 +net.ipv4.tcp_rfc1337=1 +net.ipv4.tcp_rmem=8192 87380 16777216 +net.ipv4.tcp_sack=1 +net.ipv4.tcp_slow_start_after_idle=0 +net.ipv4.tcp_syn_retries=3 +net.ipv4.tcp_synack_retries = 2 +net.ipv4.tcp_tw_recycle = 0 +net.ipv4.tcp_tw_reuse = 0 +net.ipv4.tcp_wmem=8192 65536 16777216 +net.netfilter.nf_conntrack_generic_timeout = 60 +net.netfilter.nf_conntrack_helper=0 +net.netfilter.nf_conntrack_max = 524288 +net.netfilter.nf_conntrack_tcp_timeout_established = 28800 +net.unix.max_dgram_qlen = 4096 +EOF + + sysctl --system > /dev/null 2>&1 + + local interfaces_file="/etc/network/interfaces" + if ! grep -q 'source /etc/network/interfaces.d/*' "$interfaces_file"; then + echo "source /etc/network/interfaces.d/*" >> "$interfaces_file" + fi + + msg_ok "$(translate "Network optimization completed")" + register_tool "network_optimization" true +} + +# ========================================================== +disable_rpc() { + msg_info "$(translate "Disabling portmapper/rpcbind for security...")" + + systemctl disable rpcbind > /dev/null 2>&1 + systemctl stop rpcbind > /dev/null 2>&1 + + msg_ok "$(translate "portmapper/rpcbind has been disabled and removed")" + register_tool "disable_rpc" true +} + +# ========================================================== +customize_bashrc() { + msg_info "$(translate "Customizing bashrc for root user...")" + local bashrc="/root/.bashrc" + local bash_profile="/root/.bash_profile" + + if [ ! -f "${bashrc}.bak" ]; then + cp "$bashrc" "${bashrc}.bak" + fi + + + cat >> "$bashrc" << 'EOF' + +# ProxMenux customizations +export HISTTIMEFORMAT="%d/%m/%y %T " +export PS1="\[\e[31m\][\[\e[m\]\[\e[38;5;172m\]\u\[\e[m\]@\[\e[38;5;153m\]\h\[\e[m\] \[\e[38;5;214m\]\W\[\e[m\]\[\e[31m\]]\[\e[m\]\\$ " +alias l='ls -CF' +alias la='ls -A' +alias ll='ls -alF' +alias ls='ls --color=auto' +alias grep='grep --color=auto' +alias fgrep='fgrep --color=auto' +alias egrep='egrep --color=auto' +source /etc/profile.d/bash_completion.sh +EOF + + if ! grep -q "source /root/.bashrc" "$bash_profile"; then + echo "source /root/.bashrc" >> "$bash_profile" + fi + + msg_ok "$(translate "Bashrc customization completed")" + register_tool "bashrc_custom" true +} + +# ========================================================== + +install_log2ram_auto() { + msg_info2 "$(translate "Checking if system disk is SSD or M.2...")" + + ROOT_PART=$(lsblk -no NAME,MOUNTPOINT | grep ' /$' | awk '{print $1}') + SYSTEM_DISK=$(lsblk -no PKNAME /dev/$ROOT_PART 2>/dev/null) + SYSTEM_DISK=${SYSTEM_DISK:-sda} + + if [[ "$SYSTEM_DISK" == nvme* || "$(cat /sys/block/$SYSTEM_DISK/queue/rotational 2>/dev/null)" == "0" ]]; then + msg_ok "$(translate "System disk ($SYSTEM_DISK) is SSD or M.2. Proceeding with log2ram setup.")" + else + msg_warn "$(translate "System disk ($SYSTEM_DISK) is not SSD/M.2. Skipping log2ram installation.")" + return 0 + fi + + # Clean up previous state + rm -rf /tmp/log2ram + rm -f /etc/systemd/system/log2ram* + rm -f /etc/systemd/system/log2ram-daily.* + rm -f /etc/cron.d/log2ram* + rm -f /usr/sbin/log2ram + rm -f /etc/log2ram.conf + rm -f /usr/local/bin/log2ram-check.sh + rm -rf /var/log.hdd + systemctl daemon-reexec >/dev/null 2>&1 + systemctl daemon-reload >/dev/null 2>&1 + + msg_info "$(translate "Installing log2ram from GitHub...")" + + git clone https://github.com/azlux/log2ram.git /tmp/log2ram >/dev/null 2>>/tmp/log2ram_install.log + cd /tmp/log2ram || return 1 + bash install.sh >>/tmp/log2ram_install.log 2>&1 + + if [[ -f /etc/log2ram.conf ]] && systemctl list-units --all | grep -q log2ram; then + msg_ok "$(translate "log2ram installed successfully")" + else + msg_error "$(translate "Failed to install log2ram. See /tmp/log2ram_install.log")" + return 1 + fi + + # Detect RAM + RAM_SIZE_GB=$(free -g | awk '/^Mem:/{print $2}') + [[ -z "$RAM_SIZE_GB" || "$RAM_SIZE_GB" -eq 0 ]] && RAM_SIZE_GB=4 + + if (( RAM_SIZE_GB <= 8 )); then + LOG2RAM_SIZE="128M" + CRON_HOURS=1 + elif (( RAM_SIZE_GB <= 16 )); then + LOG2RAM_SIZE="256M" + CRON_HOURS=3 + else + LOG2RAM_SIZE="512M" + CRON_HOURS=6 + fi + + msg_ok "$(translate "Detected RAM:") $RAM_SIZE_GB GB — $(translate "log2ram size set to:") $LOG2RAM_SIZE" + + sed -i "s/^SIZE=.*/SIZE=$LOG2RAM_SIZE/" /etc/log2ram.conf + rm -f /etc/cron.hourly/log2ram + echo "0 */$CRON_HOURS * * * root /usr/sbin/log2ram write" > /etc/cron.d/log2ram + msg_ok "$(translate "log2ram write scheduled every") $CRON_HOURS $(translate "hour(s)")" + + cat << 'EOF' > /usr/local/bin/log2ram-check.sh +#!/bin/bash +CONF_FILE="/etc/log2ram.conf" +LIMIT_KB=$(grep '^SIZE=' "$CONF_FILE" | cut -d'=' -f2 | tr -d 'M')000 +USED_KB=$(df /var/log --output=used | tail -1) +THRESHOLD=$(( LIMIT_KB * 90 / 100 )) +if (( USED_KB > THRESHOLD )); then + /usr/sbin/log2ram write +fi +EOF + + chmod +x /usr/local/bin/log2ram-check.sh + echo "*/5 * * * * root /usr/local/bin/log2ram-check.sh" > /etc/cron.d/log2ram-auto-sync + msg_ok "$(translate "Auto-sync enabled when /var/log exceeds 90% of") $LOG2RAM_SIZE" + + + register_tool "log2ram" true +} + +# ========================================================== + +run_complete_optimization() { + clear + show_proxmenux_logo + msg_title "$(translate "ProxMenux Optimization Post-Installation")" + + ensure_tools_json + + apt_upgrade + remove_subscription_banner + configure_time_sync + skip_apt_languages + optimize_journald + optimize_logrotate + increase_system_limits + configure_entropy + optimize_memory_settings + configure_kernel_panic + force_apt_ipv4 + apply_network_optimizations + disable_rpc + customize_bashrc + install_log2ram_auto + + + echo -e + msg_success "$(translate "Complete post-installation optimization finished!")" + + if [[ "$NECESSARY_REBOOT" -eq 1 ]]; then + whiptail --title "Reboot Required" \ + --yesno "$(translate "Some changes require a reboot to take effect. Do you want to restart now?")" 10 60 + if [[ $? -eq 0 ]]; then + msg_info "$(translate "Removing no longer required packages and purging old cached updates...")" + apt-get -y autoremove >/dev/null 2>&1 + apt-get -y autoclean >/dev/null 2>&1 + msg_ok "$(translate "Cleanup finished")" + msg_success "$(translate "Press Enter to continue...")" + read -r + msg_warn "$(translate "Rebooting the system...")" + reboot + else + msg_info "$(translate "Removing no longer required packages and purging old cached updates...")" + apt-get -y autoremove >/dev/null 2>&1 + apt-get -y autoclean >/dev/null 2>&1 + msg_ok "$(translate "Cleanup finished")" + msg_info2 "$(translate "You can reboot later manually.")" + msg_success "$(translate "Press Enter to continue...")" + read -r + exit 0 + fi + fi + + msg_success "$(translate "All changes applied. No reboot required.")" + msg_success "$(translate "Press Enter to return to menu...")" + read -r + clear +} + + +if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then + run_complete_optimization +fi \ No newline at end of file diff --git a/scripts/post_install/auto_post_install.sh b/scripts/post_install/auto_post_install.sh new file mode 100644 index 0000000..abd8fa5 --- /dev/null +++ b/scripts/post_install/auto_post_install.sh @@ -0,0 +1,854 @@ +#!/bin/bash +# ========================================================== +# ProxMenux - Complete Post-Installation Script with Registration +# ========================================================== +# Author : MacRimi +# Copyright : (c) 2024 MacRimi +# License : MIT (https://raw.githubusercontent.com/MacRimi/ProxMenux/main/LICENSE) +# Version : 1.0 +# Last Updated: 06/07/2025 +# ========================================================== +# Description: +# +# The script performs system optimizations including: +# - Repository configuration and system upgrades +# - Subscription banner removal and UI enhancements +# - Advanced memory management and kernel optimizations +# - Network stack tuning and security hardening +# - Storage optimizations including log2ram for SSD protection +# - System limits increases and entropy generation improvements +# - Journald and logrotate optimizations for better log management +# - Security enhancements including RPC disabling and time synchronization +# - Bash environment customization and system monitoring setup +# +# Key Features: +# - Zero-interaction automation: Runs completely unattended +# - Intelligent hardware detection: Automatically detects SSD/NVMe for log2ram +# - RAM-aware configurations: Adjusts settings based on available system memory +# - Comprehensive error handling: Robust installation with fallback mechanisms +# - Registration system: Tracks installed optimizations for easy management +# - Reboot management: Intelligently handles reboot requirements +# - Translation support: Multi-language compatible through ProxMenux framework +# - Rollback compatibility: All optimizations can be reversed using the uninstall script +# +# This script is based on the post-install script cutotomizable +# ========================================================== + + +# Configuration +REPO_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main" +BASE_DIR="/usr/local/share/proxmenux" +UTILS_FILE="$BASE_DIR/utils.sh" +VENV_PATH="/opt/googletrans-env" +TOOLS_JSON="/usr/local/share/proxmenux/installed_tools.json" + +if [[ -f "$UTILS_FILE" ]]; then + source "$UTILS_FILE" +fi + +load_language +initialize_cache + +# Global variables +OS_CODENAME="$(grep "VERSION_CODENAME=" /etc/os-release | cut -d"=" -f 2 | xargs)" +RAM_SIZE_GB=$(( $(vmstat -s | grep -i "total memory" | xargs | cut -d" " -f 1) / 1024 / 1000)) +NECESSARY_REBOOT=0 +SCRIPT_TITLE="Customizable post-installation optimization script" + +# ========================================================== +# Tool registration system +ensure_tools_json() { + [ -f "$TOOLS_JSON" ] || echo "{}" > "$TOOLS_JSON" +} + +register_tool() { + local tool="$1" + local state="$2" + ensure_tools_json + jq --arg t "$tool" --argjson v "$state" '.[$t]=$v' "$TOOLS_JSON" > "$TOOLS_JSON.tmp" && mv "$TOOLS_JSON.tmp" "$TOOLS_JSON" +} + +# ========================================================== +lvm_repair_check() { + msg_info "$(translate "Checking and repairing old LVM PV headers (if needed)...")" + pvs_output=$(LC_ALL=C pvs -v 2>&1 | grep "old PV header") + if [ -z "$pvs_output" ]; then + msg_ok "$(translate "No PVs with old headers found.")" + register_tool "lvm_repair" true + return + fi + + declare -A vg_map + while read -r line; do + pv=$(echo "$line" | grep -o '/dev/[^ ]*') + vg=$(pvs -o vg_name --noheadings "$pv" | awk '{print $1}') + if [ -n "$vg" ]; then + vg_map["$vg"]=1 + fi + done <<< "$pvs_output" + + for vg in "${!vg_map[@]}"; do + msg_warn "$(translate "Old PV header(s) found in VG $vg. Updating metadata...")" + vgck --updatemetadata "$vg" + vgchange -ay "$vg" + if [ $? -ne 0 ]; then + msg_warn "$(translate "Metadata update failed for VG $vg. Review manually.")" + else + msg_ok "$(translate "Metadata updated successfully for VG $vg")" + fi + done + + msg_ok "$(translate "LVM PV headers check completed")" + +} + +# ========================================================== +cleanup_duplicate_repos() { + local sources_file="/etc/apt/sources.list" + local temp_file=$(mktemp) + local cleaned_count=0 + declare -A seen_repos + + while IFS= read -r line || [[ -n "$line" ]]; do + if [[ "$line" =~ ^[[:space:]]*# ]] || [[ -z "$line" ]]; then + echo "$line" >> "$temp_file" + continue + fi + + if [[ "$line" =~ ^deb ]]; then + read -r _ url dist components <<< "$line" + local key="${url}_${dist}" + if [[ -v "seen_repos[$key]" ]]; then + echo "# $line" >> "$temp_file" + cleaned_count=$((cleaned_count + 1)) + else + echo "$line" >> "$temp_file" + seen_repos[$key]="$components" + fi + else + echo "$line" >> "$temp_file" + fi + done < "$sources_file" + + mv "$temp_file" "$sources_file" + chmod 644 "$sources_file" + + + local pve_files=(/etc/apt/sources.list.d/*proxmox*.list /etc/apt/sources.list.d/*pve*.list) + local pve_content="deb http://download.proxmox.com/debian/pve ${OS_CODENAME} pve-no-subscription" + local pve_public_repo="/etc/apt/sources.list.d/pve-public-repo.list" + local pve_public_repo_exists=false + + if [ -f "$pve_public_repo" ] && grep -q "^deb.*pve-no-subscription" "$pve_public_repo"; then + pve_public_repo_exists=true + fi + + for file in "${pve_files[@]}"; do + if [ -f "$file" ] && grep -q "^deb.*pve-no-subscription" "$file"; then + if ! $pve_public_repo_exists && [[ "$file" == "$pve_public_repo" ]]; then + sed -i 's/^# *deb/deb/' "$file" + pve_public_repo_exists=true + elif [[ "$file" != "$pve_public_repo" ]]; then + sed -i 's/^deb/# deb/' "$file" + cleaned_count=$((cleaned_count + 1)) + fi + fi + done + + apt update + +} + + + +apt_upgrade() { + + + NECESSARY_REBOOT=1 + + + if [ -f /etc/apt/sources.list.d/pve-enterprise.list ] && grep -q "^deb" /etc/apt/sources.list.d/pve-enterprise.list; then + msg_info "$(translate "Disabling enterprise Proxmox repository...")" + sed -i "s/^deb/#deb/g" /etc/apt/sources.list.d/pve-enterprise.list + msg_ok "$(translate "Enterprise Proxmox repository disabled")" + fi + + + if [ -f /etc/apt/sources.list.d/ceph.list ] && grep -q "^deb" /etc/apt/sources.list.d/ceph.list; then + msg_info "$(translate "Disabling enterprise Proxmox Ceph repository...")" + sed -i "s/^deb/#deb/g" /etc/apt/sources.list.d/ceph.list + msg_ok "$(translate "Enterprise Proxmox Ceph repository disabled")" + fi + + + if [ ! -f /etc/apt/sources.list.d/pve-public-repo.list ] || ! grep -q "pve-no-subscription" /etc/apt/sources.list.d/pve-public-repo.list; then + msg_info "$(translate "Enabling free public Proxmox repository...")" + echo "deb http://download.proxmox.com/debian/pve ${OS_CODENAME} pve-no-subscription" > /etc/apt/sources.list.d/pve-public-repo.list + msg_ok "$(translate "Free public Proxmox repository enabled")" + fi + + + + sources_file="/etc/apt/sources.list" + need_update=false + + + sed -i 's|ftp.es.debian.org|deb.debian.org|g' "$sources_file" + + + if grep -q "^deb http://security.debian.org ${OS_CODENAME}-security main contrib" "$sources_file"; then + sed -i "s|^deb http://security.debian.org ${OS_CODENAME}-security main contrib|deb http://security.debian.org/debian-security ${OS_CODENAME}-security main contrib non-free non-free-firmware|" "$sources_file" + msg_ok "$(translate "Replaced security repository with full version")" + need_update=true + fi + + + if ! grep -q "deb http://security.debian.org/debian-security ${OS_CODENAME}-security" "$sources_file"; then + echo "deb http://security.debian.org/debian-security ${OS_CODENAME}-security main contrib non-free non-free-firmware" >> "$sources_file" + need_update=true + fi + + + if ! grep -q "deb http://deb.debian.org/debian ${OS_CODENAME} " "$sources_file"; then + echo "deb http://deb.debian.org/debian ${OS_CODENAME} main contrib non-free non-free-firmware" >> "$sources_file" + need_update=true + fi + + + if ! grep -q "deb http://deb.debian.org/debian ${OS_CODENAME}-updates" "$sources_file"; then + echo "deb http://deb.debian.org/debian ${OS_CODENAME}-updates main contrib non-free non-free-firmware" >> "$sources_file" + need_update=true + fi + + msg_ok "$(translate "Debian repositories configured correctly")" + +# =================================================== + + + if [ ! -f /etc/apt/apt.conf.d/no-bookworm-firmware.conf ]; then + msg_info "$(translate "Disabling non-free firmware warnings...")" + echo 'APT::Get::Update::SourceListWarnings::NonFreeFirmware "false";' > /etc/apt/apt.conf.d/no-bookworm-firmware.conf + msg_ok "$(translate "Non-free firmware warnings disabled")" + fi + + + msg_info "$(translate "Updating package lists...")" + if apt-get update > /dev/null 2>&1; then + msg_ok "$(translate "Package lists updated")" + else + msg_error "$(translate "Failed to update package lists")" + return 1 + fi + + + msg_info "$(translate "Removing conflicting utilities...")" + if /usr/bin/env DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::='--force-confdef' purge ntp openntpd systemd-timesyncd > /dev/null 2>&1; then + msg_ok "$(translate "Conflicting utilities removed")" + else + msg_error "$(translate "Failed to remove conflicting utilities")" + fi + + + + msg_info "$(translate "Performing packages upgrade...")" + apt-get install pv -y > /dev/null 2>&1 + total_packages=$(apt-get -s dist-upgrade | grep "^Inst" | wc -l) + + if [ "$total_packages" -eq 0 ]; then + total_packages=1 + fi + msg_ok "$(translate "Packages upgrade successfull")" + tput civis + tput sc + + + ( + /usr/bin/env DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::='--force-confdef' dist-upgrade 2>&1 | \ + while IFS= read -r line; do + if [[ "$line" =~ ^(Setting up|Unpacking|Preparing to unpack|Processing triggers for) ]]; then + + package_name=$(echo "$line" | sed -E 's/.*(Setting up|Unpacking|Preparing to unpack|Processing triggers for) ([^ ]+).*/\2/') + + + [ -z "$package_name" ] && package_name="$(translate "Unknown")" + + + tput rc + tput ed + + + row=$(( $(tput lines) - 6 )) + tput cup $row 0; echo "$(translate "Installing packages...")" + tput cup $((row + 1)) 0; echo "──────────────────────────────────────────────" + tput cup $((row + 2)) 0; echo "Package: $package_name" + tput cup $((row + 3)) 0; echo "Progress: [ ] 0%" + tput cup $((row + 4)) 0; echo "──────────────────────────────────────────────" + + + for i in $(seq 1 10); do + progress=$((i * 10)) + tput cup $((row + 3)) 9 + printf "[%-50s] %3d%%" "$(printf "#%.0s" $(seq 1 $((progress/2))))" "$progress" + + done + fi + done + ) + + if [ $? -eq 0 ]; then + tput rc + tput ed + msg_ok "$(translate "System upgrade completed")" + fi + + + + msg_info "$(translate "Installing additional Proxmox packages...")" + if /usr/bin/env DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::='--force-confdef' install zfsutils-linux proxmox-backup-restore-image chrony > /dev/null 2>&1; then + msg_ok "$(translate "Additional Proxmox packages installed")" + else + msg_error "$(translate "Failed to install additional Proxmox packages")" + fi + + lvm_repair_check + + cleanup_duplicate_repos + + msg_ok "$(translate "Proxmox update completed")" + +} + +# ========================================================== +remove_subscription_banner() { + msg_info "$(translate "Removing Proxmox subscription nag banner...")" + local JS_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js" + local GZ_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js.gz" + local APT_HOOK="/etc/apt/apt.conf.d/no-nag-script" + + if [[ ! -f "$APT_HOOK" ]]; then + cat <<'EOF' > "$APT_HOOK" +DPkg::Post-Invoke { "dpkg -V proxmox-widget-toolkit | grep -q '/proxmoxlib\.js$'; if [ $? -eq 1 ]; then { echo 'Removing subscription nag from UI...'; sed -i '/.*data\.status.*{/{s/\!//;s/active/NoMoreNagging/;s/Active/NoMoreNagging/}' /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js; rm -f /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js.gz; }; fi"; }; +EOF + fi + + if [[ -f "$JS_FILE" ]]; then + sed -i '/.*data\.status.*{/{s/\!//;s/active/NoMoreNagging/;s/Active/NoMoreNagging/}' "$JS_FILE" + [[ -f "$GZ_FILE" ]] && rm -f "$GZ_FILE" + touch "$JS_FILE" + fi + + apt --reinstall install proxmox-widget-toolkit -y > /dev/null 2>&1 + + msg_ok "$(translate "Subscription nag banner removed successfully")" + register_tool "subscription_banner" true +} + +# ========================================================== + + +configure_time_sync() { + msg_info "$(translate "Configuring system time settings...")" + + this_ip=$(dig +short myip.opendns.com @resolver1.opendns.com) + if [ -z "$this_ip" ]; then + msg_warn "$(translate "Failed to obtain public IP address")" + timezone="UTC" + else + + timezone=$(curl -s "https://ipapi.co/${this_ip}/timezone") + if [ -z "$timezone" ]; then + msg_warn "$(translate "Failed to determine timezone from IP address")" + timezone="UTC" + else + msg_ok "$(translate "Found timezone $timezone for IP $this_ip")" + fi + fi + + msg_info "$(translate "Enabling automatic time synchronization...")" + if timedatectl set-ntp true; then + msg_ok "$(translate "Time settings configured - Timezone:") $timezone" + register_tool "time_sync" true + else + msg_error "$(translate "Failed to enable automatic time synchronization")" + fi + +} + +# ========================================================== +skip_apt_languages() { + msg_info "$(translate "Configuring APT to skip downloading additional languages...")" + local default_locale="" + + if [ -f /etc/default/locale ]; then + default_locale=$(grep '^LANG=' /etc/default/locale | cut -d= -f2 | tr -d '"') + elif [ -f /etc/environment ]; then + default_locale=$(grep '^LANG=' /etc/environment | cut -d= -f2 | tr -d '"') + fi + + default_locale="${default_locale:-en_US.UTF-8}" + local normalized_locale=$(echo "$default_locale" | tr 'A-Z' 'a-z' | sed 's/utf-8/utf8/;s/-/_/') + + if ! locale -a | grep -qi "^$normalized_locale$"; then + if ! grep -qE "^${default_locale}[[:space:]]+UTF-8" /etc/locale.gen; then + echo "$default_locale UTF-8" >> /etc/locale.gen + fi + locale-gen "$default_locale" > /dev/null 2>&1 + fi + + echo 'Acquire::Languages "none";' > /etc/apt/apt.conf.d/99-disable-translations + + msg_ok "$(translate "APT configured to skip additional languages")" + register_tool "apt_languages" true +} + +# ========================================================== +optimize_journald() { + msg_info "$(translate "Limiting size and optimizing journald...")" + NECESSARY_REBOOT=1 + + cat < /etc/systemd/journald.conf +[Journal] +Storage=persistent +SplitMode=none +RateLimitInterval=0 +RateLimitIntervalSec=0 +RateLimitBurst=0 +ForwardToSyslog=no +ForwardToWall=yes +Seal=no +Compress=yes +SystemMaxUse=64M +RuntimeMaxUse=60M +MaxLevelStore=warning +MaxLevelSyslog=warning +MaxLevelKMsg=warning +MaxLevelConsole=notice +MaxLevelWall=crit +EOF + + systemctl restart systemd-journald.service > /dev/null 2>&1 + journalctl --vacuum-size=64M --vacuum-time=1d > /dev/null 2>&1 + journalctl --rotate > /dev/null 2>&1 + + msg_ok "$(translate "Journald optimized - Max size: 64M")" + register_tool "journald" true +} + +# ========================================================== +optimize_logrotate() { + msg_info "$(translate "Optimizing logrotate configuration...")" + local logrotate_conf="/etc/logrotate.conf" + local backup_conf="${logrotate_conf}.bak" + + if ! grep -q "# ProxMenux optimized configuration" "$logrotate_conf"; then + cp "$logrotate_conf" "$backup_conf" + cat < "$logrotate_conf" +# ProxMenux optimized configuration +daily +su root adm +rotate 7 +create +compress +size=10M +delaycompress +copytruncate +include /etc/logrotate.d +EOF + systemctl restart logrotate > /dev/null 2>&1 + fi + + msg_ok "$(translate "Logrotate optimization completed")" + register_tool "logrotate" true +} + +# ========================================================== +increase_system_limits() { + msg_info "$(translate "Increasing various system limits...")" + NECESSARY_REBOOT=1 + + + cat > /etc/sysctl.d/99-maxwatches.conf << EOF +# ProxMenux configuration +fs.inotify.max_user_watches = 1048576 +fs.inotify.max_user_instances = 1048576 +fs.inotify.max_queued_events = 1048576 +EOF + + + cat > /etc/security/limits.d/99-limits.conf << EOF +# ProxMenux configuration +* soft nproc 1048576 +* hard nproc 1048576 +* soft nofile 1048576 +* hard nofile 1048576 +root soft nproc unlimited +root hard nproc unlimited +root soft nofile unlimited +root hard nofile unlimited +EOF + + + cat > /etc/sysctl.d/99-maxkeys.conf << EOF +# ProxMenux configuration +kernel.keys.root_maxkeys=1000000 +kernel.keys.maxkeys=1000000 +EOF + + + for file in /etc/systemd/system.conf /etc/systemd/user.conf; do + if ! grep -q "^DefaultLimitNOFILE=" "$file"; then + echo "DefaultLimitNOFILE=256000" >> "$file" + fi + done + + + for file in /etc/pam.d/common-session /etc/pam.d/runuser-l; do + if ! grep -q "^session required pam_limits.so" "$file"; then + echo 'session required pam_limits.so' >> "$file" + fi + done + + + if ! grep -q "ulimit -n 256000" /root/.profile; then + echo "ulimit -n 256000" >> /root/.profile + fi + + + cat > /etc/sysctl.d/99-swap.conf << EOF +# ProxMenux configuration +vm.swappiness = 10 +vm.vfs_cache_pressure = 100 +EOF + + + cat > /etc/sysctl.d/99-fs.conf << EOF +# ProxMenux configuration +fs.nr_open = 12000000 +fs.file-max = 9223372036854775807 +fs.aio-max-nr = 1048576 +EOF + + msg_ok "$(translate "System limits increase completed.")" + register_tool "system_limits" true +} + +# ========================================================== +configure_entropy() { + msg_info "$(translate "Configuring entropy generation to prevent slowdowns...")" + + /usr/bin/env DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::='--force-confdef' install haveged > /dev/null 2>&1 + + cat < /etc/default/haveged +# -w sets low entropy watermark (in bits) +DAEMON_ARGS="-w 1024" +EOF + + systemctl daemon-reload > /dev/null 2>&1 + systemctl enable haveged > /dev/null 2>&1 + + msg_ok "$(translate "Entropy generation configuration completed")" + register_tool "entropy" true +} + +# ========================================================== +optimize_memory_settings() { + msg_info "$(translate "Optimizing memory settings...")" + NECESSARY_REBOOT=1 + + cat < /etc/sysctl.d/99-memory.conf +# Balanced Memory Optimization +vm.swappiness = 10 +vm.dirty_ratio = 15 +vm.dirty_background_ratio = 5 +vm.overcommit_memory = 1 +vm.max_map_count = 65530 +EOF + + if [ -f /proc/sys/vm/compaction_proactiveness ]; then + echo "vm.compaction_proactiveness = 20" >> /etc/sysctl.d/99-memory.conf + fi + + msg_ok "$(translate "Memory optimization completed.")" + register_tool "memory_settings" true +} + +# ========================================================== +configure_kernel_panic() { + msg_info "$(translate "Configuring kernel panic behavior")" + NECESSARY_REBOOT=1 + + cat < /etc/sysctl.d/99-kernelpanic.conf +# Enable restart on kernel panic, kernel oops and hardlockup +kernel.core_pattern = /var/crash/core.%t.%p +kernel.panic = 10 +kernel.panic_on_oops = 1 +kernel.hardlockup_panic = 1 +EOF + + msg_ok "$(translate "Kernel panic behavior configuration completed")" + register_tool "kernel_panic" true +} + +# ========================================================== +force_apt_ipv4() { + msg_info "$(translate "Configuring APT to use IPv4...")" + + echo 'Acquire::ForceIPv4 "true";' > /etc/apt/apt.conf.d/99-force-ipv4 + + msg_ok "$(translate "APT IPv4 configuration completed")" + register_tool "apt_ipv4" true +} + +# ========================================================== +apply_network_optimizations() { + msg_info "$(translate "Optimizing network settings...")" + NECESSARY_REBOOT=1 + + cat < /etc/sysctl.d/99-network.conf +net.core.netdev_max_backlog=8192 +net.core.optmem_max=8192 +net.core.rmem_max=16777216 +net.core.somaxconn=8151 +net.core.wmem_max=16777216 +net.ipv4.conf.all.accept_redirects = 0 +net.ipv4.conf.all.accept_source_route = 0 +net.ipv4.conf.all.log_martians = 0 +net.ipv4.conf.all.rp_filter = 1 +net.ipv4.conf.all.secure_redirects = 0 +net.ipv4.conf.all.send_redirects = 0 +net.ipv4.conf.default.accept_redirects = 0 +net.ipv4.conf.default.accept_source_route = 0 +net.ipv4.conf.default.log_martians = 0 +net.ipv4.conf.default.rp_filter = 1 +net.ipv4.conf.default.secure_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.ip_local_port_range=1024 65535 +net.ipv4.tcp_base_mss = 1024 +net.ipv4.tcp_challenge_ack_limit = 999999999 +net.ipv4.tcp_fin_timeout=10 +net.ipv4.tcp_keepalive_intvl=30 +net.ipv4.tcp_keepalive_probes=3 +net.ipv4.tcp_keepalive_time=240 +net.ipv4.tcp_limit_output_bytes=65536 +net.ipv4.tcp_max_syn_backlog=8192 +net.ipv4.tcp_max_tw_buckets = 1440000 +net.ipv4.tcp_mtu_probing = 1 +net.ipv4.tcp_rfc1337=1 +net.ipv4.tcp_rmem=8192 87380 16777216 +net.ipv4.tcp_sack=1 +net.ipv4.tcp_slow_start_after_idle=0 +net.ipv4.tcp_syn_retries=3 +net.ipv4.tcp_synack_retries = 2 +net.ipv4.tcp_tw_recycle = 0 +net.ipv4.tcp_tw_reuse = 0 +net.ipv4.tcp_wmem=8192 65536 16777216 +net.netfilter.nf_conntrack_generic_timeout = 60 +net.netfilter.nf_conntrack_helper=0 +net.netfilter.nf_conntrack_max = 524288 +net.netfilter.nf_conntrack_tcp_timeout_established = 28800 +net.unix.max_dgram_qlen = 4096 +EOF + + sysctl --system > /dev/null 2>&1 + + local interfaces_file="/etc/network/interfaces" + if ! grep -q 'source /etc/network/interfaces.d/*' "$interfaces_file"; then + echo "source /etc/network/interfaces.d/*" >> "$interfaces_file" + fi + + msg_ok "$(translate "Network optimization completed")" + register_tool "network_optimization" true +} + +# ========================================================== +disable_rpc() { + msg_info "$(translate "Disabling portmapper/rpcbind for security...")" + + systemctl disable rpcbind > /dev/null 2>&1 + systemctl stop rpcbind > /dev/null 2>&1 + + msg_ok "$(translate "portmapper/rpcbind has been disabled and removed")" + register_tool "disable_rpc" true +} + +# ========================================================== +customize_bashrc() { + msg_info "$(translate "Customizing bashrc for root user...")" + local bashrc="/root/.bashrc" + local bash_profile="/root/.bash_profile" + + if [ ! -f "${bashrc}.bak" ]; then + cp "$bashrc" "${bashrc}.bak" + fi + + + cat >> "$bashrc" << 'EOF' + +# ProxMenux customizations +export HISTTIMEFORMAT="%d/%m/%y %T " +export PS1="\[\e[31m\][\[\e[m\]\[\e[38;5;172m\]\u\[\e[m\]@\[\e[38;5;153m\]\h\[\e[m\] \[\e[38;5;214m\]\W\[\e[m\]\[\e[31m\]]\[\e[m\]\\$ " +alias l='ls -CF' +alias la='ls -A' +alias ll='ls -alF' +alias ls='ls --color=auto' +alias grep='grep --color=auto' +alias fgrep='fgrep --color=auto' +alias egrep='egrep --color=auto' +source /etc/profile.d/bash_completion.sh +EOF + + if ! grep -q "source /root/.bashrc" "$bash_profile"; then + echo "source /root/.bashrc" >> "$bash_profile" + fi + + msg_ok "$(translate "Bashrc customization completed")" + register_tool "bashrc_custom" true +} + +# ========================================================== + +install_log2ram_auto() { + msg_info2 "$(translate "Checking if system disk is SSD or M.2...")" + + ROOT_PART=$(lsblk -no NAME,MOUNTPOINT | grep ' /$' | awk '{print $1}') + SYSTEM_DISK=$(lsblk -no PKNAME /dev/$ROOT_PART 2>/dev/null) + SYSTEM_DISK=${SYSTEM_DISK:-sda} + + if [[ "$SYSTEM_DISK" == nvme* || "$(cat /sys/block/$SYSTEM_DISK/queue/rotational 2>/dev/null)" == "0" ]]; then + msg_ok "$(translate "System disk ($SYSTEM_DISK) is SSD or M.2. Proceeding with log2ram setup.")" + else + msg_warn "$(translate "System disk ($SYSTEM_DISK) is not SSD/M.2. Skipping log2ram installation.")" + return 0 + fi + + # Clean up previous state + rm -rf /tmp/log2ram + rm -f /etc/systemd/system/log2ram* + rm -f /etc/systemd/system/log2ram-daily.* + rm -f /etc/cron.d/log2ram* + rm -f /usr/sbin/log2ram + rm -f /etc/log2ram.conf + rm -f /usr/local/bin/log2ram-check.sh + rm -rf /var/log.hdd + systemctl daemon-reexec >/dev/null 2>&1 + systemctl daemon-reload >/dev/null 2>&1 + + msg_info "$(translate "Installing log2ram from GitHub...")" + + git clone https://github.com/azlux/log2ram.git /tmp/log2ram >/dev/null 2>>/tmp/log2ram_install.log + cd /tmp/log2ram || return 1 + bash install.sh >>/tmp/log2ram_install.log 2>&1 + + if [[ -f /etc/log2ram.conf ]] && systemctl list-units --all | grep -q log2ram; then + msg_ok "$(translate "log2ram installed successfully")" + else + msg_error "$(translate "Failed to install log2ram. See /tmp/log2ram_install.log")" + return 1 + fi + + # Detect RAM + RAM_SIZE_GB=$(free -g | awk '/^Mem:/{print $2}') + [[ -z "$RAM_SIZE_GB" || "$RAM_SIZE_GB" -eq 0 ]] && RAM_SIZE_GB=4 + + if (( RAM_SIZE_GB <= 8 )); then + LOG2RAM_SIZE="128M" + CRON_HOURS=1 + elif (( RAM_SIZE_GB <= 16 )); then + LOG2RAM_SIZE="256M" + CRON_HOURS=3 + else + LOG2RAM_SIZE="512M" + CRON_HOURS=6 + fi + + msg_ok "$(translate "Detected RAM:") $RAM_SIZE_GB GB — $(translate "log2ram size set to:") $LOG2RAM_SIZE" + + sed -i "s/^SIZE=.*/SIZE=$LOG2RAM_SIZE/" /etc/log2ram.conf + rm -f /etc/cron.hourly/log2ram + echo "0 */$CRON_HOURS * * * root /usr/sbin/log2ram write" > /etc/cron.d/log2ram + msg_ok "$(translate "log2ram write scheduled every") $CRON_HOURS $(translate "hour(s)")" + + cat << 'EOF' > /usr/local/bin/log2ram-check.sh +#!/bin/bash +CONF_FILE="/etc/log2ram.conf" +LIMIT_KB=$(grep '^SIZE=' "$CONF_FILE" | cut -d'=' -f2 | tr -d 'M')000 +USED_KB=$(df /var/log --output=used | tail -1) +THRESHOLD=$(( LIMIT_KB * 90 / 100 )) +if (( USED_KB > THRESHOLD )); then + /usr/sbin/log2ram write +fi +EOF + + chmod +x /usr/local/bin/log2ram-check.sh + echo "*/5 * * * * root /usr/local/bin/log2ram-check.sh" > /etc/cron.d/log2ram-auto-sync + msg_ok "$(translate "Auto-sync enabled when /var/log exceeds 90% of") $LOG2RAM_SIZE" + + + register_tool "log2ram" true +} + +# ========================================================== + +run_complete_optimization() { + clear + show_proxmenux_logo + msg_title "$(translate "ProxMenux Optimization Post-Installation")" + + ensure_tools_json + + apt_upgrade + remove_subscription_banner + configure_time_sync + skip_apt_languages + optimize_journald + optimize_logrotate + increase_system_limits + configure_entropy + optimize_memory_settings + configure_kernel_panic + force_apt_ipv4 + apply_network_optimizations + disable_rpc + customize_bashrc + install_log2ram_auto + + + echo -e + msg_success "$(translate "Complete post-installation optimization finished!")" + + if [[ "$NECESSARY_REBOOT" -eq 1 ]]; then + whiptail --title "Reboot Required" \ + --yesno "$(translate "Some changes require a reboot to take effect. Do you want to restart now?")" 10 60 + if [[ $? -eq 0 ]]; then + msg_info "$(translate "Removing no longer required packages and purging old cached updates...")" + apt-get -y autoremove >/dev/null 2>&1 + apt-get -y autoclean >/dev/null 2>&1 + msg_ok "$(translate "Cleanup finished")" + msg_success "$(translate "Press Enter to continue...")" + read -r + msg_warn "$(translate "Rebooting the system...")" + reboot + else + msg_info "$(translate "Removing no longer required packages and purging old cached updates...")" + apt-get -y autoremove >/dev/null 2>&1 + apt-get -y autoclean >/dev/null 2>&1 + msg_ok "$(translate "Cleanup finished")" + msg_info2 "$(translate "You can reboot later manually.")" + msg_success "$(translate "Press Enter to continue...")" + read -r + exit 0 + fi + fi + + msg_success "$(translate "All changes applied. No reboot required.")" + msg_success "$(translate "Press Enter to return to menu...")" + read -r + clear +} + + +if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then + run_complete_optimization +fi \ No newline at end of file diff --git a/scripts/post_install/customizable_post_install.sh b/scripts/post_install/customizable_post_install.sh new file mode 100644 index 0000000..58a301e --- /dev/null +++ b/scripts/post_install/customizable_post_install.sh @@ -0,0 +1,3211 @@ +#!/bin/bash + +# ========================================================== +# ProxMenux - Customizable script settings for Proxmox post-installation +# ========================================================== +# Author : MacRimi +# Copyright : (c) 2024 MacRimi +# License : MIT (https://raw.githubusercontent.com/MacRimi/ProxMenux/main/LICENSE) +# Version : 1.3 +# Last Updated: 30/06/2025 +# ========================================================== +# Description: +# This script automates post-installation configurations and optimizations +# for Proxmox Virtual Environment (VE). It allows for a variety of system +# customizations, including kernel optimizations, memory management, network +# tweaks, and virtualization environment adjustments. The script facilitates +# easy installation of useful tools and security enhancements, including +# fail2ban, ZFS auto-snapshot, and more. +# +# This script is based on the work of Adrian Jon Kriel from eXtremeSHOK.com, +# and it was originally published as a post-installation script for Proxmox under the +# BSD License. +# +# Copyright (c) Adrian Jon Kriel :: admin@extremeshok.com +# Script updates can be found at: https://github.com/extremeshok/xshok-proxmox +# +# License: BSD (Berkeley Software Distribution) +# +# Additionally, this script incorporates elements from the +# Proxmox VE Post Install script from Proxmox VE Helper-Scripts. +# +# Copyright (c) Proxmox VE Helper-Scripts Community +# Script updates can be found at: https://github.com/community-scripts/ProxmoxVE +# +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# +# Key features: +# - Configures system memory and kernel settings for better performance. +# - Enables IOMMU and VFIO for PCI passthrough and virtualization optimizations. +# - Installs essential tools such as kernel headers, system utilities, and networking tools. +# - Optimizes journald, achievement, and other system services for better efficiency. +# - Enables guest agents for virtualization platforms such as KVM, VMware, and VirtualBox. +# - Updates the system, adds correct repositories, and optimizes system features such as memory, network settings, and more. +# - Provides a wide range of additional options for customization and optimization. +# - Offers interactive selection of features using an easy-to-use menu-driven interface. +# - And many more... +# +# ========================================================== + + +# Configuration +REPO_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main" +BASE_DIR="/usr/local/share/proxmenux" +UTILS_FILE="$BASE_DIR/utils.sh" +VENV_PATH="/opt/googletrans-env" + +if [[ -f "$UTILS_FILE" ]]; then + source "$UTILS_FILE" +fi + +load_language +initialize_cache +# ========================================================== + +# VARIBLES +OS_CODENAME="$(grep "VERSION_CODENAME=" /etc/os-release | cut -d"=" -f 2 | xargs )" +RAM_SIZE_GB=$(( $(vmstat -s | grep -i "total memory" | xargs | cut -d" " -f 1) / 1024 / 1000)) +NECESSARY_REBOOT=0 +SCRIPT_TITLE="Customizable post-installation optimization script" + +TOOLS_JSON="/usr/local/share/proxmenux/installed_tools.json" + +ensure_tools_json() { + [ -f "$TOOLS_JSON" ] || echo "{}" > "$TOOLS_JSON" +} + +register_tool() { + local tool="$1" + local state="$2" + ensure_tools_json + jq --arg t "$tool" --argjson v "$state" '.[$t]=$v' "$TOOLS_JSON" > "$TOOLS_JSON.tmp" && mv "$TOOLS_JSON.tmp" "$TOOLS_JSON" +} + +# ========================================================== + + +enable_kexec() { + msg_info2 "$(translate "Configuring kexec for quick reboots...")" + NECESSARY_REBOOT=1 + register_tool "kexec" true + + # Set default answers for debconf + echo "kexec-tools kexec-tools/load_kexec boolean false" | debconf-set-selections > /dev/null 2>&1 + + msg_info "$(translate "Installing kexec-tools...")" + # Install kexec-tools without showing output + if ! dpkg -s kexec-tools >/dev/null 2>&1; then + /usr/bin/env DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::='--force-confdef' install kexec-tools > /dev/null 2>&1 + msg_ok "$(translate "kexec-tools installed successfully")" + else + msg_ok "$(translate "kexec-tools installed successfully")" + fi + + # Create systemd service file + local service_file="/etc/systemd/system/kexec-pve.service" + if [ ! -f "$service_file" ]; then + cat <<'EOF' > "$service_file" +[Unit] +Description=Loading new kernel into memory +Documentation=man:kexec(8) +DefaultDependencies=no +Before=reboot.target +RequiresMountsFor=/boot +#Before=shutdown.target umount.target final.target + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=/sbin/kexec -d -l /boot/pve/vmlinuz --initrd=/boot/pve/initrd.img --reuse-cmdline + +[Install] +WantedBy=default.target +EOF + msg_ok "$(translate "kexec-pve service file created")" + else + msg_ok "$(translate "kexec-pve service file created")" + fi + + # Enable the service + if ! systemctl is-enabled kexec-pve.service > /dev/null 2>&1; then + systemctl enable kexec-pve.service > /dev/null 2>&1 + msg_ok "$(translate "kexec-pve service enabled")" + else + msg_ok "$(translate "kexec-pve service enabled")" + fi + + if [ ! -f /root/.bash_profile ]; then + touch /root/.bash_profile + fi + + if ! grep -q "alias reboot-quick='systemctl kexec'" /root/.bash_profile; then + echo "alias reboot-quick='systemctl kexec'" >> /root/.bash_profile + msg_ok "$(translate "reboot-quick alias added")" + else + msg_ok "$(translate "reboot-quick alias added")" + fi + + msg_success "$(translate "kexec configured successfully. Use the command: reboot-quick")" +} + + + +# ========================================================== + + + + +apt_upgrade() { + + msg_info2 "$(translate "Configuring Proxmox repositories")" + NECESSARY_REBOOT=1 + + # Disable enterprise proxmox repo + if [ -f /etc/apt/sources.list.d/pve-enterprise.list ] && grep -q "^deb" /etc/apt/sources.list.d/pve-enterprise.list; then + msg_info "$(translate "Disabling enterprise Proxmox repository...")" + sed -i "s/^deb/#deb/g" /etc/apt/sources.list.d/pve-enterprise.list + msg_ok "$(translate "Enterprise Proxmox repository disabled")" + fi + + # Disable enterprise Proxmox Ceph repo + if [ -f /etc/apt/sources.list.d/ceph.list ] && grep -q "^deb" /etc/apt/sources.list.d/ceph.list; then + msg_info "$(translate "Disabling enterprise Proxmox Ceph repository...")" + sed -i "s/^deb/#deb/g" /etc/apt/sources.list.d/ceph.list + msg_ok "$(translate "Enterprise Proxmox Ceph repository disabled")" + fi + + # Enable free public proxmox repo + if [ ! -f /etc/apt/sources.list.d/pve-public-repo.list ] || ! grep -q "pve-no-subscription" /etc/apt/sources.list.d/pve-public-repo.list; then + msg_info "$(translate "Enabling free public Proxmox repository...")" + echo "deb http://download.proxmox.com/debian/pve ${OS_CODENAME} pve-no-subscription" > /etc/apt/sources.list.d/pve-public-repo.list + msg_ok "$(translate "Free public Proxmox repository enabled")" + fi + +# # Enable Proxmox testing repository +# if [ ! -f /etc/apt/sources.list.d/pve-testing-repo.list ] || ! grep -q "pvetest" /etc/apt/sources.list.d/pve-testing-repo.list; then +# msg_info "$(translate "Enabling Proxmox testing repository...")" +# echo -e "deb http://download.proxmox.com/debian/pve ${OS_CODENAME} pvetest\\n" > /etc/apt/sources.list.d/pve-testing-repo.list +# msg_ok "$(translate "Proxmox testing repository enabled")" +# fi + +# ====================================================== +# Configure main Debian repositories +# ====================================================== + + sources_file="/etc/apt/sources.list" + need_update=false + + # Reemplazar ftp.es.debian.org por deb.debian.org si existe + sed -i 's|ftp.es.debian.org|deb.debian.org|g' "$sources_file" + + # Reemplazar línea incompleta de seguridad por la completa + if grep -q "^deb http://security.debian.org ${OS_CODENAME}-security main contrib" "$sources_file"; then + sed -i "s|^deb http://security.debian.org ${OS_CODENAME}-security main contrib|deb http://security.debian.org/debian-security ${OS_CODENAME}-security main contrib non-free non-free-firmware|" "$sources_file" + msg_ok "$(translate "Replaced security repository with full version")" + need_update=true + fi + + # Check and add security repository (completa) + if ! grep -q "deb http://security.debian.org/debian-security ${OS_CODENAME}-security" "$sources_file"; then + echo "deb http://security.debian.org/debian-security ${OS_CODENAME}-security main contrib non-free non-free-firmware" >> "$sources_file" + need_update=true + fi + + # Check and add main repository + if ! grep -q "deb http://deb.debian.org/debian ${OS_CODENAME} " "$sources_file"; then + echo "deb http://deb.debian.org/debian ${OS_CODENAME} main contrib non-free non-free-firmware" >> "$sources_file" + need_update=true + fi + + # Check and add updates repository + if ! grep -q "deb http://deb.debian.org/debian ${OS_CODENAME}-updates" "$sources_file"; then + echo "deb http://deb.debian.org/debian ${OS_CODENAME}-updates main contrib non-free non-free-firmware" >> "$sources_file" + need_update=true + fi + + msg_ok "$(translate "Debian repositories configured correctly")" + +# =================================================== + + # Disable non-free firmware warnings + if [ ! -f /etc/apt/apt.conf.d/no-bookworm-firmware.conf ]; then + msg_info "$(translate "Disabling non-free firmware warnings...")" + echo 'APT::Get::Update::SourceListWarnings::NonFreeFirmware "false";' > /etc/apt/apt.conf.d/no-bookworm-firmware.conf + msg_ok "$(translate "Non-free firmware warnings disabled")" + fi + + # Update package lists + msg_info "$(translate "Updating package lists...")" + if apt-get update > /dev/null 2>&1; then + msg_ok "$(translate "Package lists updated")" + else + msg_error "$(translate "Failed to update package lists")" + return 1 + fi + + # Remove conflicting utilities + msg_info "$(translate "Removing conflicting utilities...")" + if /usr/bin/env DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::='--force-confdef' purge ntp openntpd systemd-timesyncd > /dev/null 2>&1; then + msg_ok "$(translate "Conflicting utilities removed")" + else + msg_error "$(translate "Failed to remove conflicting utilities")" + fi + + + # update proxmox and install system utils + msg_info "$(translate "Performing packages upgrade...")" + apt-get install pv -y > /dev/null 2>&1 + total_packages=$(apt-get -s dist-upgrade | grep "^Inst" | wc -l) + + if [ "$total_packages" -eq 0 ]; then + total_packages=1 + fi + msg_ok "$(translate "Packages upgrade successfull")" + tput civis + tput sc + + + ( + /usr/bin/env DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::='--force-confdef' dist-upgrade 2>&1 | \ + while IFS= read -r line; do + if [[ "$line" =~ ^(Setting up|Unpacking|Preparing to unpack|Processing triggers for) ]]; then + + package_name=$(echo "$line" | sed -E 's/.*(Setting up|Unpacking|Preparing to unpack|Processing triggers for) ([^ ]+).*/\2/') + + + [ -z "$package_name" ] && package_name="$(translate "Unknown")" + + + tput rc + tput ed + + + row=$(( $(tput lines) - 6 )) + tput cup $row 0; echo "$(translate "Installing packages...")" + tput cup $((row + 1)) 0; echo "──────────────────────────────────────────────" + tput cup $((row + 2)) 0; echo "Package: $package_name" + tput cup $((row + 3)) 0; echo "Progress: [ ] 0%" + tput cup $((row + 4)) 0; echo "──────────────────────────────────────────────" + + + for i in $(seq 1 10); do + progress=$((i * 10)) + tput cup $((row + 3)) 9 + printf "[%-50s] %3d%%" "$(printf "#%.0s" $(seq 1 $((progress/2))))" "$progress" + + done + fi + done + ) + + if [ $? -eq 0 ]; then + tput rc + tput ed + msg_ok "$(translate "System upgrade completed")" + fi + + + + + + # Install additional Proxmox packages + msg_info "$(translate "Installing additional Proxmox packages...")" + if /usr/bin/env DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::='--force-confdef' install zfsutils-linux proxmox-backup-restore-image chrony > /dev/null 2>&1; then + msg_ok "$(translate "Additional Proxmox packages installed")" + else + msg_error "$(translate "Failed to install additional Proxmox packages")" + fi + + lvm_repair_check + + cleanup_duplicate_repos + + msg_success "$(translate "Proxmox repository configuration completed")" + +} + + + + +# ========================================================== + + + + + +optimize_journald() { + msg_info2 "$(translate "Limiting size and optimizing journald")" + NECESSARY_REBOOT=1 + local journald_conf="/etc/systemd/journald.conf" + local config_changed=false + + msg_info "$(translate "Configuring journald...")" + + # Create a temporary configuration + cat < /tmp/journald.conf.new +[Journal] +# Store on disk +Storage=persistent +# Don't split Journald logs by user +SplitMode=none +# Disable rate limits +RateLimitInterval=0 +RateLimitIntervalSec=0 +RateLimitBurst=0 +# Disable Journald forwarding to syslog +ForwardToSyslog=no +# Journald forwarding to wall /var/log/kern.log +ForwardToWall=yes +# Disable signing of the logs, save cpu resources +Seal=no +Compress=yes +# Fix the log size +SystemMaxUse=64M +RuntimeMaxUse=60M +# Optimize the logging and speed up tasks +MaxLevelStore=warning +MaxLevelSyslog=warning +MaxLevelKMsg=warning +MaxLevelConsole=notice +MaxLevelWall=crit +EOF + + # Compare the current configuration with the new one + if ! cmp -s "$journald_conf" "/tmp/journald.conf.new"; then + mv "/tmp/journald.conf.new" "$journald_conf" + config_changed=true + else + rm "/tmp/journald.conf.new" + fi + + if [ "$config_changed" = true ]; then + systemctl restart systemd-journald.service > /dev/null 2>&1 + msg_ok "$(translate "Journald configuration updated and service restarted")" + else + msg_ok "$(translate "Journald configuration is already optimized")" + fi + + # Clean and rotate logs + journalctl --vacuum-size=64M --vacuum-time=1d > /dev/null 2>&1 + journalctl --rotate > /dev/null 2>&1 + + msg_success "$(translate "Journald optimization completed")" +} + + + + + +# ========================================================== + + + + + +install_kernel_headers() { + msg_info2 "$(translate "Installing kernel headers")" + NECESSARY_REBOOT=1 + + # Get the current kernel version + local kernel_version=$(uname -r) + local headers_package="linux-headers-${kernel_version}" + + # Check if headers are already installed + if dpkg -s "$headers_package" >/dev/null 2>&1; then + msg_ok "$(translate "Kernel headers are already installed")" + else + msg_info "$(translate "Installing kernel headers...")" + if /usr/bin/env DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::='--force-confdef' install "$headers_package" > /dev/null 2>&1; then + msg_ok "$(translate "Kernel headers installed successfully")" + else + msg_error "$(translate "Failed to install kernel headers")" + return 1 + fi + fi + + msg_success "$(translate "Kernel headers installation process completed")" +} + + + +# ========================================================== + + + +configure_kernel_panic() { + msg_info2 "$(translate "Configuring kernel panic behavior")" + NECESSARY_REBOOT=1 + + local config_file="/etc/sysctl.d/99-kernelpanic.conf" + + msg_info "$(translate "Updating kernel panic configuration...")" + + # Create or update the configuration file + cat < "$config_file" +# Enable restart on kernel panic, kernel oops and hardlockup +kernel.core_pattern = /var/crash/core.%t.%p +# Reboot on kernel panic after 10s +kernel.panic = 10 +# Panic on kernel oops, kernel exploits generally create an oops +kernel.panic_on_oops = 1 +# Panic on a hardlockup +kernel.hardlockup_panic = 1 +EOF + + + msg_ok "$(translate "Kernel panic configuration updated and applied")" + msg_success "$(translate "Kernel panic behavior configuration completed")" +} + + + + +# ========================================================== + + + + +increase_system_limits() { + msg_info2 "$(translate "Increasing various system limits...")" + NECESSARY_REBOOT=1 + + # Function to safely append or replace configuration + append_or_replace() { + local file="$1" + local content="$2" + local temp_file=$(mktemp) + + if [ -f "$file" ]; then + grep -vF "# ProxMenux configuration" "$file" > "$temp_file" + fi + echo -e "# ProxMenux configuration\n$content" >> "$temp_file" + mv "$temp_file" "$file" + } + + # Increase max user watches + msg_info "$(translate "Configuring max user watches...")" + append_or_replace "/etc/sysctl.d/99-maxwatches.conf" " +fs.inotify.max_user_watches = 1048576 +fs.inotify.max_user_instances = 1048576 +fs.inotify.max_queued_events = 1048576" + msg_ok "$(translate "Max user watches configured")" + + # Increase max FD limit / ulimit + msg_info "$(translate "Configuring max FD limit / ulimit...")" + append_or_replace "/etc/security/limits.d/99-limits.conf" " +* soft nproc 1048576 +* hard nproc 1048576 +* soft nofile 1048576 +* hard nofile 1048576 +root soft nproc unlimited +root hard nproc unlimited +root soft nofile unlimited +root hard nofile unlimited" + msg_ok "$(translate "Max FD limit / ulimit configured")" + + # Increase kernel max Key limit + msg_info "$(translate "Configuring kernel max Key limit...")" + append_or_replace "/etc/sysctl.d/99-maxkeys.conf" " +kernel.keys.root_maxkeys=1000000 +kernel.keys.maxkeys=1000000" + msg_ok "$(translate "Kernel max Key limit configured")" + + # Set systemd ulimits + msg_info "$(translate "Setting systemd ulimits...")" + for file in /etc/systemd/system.conf /etc/systemd/user.conf; do + if ! grep -q "^DefaultLimitNOFILE=" "$file"; then + echo "DefaultLimitNOFILE=256000" >> "$file" + fi + done + msg_ok "$(translate "Systemd ulimits set")" + + # Configure PAM limits + msg_info "$(translate "Configuring PAM limits...")" + for file in /etc/pam.d/common-session /etc/pam.d/runuser-l; do + if ! grep -q "^session required pam_limits.so" "$file"; then + echo 'session required pam_limits.so' >> "$file" + fi + done + msg_ok "$(translate "PAM limits configured")" + + # Set ulimit for the shell user + msg_info "$(translate "Setting ulimit for the shell user...")" + if ! grep -q "ulimit -n 256000" /root/.profile; then + echo "ulimit -n 256000" >> /root/.profile + fi + msg_ok "$(translate "Shell user ulimit set")" + + # Configure swappiness + msg_info "$(translate "Configuring kernel swappiness...")" + append_or_replace "/etc/sysctl.d/99-swap.conf" " +vm.swappiness = 10 +vm.vfs_cache_pressure = 100" + msg_ok "$(translate "Swappiness configuration created successfully")" + + # Increase Max FS open files + msg_info "$(translate "Increasing maximum file system open files...")" + append_or_replace "/etc/sysctl.d/99-fs.conf" " +fs.nr_open = 12000000 +fs.file-max = 9223372036854775807 +fs.aio-max-nr = 1048576" + + msg_ok "$(translate "Max FS open files configuration created successfully")" + msg_success "$(translate "System limits increase completed.")" +} + + + +# ========================================================== + + + + +skip_apt_languages_() { + msg_info2 "$(translate "Configuring APT to skip downloading additional languages")" + + local config_file="/etc/apt/apt.conf.d/99-disable-translations" + local config_content="Acquire::Languages \"none\";" + + msg_info "$(translate "Setting APT language configuration...")" + + if [ -f "$config_file" ] && grep -q "$config_content" "$config_file"; then + msg_ok "$(translate "APT language configuration updated")" + else + echo -e "$config_content\n" > "$config_file" + msg_ok "$(translate "APT language configuration updated")" + fi + + msg_success "$(translate "APT configured to skip downloading additional languages")" +} + + + + +skip_apt_languages() { + msg_info2 "$(translate "Configuring APT to skip downloading additional languages")" + + # 1. Detect locale + local default_locale="" + if [ -f /etc/default/locale ]; then + default_locale=$(grep '^LANG=' /etc/default/locale | cut -d= -f2 | tr -d '"') + elif [ -f /etc/environment ]; then + default_locale=$(grep '^LANG=' /etc/environment | cut -d= -f2 | tr -d '"') + fi + + # Fallback + default_locale="${default_locale:-en_US.UTF-8}" + + # Normalize for comparison (en_US.UTF-8 → en_US.utf8) + local normalized_locale + normalized_locale=$(echo "$default_locale" | tr 'A-Z' 'a-z' | sed 's/utf-8/utf8/;s/-/_/') + + # 2. Only generate if missing + if ! locale -a | grep -qi "^$normalized_locale$"; then + # Only add to locale.gen if missing + if ! grep -qE "^${default_locale}[[:space:]]+UTF-8" /etc/locale.gen; then + echo "$default_locale UTF-8" >> /etc/locale.gen + fi + msg_info "$(translate "Generating missing locale:") $default_locale" + locale-gen "$default_locale" + msg_ok "$(translate "Locale generated")" + fi + + # 3. Set APT to skip language downloads + local config_file="/etc/apt/apt.conf.d/99-disable-translations" + local config_content='Acquire::Languages "none";' + + msg_info "$(translate "Setting APT language configuration...")" + if [ -f "$config_file" ] && grep -Fxq "$config_content" "$config_file"; then + msg_ok "$(translate "APT language configuration already set")" + else + echo "$config_content" > "$config_file" + msg_ok "$(translate "APT language configuration updated")" + fi + + msg_success "$(translate "APT configured to skip downloading additional languages")" +} + + + + + + +# ========================================================== + + + + +configure_time_sync() { + msg_info2 "$(translate "Configuring system time settings...")" + + # Set timezone + # msg_info "$(translate "Attempting to set timezone automatically based on IP address...")" + + # Get public IP address + this_ip=$(dig +short myip.opendns.com @resolver1.opendns.com) + if [ -z "$this_ip" ]; then + msg_warn "$(translate "Failed to obtain public IP address")" + timezone="UTC" + else + # Get timezone based on IP + timezone=$(curl -s "https://ipapi.co/${this_ip}/timezone") + if [ -z "$timezone" ]; then + msg_warn "$(translate "Failed to determine timezone from IP address")" + timezone="UTC" + else + msg_ok "$(translate "Found timezone $timezone for IP $this_ip")" + fi + fi + + # Set the timezone + if timedatectl set-timezone "$timezone"; then + msg_ok "$(translate "Timezone set to $timezone")" + else + msg_error "$(translate "Failed to set timezone to $timezone")" + fi + + # Configure time synchronization + msg_info "$(translate "Enabling automatic time synchronization...")" + if timedatectl set-ntp true; then + msg_ok "$(translate "Automatic time synchronization enabled")" + else + msg_error "$(translate "Failed to enable automatic time synchronization")" + fi + + msg_success "$(translate "Time settings configuration completed")" +} + + + + +# ========================================================== + + + + +install_system_utils() { + + command_exists() { + command -v "$1" >/dev/null 2>&1 + } + + + install_single_package() { + local package="$1" + local command_name="${2:-$package}" + local description="$3" + + msg_info "$(translate "Installing") $package ($description)..." + + + local install_success=false + + if command_exists apt; then + + if apt update >/dev/null 2>&1 && apt install -y "$package" >/dev/null 2>&1; then + install_success=true + fi + + elif command_exists yum; then + if yum install -y "$package" >/dev/null 2>&1; then + install_success=true + fi + + elif command_exists dnf; then + if dnf install -y "$package" >/dev/null 2>&1; then + install_success=true + fi + + elif command_exists pacman; then + if pacman -S --noconfirm "$package" >/dev/null 2>&1; then + install_success=true + fi + + elif command_exists zypper; then + if zypper install -y "$package" >/dev/null 2>&1; then + install_success=true + fi + else + cleanup + msg_error "$(translate "No compatible package manager detected")" + return 1 + fi + + cleanup + + + if [ "$install_success" = true ]; then + + hash -r 2>/dev/null + sleep 1 + + if command_exists "$command_name"; then + msg_ok "$package $(translate "installed correctly and available")" + return 0 + else + msg_warn "$package $(translate "installed but command not immediately available")" + msg_info2 "$(translate "May need to restart terminal")" + return 2 + fi + else + msg_error "$(translate "Error installing") $package" + return 1 + fi + } + + + show_utilities_selection() { + local utilities=( + "axel" "$(translate "Download accelerator")" "OFF" + "dos2unix" "$(translate "Convert DOS/Unix text files")" "OFF" + "grc" "$(translate "Generic log/command colorizer")" "OFF" + "htop" "$(translate "Interactive process viewer")" "OFF" + "btop" "$(translate "Modern resource monitor")" "OFF" + "iftop" "$(translate "Real-time network usage")" "OFF" + "iotop" "$(translate "Monitor disk I/O usage")" "OFF" + "iperf3" "$(translate "Network performance testing")" "OFF" + "ipset" "$(translate "Manage IP sets")" "OFF" + "iptraf-ng" "$(translate "Network monitoring tool")" "OFF" + "mlocate" "$(translate "Locate files quickly")" "OFF" + "msr-tools" "$(translate "Access CPU MSRs")" "OFF" + "net-tools" "$(translate "Legacy networking tools")" "OFF" + "sshpass" "$(translate "Non-interactive SSH login")" "OFF" + "tmux" "$(translate "Terminal multiplexer")" "OFF" + "unzip" "$(translate "Extract ZIP files")" "OFF" + "zip" "$(translate "Create ZIP files")" "OFF" + "libguestfs-tools" "$(translate "VM disk utilities")" "OFF" + "aria2" "$(translate "Multi-source downloader")" "OFF" + "cabextract" "$(translate "Extract CAB files")" "OFF" + "wimtools" "$(translate "Manage WIM images")" "OFF" + "genisoimage" "$(translate "Create ISO images")" "OFF" + "chntpw" "$(translate "Edit Windows registry/passwords")" "OFF" + ) + + local selected + selected=$(dialog --clear --backtitle "ProxMenu - $(translate "System Utilities")" \ + --title "$(translate "Select utilities to install")" \ + --checklist "$(translate "Use SPACE to select/deselect, ENTER to confirm")" \ + 20 70 12 "${utilities[@]}" 2>&1 >/dev/tty) + + echo "$selected" + } + + + install_selected_utilities() { + local selected="$1" + + if [ -z "$selected" ]; then + dialog --clear --backtitle "ProxMenu" \ + --title "$(translate "No Selection")" \ + --msgbox "$(translate "No utilities were selected")" 8 40 + return + fi + + clear + show_proxmenux_logo + msg_title "$SCRIPT_TITLE" + msg_info2 "$(translate "Installing selected utilities")" + + local failed=0 + local success=0 + local warning=0 + + + local selected_array + IFS=' ' read -ra selected_array <<< "$selected" + + + declare -A package_to_command=( + ["mlocate"]="locate" + ["msr-tools"]="rdmsr" + ["net-tools"]="netstat" + ["libguestfs-tools"]="virt-filesystems" + ["aria2"]="aria2c" + ["wimtools"]="wimlib-imagex" + ) + + for util in "${selected_array[@]}"; do + + util=$(echo "$util" | tr -d '"') + + + local verify_command="${package_to_command[$util]:-$util}" + + + install_single_package "$util" "$verify_command" "$util" + local install_result=$? + + case $install_result in + 0) + success=$((success + 1)) + ;; + 1) + failed=$((failed + 1)) + ;; + 2) + warning=$((warning + 1)) + ;; + esac + done + + + if [ -f ~/.bashrc ]; then + source ~/.bashrc >/dev/null 2>&1 + fi + hash -r 2>/dev/null + + echo + msg_info2 "$(translate "Installation summary"):" + msg_ok "$(translate "Successful"): $success" + msg_success "$(translate "Common system utilities installation completed")" + + } + + + local selected_utilities + selected_utilities=$(show_utilities_selection) + + if [ -n "$selected_utilities" ]; then + install_selected_utilities "$selected_utilities" + fi + + +} + + + + +install_system_utils_() { + + msg_info2 "$(translate "Installing common system utilities...")" + + if [[ "$LANGUAGE" != "en" ]]; then + msg_lang "$(translate "Generating automatic translations...")" + fi + +packages_list=( + axel "$(translate "Download accelerator")" OFF + dialog "$(translate "Console GUI dialogs")" OFF + dos2unix "$(translate "Convert DOS/Unix text files")" OFF + grc "$(translate "Generic log/command colorizer")" OFF + htop "$(translate "Interactive process viewer")" OFF + btop "$(translate "Modern resource monitor")" OFF + iftop "$(translate "Real-time network usage")" OFF + iotop "$(translate "Monitor disk I/O usage")" OFF + iperf3 "$(translate "Network performance testing")" OFF + ipset "$(translate "Manage IP sets")" OFF + iptraf-ng "$(translate "Network monitoring tool")" OFF + mlocate "$(translate "Locate files quickly")" OFF + msr-tools "$(translate "Access CPU MSRs")" OFF + net-tools "$(translate "Legacy networking tools")" OFF + sshpass "$(translate "Non-interactive SSH login")" OFF + tmux "$(translate "Terminal multiplexer")" OFF + unzip "$(translate "Extract ZIP files")" OFF + zip "$(translate "Create ZIP files")" OFF + libguestfs-tools "$(translate "VM disk utilities")" OFF + aria2c "$(translate "Multi-source downloader")" OFF + cabextract "$(translate "Extract CAB files")" OFF + wimlib-imagex "$(translate "Manage WIM images")" OFF + genisoimage "$(translate "Create ISO images")" OFF + chntpw "$(translate "Edit Windows registry/passwords")" OFF +) + + + cleanup + + choices=$(whiptail --title "System Utilities" \ + --checklist "$(translate "Select the system utilities to install:")" 20 70 12 \ + "${packages_list[@]}" 3>&1 1>&2 2>&3) + + if [ $? -ne 0 ]; then + msg_warn "$(translate "Installation cancelled by user")" + return + fi + + selected_packages=($choices) + + if [ ${#selected_packages[@]} -eq 0 ]; then + msg_warn "$(translate "No packages selected for installation")" + return + fi + + tput civis + tput sc + + for package in "${selected_packages[@]}"; do + if dpkg -s "$package" >/dev/null 2>&1; then + continue + fi + + tput rc + tput ed + + row=$(( $(tput lines) - 6 )) + tput cup $row 0; echo "$(translate "Installing system utilities...")" + tput cup $((row + 1)) 0; echo "──────────────────────────────────────────────" + tput cup $((row + 2)) 0; echo "Package: $package" + tput cup $((row + 3)) 0; echo "Progress: [ ] 0%" + tput cup $((row + 4)) 0; echo "──────────────────────────────────────────────" + + for i in $(seq 1 10); do + progress=$((i * 10)) + tput cup $((row + 3)) 9 + printf "[%-50s] %3d%%" "$(printf "#%.0s" $(seq 1 $((progress/2))))" "$progress" + done + + /usr/bin/env DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::='--force-confdef' install "$package" > /dev/null 2>&1 + done + + tput rc + tput ed + tput cnorm + msg_ok "$(translate "System utilities installed successfully")" + msg_success "$(translate "Common system utilities installation completed")" +} + + + + + + +# ========================================================== + + + + +configure_entropy() { + msg_info2 "$(translate "Configuring entropy generation to prevent slowdowns...")" + + # Install haveged + msg_info "$(translate "Installing haveged...")" + /usr/bin/env DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::='--force-confdef' install haveged > /dev/null 2>&1 + msg_ok "$(translate "haveged installed successfully")" + + # Configure haveged + msg_info "$(translate "Configuring haveged...")" + cat < /etc/default/haveged +# -w sets low entropy watermark (in bits) +DAEMON_ARGS="-w 1024" +EOF + + # Reload systemd daemon + systemctl daemon-reload > /dev/null 2>&1 + + # Enable haveged service + systemctl enable haveged > /dev/null 2>&1 + msg_ok "$(translate "haveged service enabled successfully")" + + msg_success "$(translate "Entropy generation configuration completed")" +} + + + + + +# ========================================================== + + + + +apply_amd_fixes() { + msg_info2 "$(translate "Detecting AMD CPU and applying fixes if necessary...")" + NECESSARY_REBOOT=1 + + local cpu_model=$(grep -i -m 1 "model name" /proc/cpuinfo) + if echo "$cpu_model" | grep -qi "EPYC"; then + msg_info "$(translate "AMD EPYC CPU detected")" + elif echo "$cpu_model" | grep -qi "Ryzen"; then + msg_info "$(translate "AMD Ryzen CPU detected")" + else + msg_ok "$(translate "No AMD CPU detected. Skipping AMD fixes.")" + return + fi + + msg_info "$(translate "Applying AMD-specific fixes...")" + + # Apply kernel fix for random crashing and instability + local grub_file="/etc/default/grub" + if ! grep -q "idle=nomwait" "$grub_file"; then + msg_info "$(translate "Setting kernel parameter: idle=nomwait")" + if sed -i 's/GRUB_CMDLINE_LINUX_DEFAULT="/GRUB_CMDLINE_LINUX_DEFAULT="idle=nomwait /g' "$grub_file"; then + msg_ok "$(translate "Kernel parameter set successfully")" + if update-grub > /dev/null 2>&1; then + msg_ok "$(translate "GRUB configuration updated")" + else + msg_warn "$(translate "Failed to update GRUB configuration")" + fi + else + msg_warn "$(translate "Failed to set kernel parameter")" + fi + else + msg_info "$(translate "Kernel parameter 'idle=nomwait' already set")" + fi + + # Add MSR ignore to fix Windows guest on EPYC/Ryzen host + local kvm_conf="/etc/modprobe.d/kvm.conf" + msg_info "$(translate "Configuring KVM to ignore MSRs...")" + if ! grep -q "options kvm ignore_msrs=Y" "$kvm_conf"; then + echo "options kvm ignore_msrs=Y" >> "$kvm_conf" + msg_ok "$(translate "KVM ignore_msrs option added")" + else + msg_info "$(translate "KVM ignore_msrs option already set")" + fi + if ! grep -q "options kvm report_ignored_msrs=N" "$kvm_conf"; then + echo "options kvm report_ignored_msrs=N" >> "$kvm_conf" + msg_ok "$(translate "KVM report_ignored_msrs option added")" + else + msg_info "$(translate "KVM report_ignored_msrs option already set")" + fi + + # Install the latest Proxmox VE kernel + msg_info "$(translate "Checking for Proxmox VE kernel updates...")" + local current_kernel=$(uname -r | cut -d'-' -f1-2) + local latest_kernel=$(apt-cache search pve-kernel | grep "^pve-kernel-${current_kernel}" | sort -V | tail -n1 | cut -d' ' -f1) + + if [ "$latest_kernel" != "pve-kernel-$current_kernel" ]; then + msg_info "$(translate "Installing the latest Proxmox VE kernel...")" + if /usr/bin/env DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::='--force-confdef' install "$latest_kernel" > /dev/null 2>&1; then + msg_ok "$(translate "Latest Proxmox VE kernel installed successfully")" + else + msg_warn "$(translate "Failed to install the latest Proxmox VE kernel")" + fi + else + msg_ok "$(translate "The latest Proxmox VE kernel is already installed")" + fi + + msg_success "$(translate "AMD CPU fixes applied successfully")" +} + + + + + +# ========================================================== + + + + +force_apt_ipv4() { + msg_info2 "$(translate "Configuring APT to use IPv4...")" + + local config_file="/etc/apt/apt.conf.d/99-force-ipv4" + local config_content="Acquire::ForceIPv4 \"true\";" + + if [ -f "$config_file" ] && grep -q "$config_content" "$config_file"; then + msg_ok "$(translate "APT configured to use IPv4")" + else + msg_info "$(translate "Creating APT configuration to force IPv4...")" + if echo -e "$config_content\n" > "$config_file"; then + msg_ok "$(translate "APT configured to use IPv4")" + fi + fi + + msg_success "$(translate "APT IPv4 configuration completed")" +} + + + + + +# ========================================================== + + + + + +apply_network_optimizations() { + msg_info2 "$(translate "Optimizing network settings...")" + NECESSARY_REBOOT=1 + + local sysctl_conf="/etc/sysctl.d/99-network.conf" + local interfaces_file="/etc/network/interfaces" + + msg_info "$(translate "Applying network optimizations...")" + + # Update sysctl configuration + cat < "$sysctl_conf" +net.core.netdev_max_backlog=8192 +net.core.optmem_max=8192 +net.core.rmem_max=16777216 +net.core.somaxconn=8151 +net.core.wmem_max=16777216 +net.ipv4.conf.all.accept_redirects = 0 +net.ipv4.conf.all.accept_source_route = 0 +net.ipv4.conf.all.log_martians = 0 +net.ipv4.conf.all.rp_filter = 1 +net.ipv4.conf.all.secure_redirects = 0 +net.ipv4.conf.all.send_redirects = 0 +net.ipv4.conf.default.accept_redirects = 0 +net.ipv4.conf.default.accept_source_route = 0 +net.ipv4.conf.default.log_martians = 0 +net.ipv4.conf.default.rp_filter = 1 +net.ipv4.conf.default.secure_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.ip_local_port_range=1024 65535 +net.ipv4.tcp_base_mss = 1024 +net.ipv4.tcp_challenge_ack_limit = 999999999 +net.ipv4.tcp_fin_timeout=10 +net.ipv4.tcp_keepalive_intvl=30 +net.ipv4.tcp_keepalive_probes=3 +net.ipv4.tcp_keepalive_time=240 +net.ipv4.tcp_limit_output_bytes=65536 +net.ipv4.tcp_max_syn_backlog=8192 +net.ipv4.tcp_max_tw_buckets = 1440000 +net.ipv4.tcp_mtu_probing = 1 +net.ipv4.tcp_rfc1337=1 +net.ipv4.tcp_rmem=8192 87380 16777216 +net.ipv4.tcp_sack=1 +net.ipv4.tcp_slow_start_after_idle=0 +net.ipv4.tcp_syn_retries=3 +net.ipv4.tcp_synack_retries = 2 +net.ipv4.tcp_tw_recycle = 0 +net.ipv4.tcp_tw_reuse = 0 +net.ipv4.tcp_wmem=8192 65536 16777216 +net.netfilter.nf_conntrack_generic_timeout = 60 +net.netfilter.nf_conntrack_helper=0 +net.netfilter.nf_conntrack_max = 524288 +net.netfilter.nf_conntrack_tcp_timeout_established = 28800 +net.unix.max_dgram_qlen = 4096 +EOF + + sysctl --system > /dev/null 2>&1 + + # Ensure /etc/network/interfaces includes the interfaces.d directory + if ! grep -q 'source /etc/network/interfaces.d/*' "$interfaces_file"; then + echo "source /etc/network/interfaces.d/*" >> "$interfaces_file" + fi + + msg_ok "$(translate "Network optimizations applied")" + msg_success "$(translate "Network optimization completed")" +} + + + + + +# ========================================================== + + + + + +install_openvswitch() { + msg_info2 "$(translate "Installing OpenVSwitch for virtual internal network...")" + + + + # Install OpenVSwitch + msg_info "$(translate "Installing OpenVSwitch packages...")" + ( + /usr/bin/env DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::='--force-confdef' install openvswitch-switch openvswitch-common 2>&1 | \ + while IFS= read -r line; do + if [[ $line == *"Installing"* ]] || [[ $line == *"Unpacking"* ]]; then + printf "\r%-$(($(tput cols)-1))s\r" " " # Clear current line + printf "\r%s" "$line" + fi + done + ) + + if [ $? -eq 0 ]; then + printf "\r%-$(($(tput cols)-1))s\r" " " # Clear final line + msg_ok "$(translate "OpenVSwitch installed successfully")" + else + printf "\r%-$(($(tput cols)-1))s\r" " " # Clear final line + msg_warn "$(translate "Failed to install OpenVSwitch")" + fi + + # Verify installation + if command -v ovs-vsctl >/dev/null 2>&1; then + msg_success "$(translate "OpenVSwitch is ready to use")" + else + msg_warn "$(translate "OpenVSwitch installation could not be verified")" + fi + +} + + + + + +# ========================================================== + + + + +enable_tcp_fast_open() { + msg_info2 "$(translate "Configuring TCP optimizations...")" + + local bbr_conf="/etc/sysctl.d/99-kernel-bbr.conf" + local tfo_conf="/etc/sysctl.d/99-tcp-fastopen.conf" + local reboot_needed=0 + + # Enable Google TCP BBR congestion control + msg_info "$(translate "Enabling Google TCP BBR congestion control...")" + if [ ! -f "$bbr_conf" ] || ! grep -q "net.ipv4.tcp_congestion_control = bbr" "$bbr_conf"; then + cat < "$bbr_conf" +# TCP BBR congestion control +net.core.default_qdisc = fq +net.ipv4.tcp_congestion_control = bbr +EOF + msg_ok "$(translate "TCP BBR configuration created successfully")" + reboot_needed=1 + else + msg_ok "$(translate "TCP BBR configuration created successfully")" + fi + + # Enable TCP Fast Open + msg_info "$(translate "Enabling TCP Fast Open...")" + if [ ! -f "$tfo_conf" ] || ! grep -q "net.ipv4.tcp_fastopen = 3" "$tfo_conf"; then + cat < "$tfo_conf" +# TCP Fast Open (TFO) +net.ipv4.tcp_fastopen = 3 +EOF + msg_ok "$(translate "TCP Fast Open configuration created successfully")" + else + msg_ok "$(translate "TCP Fast Open configuration created successfully")" + fi + + # Apply changes + sysctl --system > /dev/null 2>&1 + + if [ "$reboot_needed" -eq 1 ]; then + NECESSARY_REBOOT=1 + fi + + msg_success "$(translate "TCP optimizations configuration completed")" +} + + + + +# ========================================================== + + + + +install_ceph() { + msg_info2 "$(translate "Installing Ceph support...")" + + # Check if Ceph is already installed + if pveceph status &>/dev/null; then + msg_ok "$(translate "Ceph is already installed")" + msg_success "$(translate "Ceph installation check completed")" + return 0 + fi + + # Add Ceph repository using HTTPS + msg_info "$(translate "Adding Ceph repository...")" + if echo "deb https://download.proxmox.com/debian/ceph-squid ${OS_CODENAME} no-subscription" > /etc/apt/sources.list.d/ceph-squid.list; then + msg_ok "$(translate "Ceph repository added successfully")" + else + msg_warn "$(translate "Failed to add Ceph repository")" + # Continue execution despite the error + fi + + # Update package lists + msg_info "$(translate "Updating package lists...")" + if apt-get update > /dev/null 2>&1; then + msg_ok "$(translate "Package lists updated successfully")" + else + msg_warn "$(translate "Failed to update package lists")" + # Continue execution despite the error + fi + + # Install Ceph with progress display + msg_info "$(translate "Installing Ceph packages...")" + ( + pveceph install 2>&1 | \ + while IFS= read -r line; do + if [[ $line == *"Installing"* ]] || [[ $line == *"Unpacking"* ]]; then + printf "\r%-$(($(tput cols)-1))s\r" " " + printf "\r%s" "$line" + fi + done + # Clear the last line of output + printf "\r%-$(($(tput cols)-1))s\r" " " + ) + + # Verify Ceph installation + if pveceph status &>/dev/null; then + msg_ok "$(translate "Ceph packages installed and verified successfully")" + msg_success "$(translate "Ceph installation completed")" + else + msg_warn "$(translate "Ceph installation could not be verified")" + msg_success "$(translate "Ceph installation process finished")" + fi +} + + + + +# ========================================================== + + + + + +optimize_zfs_arc() { + msg_info2 "$(translate "Optimizing ZFS ARC size according to available memory...")" + + # Check if ZFS is installed + if ! command -v zfs > /dev/null; then + msg_warn "$(translate "ZFS not detected. Skipping ZFS ARC optimization.")" + return 0 + fi + + # Ensure RAM_SIZE_GB is set + if [ -z "$RAM_SIZE_GB" ]; then + RAM_SIZE_GB=$(free -g | awk '/^Mem:/{print $2}') + if [ -z "$RAM_SIZE_GB" ] || [ "$RAM_SIZE_GB" -eq 0 ]; then + msg_warn "$(translate "Failed to detect RAM size. Using default value of 16GB for ZFS ARC optimization.")" + RAM_SIZE_GB=16 # Default to 16GB if detection fails + fi + fi + + msg_ok "$(translate "Detected RAM size: ${RAM_SIZE_GB} GB")" + + # Calculate ZFS ARC sizes + if [[ "$RAM_SIZE_GB" -le 16 ]]; then + MY_ZFS_ARC_MIN=536870911 # 512MB + MY_ZFS_ARC_MAX=536870912 # 512MB + elif [[ "$RAM_SIZE_GB" -le 32 ]]; then + MY_ZFS_ARC_MIN=1073741823 # 1GB + MY_ZFS_ARC_MAX=1073741824 # 1GB + else + # Use 1/16 of RAM for min and 1/8 for max + MY_ZFS_ARC_MIN=$((RAM_SIZE_GB * 1073741824 / 16)) + MY_ZFS_ARC_MAX=$((RAM_SIZE_GB * 1073741824 / 8)) + fi + + # Enforce the minimum values + MY_ZFS_ARC_MIN=$((MY_ZFS_ARC_MIN > 536870911 ? MY_ZFS_ARC_MIN : 536870911)) + MY_ZFS_ARC_MAX=$((MY_ZFS_ARC_MAX > 536870912 ? MY_ZFS_ARC_MAX : 536870912)) + + # Apply ZFS tuning parameters + local zfs_conf="/etc/modprobe.d/99-zfsarc.conf" + local config_changed=false + + if [ -f "$zfs_conf" ]; then + msg_info "$(translate "Checking existing ZFS ARC configuration...")" + if ! grep -q "zfs_arc_min=$MY_ZFS_ARC_MIN" "$zfs_conf" || \ + ! grep -q "zfs_arc_max=$MY_ZFS_ARC_MAX" "$zfs_conf"; then + msg_ok "$(translate "Changes detected. Updating ZFS ARC configuration...")" + cp "$zfs_conf" "${zfs_conf}.bak" + config_changed=true + else + msg_ok "$(translate "ZFS ARC configuration is up to date")" + fi + else + msg_info "$(translate "Creating new ZFS ARC configuration...")" + config_changed=true + fi + + if $config_changed; then + cat < "$zfs_conf" +# ZFS tuning +# Use 1/8 RAM for MAX cache, 1/16 RAM for MIN cache, or 512MB/1GB for systems with <= 32GB RAM +options zfs zfs_arc_min=$MY_ZFS_ARC_MIN +options zfs zfs_arc_max=$MY_ZFS_ARC_MAX + +# Enable prefetch method +options zfs l2arc_noprefetch=0 + +# Set max write speed to L2ARC (500MB) +options zfs l2arc_write_max=524288000 +options zfs zfs_txg_timeout=60 +EOF + + if [ $? -eq 0 ]; then + msg_ok "$(translate "ZFS ARC configuration file created/updated successfully")" + NECESSARY_REBOOT=1 + else + msg_error "$(translate "Failed to create/update ZFS ARC configuration file")" + fi + fi + + msg_success "$(translate "ZFS ARC optimization completed")" +} + + + + +# ========================================================== + + + + + +install_zfs_auto_snapshot() { + msg_info2 "$(translate "Installing and configuring ZFS auto-snapshot...")" + + # Check if zfs-auto-snapshot is already installed + if command -v zfs-auto-snapshot >/dev/null 2>&1; then + msg_ok "$(translate "zfs-auto-snapshot is already installed")" + else + # Install zfs-auto-snapshot + msg_info "$(translate "Installing zfs-auto-snapshot package...")" + if /usr/bin/env DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::='--force-confdef' install zfs-auto-snapshot > /dev/null 2>&1; then + msg_ok "$(translate "zfs-auto-snapshot installed successfully")" + else + msg_error "$(translate "Failed to install zfs-auto-snapshot")" + return 1 + fi + fi + + # Configure snapshot schedules + config_zfs_auto_snapshot + + msg_success "$(translate "ZFS auto-snapshot installation and configuration completed")" +} + +config_zfs_auto_snapshot() { + msg_info "$(translate "Configuring snapshot schedules...")" + + # Update 15-minute snapshots + update_snapshot_schedule "/etc/cron.d/zfs-auto-snapshot" "frequent" "4" "*/15" + + # Update other snapshot schedules + update_snapshot_schedule "/etc/cron.hourly/zfs-auto-snapshot" "hourly" "1" + update_snapshot_schedule "/etc/cron.daily/zfs-auto-snapshot" "daily" "1" + update_snapshot_schedule "/etc/cron.weekly/zfs-auto-snapshot" "weekly" "1" + update_snapshot_schedule "/etc/cron.monthly/zfs-auto-snapshot" "monthly" "1" +} + +update_snapshot_schedule() { + local config_file="$1" + local schedule_type="$2" + local keep_value="$3" + local frequency="$4" + + if [ -f "$config_file" ]; then + if ! grep -q ".*--keep=$keep_value" "$config_file"; then + if [ -n "$frequency" ]; then + sed -i "s|^\*/[0-9]*.*--keep=[0-9]*|$frequency * * * * root /usr/sbin/zfs-auto-snapshot --quiet --syslog --label=$schedule_type --keep=$keep_value|" "$config_file" + else + sed -i "s|--keep=[0-9]*|--keep=$keep_value|g" "$config_file" + fi + msg_ok "$(translate "Updated $schedule_type snapshot schedule")" + else + msg_ok "$(translate "$schedule_type snapshot schedule already configured")" + fi + fi +} + + + + +# ========================================================== + + + + + +disable_rpc() { + msg_info2 "$(translate "Disabling portmapper/rpcbind for security...")" + + msg_info "$(translate "Disabling and stopping rpcbind service...")" + + # Disable and stop rpcbind + systemctl disable rpcbind > /dev/null 2>&1 + systemctl stop rpcbind > /dev/null 2>&1 + + msg_ok "$(translate "rpcbind service has been disabled and stopped")" + + msg_success "$(translate "portmapper/rpcbind has been disabled and removed")" +} + + + + +# ========================================================== + + + + +configure_pigz() { + msg_info2 "$(translate "Configuring pigz as a faster replacement for gzip...")" + + # Enable pigz in vzdump configuration + msg_info "$(translate "Enabling pigz in vzdump configuration...")" + if ! grep -q "^pigz: 1" /etc/vzdump.conf; then + sed -i "s/#pigz:.*/pigz: 1/" /etc/vzdump.conf + msg_ok "$(translate "pigz enabled in vzdump configuration")" + else + msg_ok "$(translate "pigz enabled in vzdump configuration")" + fi + + # Install pigz + if ! dpkg -s pigz >/dev/null 2>&1; then + msg_info "$(translate "Installing pigz...")" + if /usr/bin/env DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::='--force-confdef' install pigz > /dev/null 2>&1; then + msg_ok "$(translate "pigz installed successfully")" + else + msg_error "$(translate "Failed to install pigz")" + return 1 + fi + else + msg_ok "$(translate "pigz installed successfully")" + fi + + # Create pigz wrapper script + msg_info "$(translate "Creating pigz wrapper script...")" + if [ ! -f /bin/pigzwrapper ] || ! cmp -s /bin/pigzwrapper - < /bin/pigzwrapper +#!/bin/sh +PATH=/bin:\$PATH +GZIP="-1" +exec /usr/bin/pigz "\$@" +EOF + chmod +x /bin/pigzwrapper + msg_ok "$(translate "pigz wrapper script created")" + else + msg_ok "$(translate "pigz wrapper script created")" + fi + + # Replace gzip with pigz wrapper + msg_info "$(translate "Replacing gzip with pigz wrapper...")" + if [ ! -f /bin/gzip.original ]; then + mv -f /bin/gzip /bin/gzip.original && \ + cp -f /bin/pigzwrapper /bin/gzip && \ + chmod +x /bin/gzip + msg_ok "$(translate "gzip replaced with pigz wrapper successfully")" + else + msg_ok "$(translate "gzip replaced with pigz wrapper successfully")" + fi + + msg_success "$(translate "pigz configuration completed")" +} + + + + + +# ========================================================== + + + + + +install_fail2ban() { + msg_info2 "$(translate "Installing and configuring Fail2Ban to protect the web interface...")" + + +# if dpkg -l | grep -qw fail2ban; then +# msg_info "$(translate "Removing existing Fail2Ban installation...")" +# apt-get remove --purge -y fail2ban >/dev/null 2>&1 +# rm -rf /etc/fail2ban /var/lib/fail2ban /var/run/fail2ban +# msg_ok "$(translate "Fail2Ban removed successfully")" +# else +# msg_ok "$(translate "Fail2Ban was not installed")" +# fi + + + msg_info "$(translate "Installing Fail2Ban...")" + apt-get update >/dev/null 2>&1 && apt-get install -y fail2ban >/dev/null 2>&1 + if [[ $? -eq 0 ]]; then + msg_ok "$(translate "Fail2Ban installed successfully")" + else + msg_error "$(translate "Failed to install Fail2Ban")" + return 1 + fi + + + mkdir -p /etc/fail2ban/jail.d /etc/fail2ban/filter.d + + + msg_info "$(translate "Configuring Proxmox filter...")" + cat > /etc/fail2ban/filter.d/proxmox.conf << EOF +[Definition] +failregex = pvedaemon\[.*authentication failure; rhost= user=.* msg=.* +ignoreregex = +EOF + msg_ok "$(translate "Proxmox filter configured")" + + + msg_info "$(translate "Configuring Proxmox jail...")" + cat > /etc/fail2ban/jail.d/proxmox.conf << EOF +[proxmox] +enabled = true +port = https,http,8006,8007 +filter = proxmox +logpath = /var/log/daemon.log +maxretry = 3 +bantime = 3600 +findtime = 600 +EOF + msg_ok "$(translate "Proxmox jail configured")" + + + msg_info "$(translate "Configuring general Fail2Ban settings...")" + cat > /etc/fail2ban/jail.local << EOF +[DEFAULT] +ignoreip = 127.0.0.1 +bantime = 86400 +maxretry = 2 +findtime = 1800 + +[ssh-iptables] +enabled = true +filter = sshd +action = iptables[name=SSH, port=ssh, protocol=tcp] +logpath = /var/log/auth.log +maxretry = 2 +findtime = 3600 +bantime = 32400 +EOF + msg_ok "$(translate "General Fail2Ban settings configured")" + + + msg_info "$(translate "Stopping Fail2Ban service...")" + systemctl stop fail2ban >/dev/null 2>&1 + msg_ok "$(translate "Fail2Ban service stopped")" + + + msg_info "$(translate "Ensuring authentication logs exist...")" + touch /var/log/auth.log /var/log/daemon.log + chown root:adm /var/log/auth.log /var/log/daemon.log + chmod 640 /var/log/auth.log /var/log/daemon.log + msg_ok "$(translate "Authentication logs verified")" + + + if [[ ! -f /var/log/auth.log && -f /var/log/secure ]]; then + msg_warn "$(translate "Using /var/log/secure instead of /var/log/auth.log")" + sed -i 's|logpath = /var/log/auth.log|logpath = /var/log/secure|' /etc/fail2ban/jail.local + fi + + + msg_info "$(translate "Ensuring Fail2Ban runtime directory exists...")" + mkdir -p /var/run/fail2ban + chown root:root /var/run/fail2ban + chmod 755 /var/run/fail2ban + msg_ok "$(translate "Fail2Ban runtime directory verified")" + + + msg_info "$(translate "Removing old Fail2Ban database (if exists)...")" + rm -f /var/lib/fail2ban/fail2ban.sqlite3 + msg_ok "$(translate "Fail2Ban database reset")" + + + msg_info "$(translate "Reloading systemd and restarting Fail2Ban...")" + systemctl daemon-reload + systemctl enable fail2ban >/dev/null 2>&1 + systemctl restart fail2ban >/dev/null 2>&1 + msg_ok "$(translate "Fail2Ban service restarted")" + + + sleep 3 + + + msg_info "$(translate "Checking Fail2Ban service status...")" + if systemctl is-active --quiet fail2ban; then + msg_ok "$(translate "Fail2Ban is running correctly")" + else + msg_error "$(translate "Fail2Ban is NOT running! Checking logs...")" + journalctl -u fail2ban --no-pager -n 20 + + fi + + + msg_info "$(translate "Checking Fail2Ban socket...")" + if [ -S /var/run/fail2ban/fail2ban.sock ]; then + msg_ok "$(translate "Fail2Ban socket exists!")" + else + msg_warn "$(translate "Warning: Fail2Ban socket does not exist!")" + fi + + + msg_info "$(translate "Testing fail2ban-client...")" + if fail2ban-client ping >/dev/null 2>&1; then + msg_ok "$(translate "fail2ban-client successfully communicated with the server")" + else + msg_error "$(translate "fail2ban-client could not communicate with the server")" + + fi + + + msg_info "$(translate "Displaying Fail2Ban status...")" + fail2ban-client status >/dev/null 2>&1 + msg_ok "$(translate "Fail2Ban status displayed")" + + msg_success "$(translate "Fail2Ban installation and configuration completed successfully!")" + +} + + + + +# ========================================================== + + + + +install_lynis_() { + msg_info2 "$(translate "Installing Lynis security scan tool...")" + + # Install Lynis directly from Debian repositories + msg_info "$(translate "Installing Lynis packages...")" + ( + /usr/bin/env DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::='--force-confdef' install lynis 2>&1 | \ + while IFS= read -r line; do + if [[ $line == *"Installing"* ]] || [[ $line == *"Unpacking"* ]]; then + printf "\r%-$(($(tput cols)-1))s\r" " " # Clear current line + printf "\r%s" "$line" + fi + done + ) + + if [ $? -eq 0 ]; then + printf "\r%-$(($(tput cols)-1))s\r" " " # Clear final line + msg_ok "$(translate "Lynis installed successfully")" + else + printf "\r%-$(($(tput cols)-1))s\r" " " # Clear final line + msg_warn "$(translate "Failed to install Lynis")" + fi + + # Verify installation + if command -v lynis >/dev/null 2>&1; then + msg_success "$(translate "Lynis is ready to use")" + else + msg_warn "$(translate "Lynis installation could not be verified")" + fi +} + + +install_lynis() { + msg_info2 "$(translate "Installing latest Lynis security scan tool...")" + + if ! command -v git >/dev/null 2>&1; then + msg_info "$(translate "Installing Git as a prerequisite...")" + apt-get update -qq >/dev/null 2>&1 + apt-get install -y git >/dev/null 2>&1 + msg_ok "$(translate "Git installed")" + fi + + if [ -d /opt/lynis ]; then + rm -rf /opt/lynis >/dev/null 2>&1 + fi + + msg_info "$(translate "Cloning Lynis from GitHub...")" + if git clone --quiet https://github.com/CISOfy/lynis.git /opt/lynis >/dev/null 2>&1; then + # Create wrapper script instead of symbolic link + cat << 'EOF' > /usr/local/bin/lynis +#!/bin/bash +cd /opt/lynis && ./lynis "$@" +EOF + chmod +x /usr/local/bin/lynis + msg_ok "$(translate "Lynis installed successfully from GitHub")" + else + msg_warn "$(translate "Failed to clone Lynis from GitHub")" + return 1 + fi + + if /usr/local/bin/lynis show version >/dev/null 2>&1; then + msg_success "$(translate "Lynis is ready to use")" + else + msg_warn "$(translate "Lynis installation could not be verified")" + fi +} + + + + + + +# ========================================================== + + + + + +install_guest_agent() { + msg_info2 "$(translate "Detecting virtualization and installing guest agent...")" + NECESSARY_REBOOT=1 + + local virt_env="" + local guest_agent="" + + # Detect virtualization environment + if [ "$(dmidecode -s system-manufacturer | xargs)" == "QEMU" ] || [ "$(systemd-detect-virt | xargs)" == "kvm" ]; then + virt_env="QEMU/KVM" + guest_agent="qemu-guest-agent" + elif [ "$(systemd-detect-virt | xargs)" == "vmware" ]; then + virt_env="VMware" + guest_agent="open-vm-tools" + elif [ "$(systemd-detect-virt | xargs)" == "oracle" ]; then + virt_env="VirtualBox" + guest_agent="virtualbox-guest-utils" + else + msg_ok "$(translate "Guest agent detection completed")" + msg_success "$(translate "Guest agent installation process completed")" + return + fi + + # Install guest agent + if [ -n "$guest_agent" ]; then + msg_info "$(translate "Installing $guest_agent for $virt_env...")" + if /usr/bin/env DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::='--force-confdef' install $guest_agent > /dev/null 2>&1; then + msg_ok "$(translate "$guest_agent installed successfully")" + else + msg_error "$(translate "Failed to install $guest_agent")" + fi + fi + + msg_success "$(translate "Guest agent installation process completed")" +} + + + + +# ========================================================== + + + + + +configure_ksmtuned() { + msg_info2 "$(translate "Installing and configuring KSM (Kernel Samepage Merging) daemon...")" + NECESSARY_REBOOT=1 + + # Install ksm-control-daemon + msg_info "$(translate "Installing ksm-control-daemon...")" + if /usr/bin/env DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::='--force-confdef' install ksm-control-daemon > /dev/null 2>&1; then + msg_ok "$(translate "ksm-control-daemon installed successfully")" + fi + + # Determine RAM size and set KSM parameters + if [[ RAM_SIZE_GB -le 16 ]]; then + KSM_THRES_COEF=50 + KSM_SLEEP_MSEC=80 + msg_info "$(translate "RAM <= 16GB: Setting KSM to start at 50% full")" + elif [[ RAM_SIZE_GB -le 32 ]]; then + KSM_THRES_COEF=40 + KSM_SLEEP_MSEC=60 + msg_info "$(translate "RAM <= 32GB: Setting KSM to start at 60% full")" + elif [[ RAM_SIZE_GB -le 64 ]]; then + KSM_THRES_COEF=30 + KSM_SLEEP_MSEC=40 + msg_info "$(translate "RAM <= 64GB: Setting KSM to start at 70% full")" + elif [[ RAM_SIZE_GB -le 128 ]]; then + KSM_THRES_COEF=20 + KSM_SLEEP_MSEC=20 + msg_info "$(translate "RAM <= 128GB: Setting KSM to start at 80% full")" + else + KSM_THRES_COEF=10 + KSM_SLEEP_MSEC=10 + msg_info "$(translate "RAM > 128GB: Setting KSM to start at 90% full")" + fi + # Update ksmtuned configuration + if sed -i -e "s/\# KSM_THRES_COEF=.*/KSM_THRES_COEF=${KSM_THRES_COEF}/g" /etc/ksmtuned.conf && \ + sed -i -e "s/\# KSM_SLEEP_MSEC=.*/KSM_SLEEP_MSEC=${KSM_SLEEP_MSEC}/g" /etc/ksmtuned.conf; then + msg_ok "$(translate "ksmtuned configuration updated successfully")" + fi + + # Enable ksmtuned service + if systemctl enable ksmtuned > /dev/null 2>&1; then + msg_ok "$(translate "ksmtuned service enabled successfully")" + fi + + msg_success "$(translate "KSM configuration completed")" +} + + + + + +# ========================================================== + + + + + +enable_vfio_iommu_() { + msg_info2 "$(translate "Enabling IOMMU and configuring VFIO for PCI passthrough...")" + NECESSARY_REBOOT=1 + + # Detect if system uses ZFS + local uses_zfs=false + local cmdline_file="/etc/kernel/cmdline" + if [[ -f "$cmdline_file" && $(grep -q "root=ZFS=" "$cmdline_file") ]]; then + uses_zfs=true + fi + + # Enable IOMMU + local cpu_info=$(cat /proc/cpuinfo) + local grub_file="/etc/default/grub" + local iommu_param="" + local additional_params="pcie_acs_override=downstream,multifunction nofb nomodeset video=vesafb:off,efifb:off" + + if [[ "$cpu_info" == *"GenuineIntel"* ]]; then + msg_info "$(translate "Detected Intel CPU")" + iommu_param="intel_iommu=on" + elif [[ "$cpu_info" == *"AuthenticAMD"* ]]; then + msg_info "$(translate "Detected AMD CPU")" + iommu_param="amd_iommu=on" + else + msg_warning "$(translate "Unknown CPU type. IOMMU might not be properly enabled.")" + return 1 + fi + + if [[ "$uses_zfs" == true ]]; then + if grep -q "$iommu_param" "$cmdline_file"; then + if ! grep -q "iommu=pt" "$cmdline_file"; then + cp "$cmdline_file" "${cmdline_file}.bak" + sed -i "/^.*root=ZFS=/ s|$| iommu=pt|" "$cmdline_file" + msg_ok "$(translate "Added missing iommu=pt to ZFS configuration")" + else + msg_ok "$(translate "IOMMU and additional parameters already configured for ZFS")" + fi + else + cp "$cmdline_file" "${cmdline_file}.bak" + sed -i "/^.*root=ZFS=/ s|$| $iommu_param iommu=pt|" "$cmdline_file" + msg_ok "$(translate "IOMMU and additional parameters added for ZFS")" + fi + else + + if grep -q "$iommu_param" "$grub_file"; then + if ! grep -q "iommu=pt" "$grub_file"; then + cp "$grub_file" "${grub_file}.bak" + sed -i "/GRUB_CMDLINE_LINUX_DEFAULT=/ s|\"$| iommu=pt\"|" "$grub_file" + msg_ok "$(translate "Added missing iommu=pt to GRUB configuration")" + else + msg_ok "$(translate "IOMMU already enabled in GRUB configuration")" + fi + else + cp "$grub_file" "${grub_file}.bak" + sed -i "/GRUB_CMDLINE_LINUX_DEFAULT=/ s|\"$| $iommu_param iommu=pt\"|" "$grub_file" + msg_ok "$(translate "IOMMU enabled in GRUB configuration")" + fi + fi + + # Configure VFIO modules (avoid duplicates) + local modules_file="/etc/modules" + msg_info "$(translate "Checking VFIO modules...")" + local vfio_modules=("vfio" "vfio_iommu_type1" "vfio_pci" "vfio_virqfd") + + for module in "${vfio_modules[@]}"; do + grep -q "^$module" "$modules_file" || echo "$module" >> "$modules_file" + done + msg_ok "$(translate "VFIO modules configured.)")" + + # Blacklist conflicting drivers (avoid duplicates) + local blacklist_file="/etc/modprobe.d/blacklist.conf" + msg_info "$(translate "Checking conflicting drivers blacklist...")" + touch "$blacklist_file" + local blacklist_drivers=("nouveau" "lbm-nouveau" "amdgpu" "radeon" "nvidia" "nvidiafb") + + for driver in "${blacklist_drivers[@]}"; do + grep -q "^blacklist $driver" "$blacklist_file" || echo "blacklist $driver" >> "$blacklist_file" + done + + if ! grep -q "options nouveau modeset=0" "$blacklist_file"; then + echo "options nouveau modeset=0" >> "$blacklist_file" + fi + msg_ok "$(translate "Conflicting drivers blacklisted successfully.")" + + + # Propagate the settings + msg_info "$(translate "Updating initramfs, GRUB, and EFI boot, patience...")" + if update-initramfs -u -k all > /dev/null 2>&1 && \ + update-grub > /dev/null 2>&1 && \ + pve-efiboot-tool refresh > /dev/null 2>&1; then + msg_ok "$(translate "Initramfs, GRUB, and EFI boot updated successfully")" + else + msg_error "$(translate "Failed to update one or more components (initramfs, GRUB, or EFI boot)")" + fi + + msg_success "$(translate "IOMMU and VFIO setup completed")" +} + + + +enable_vfio_iommu() { + msg_info2 "$(translate "Enabling IOMMU and configuring VFIO for PCI passthrough...")" + NECESSARY_REBOOT=1 + + # Detect if system uses ZFS/systemd-boot (Proxmox) + local uses_zfs=false + local cmdline_file="/etc/kernel/cmdline" + if [[ -f "$cmdline_file" ]] && grep -qE 'root=ZFS=|root=ZFS/' "$cmdline_file"; then + uses_zfs=true + fi + + # Enable IOMMU + local cpu_info=$(cat /proc/cpuinfo) + local grub_file="/etc/default/grub" + local iommu_param="" + local additional_params="pcie_acs_override=downstream,multifunction nofb nomodeset video=vesafb:off,efifb:off" + + if [[ "$cpu_info" == *"GenuineIntel"* ]]; then + msg_info "$(translate "Detected Intel CPU")" + iommu_param="intel_iommu=on" + elif [[ "$cpu_info" == *"AuthenticAMD"* ]]; then + msg_info "$(translate "Detected AMD CPU")" + iommu_param="amd_iommu=on" + else + msg_warning "$(translate "Unknown CPU type. IOMMU might not be properly enabled.")" + return 1 + fi + + if [[ "$uses_zfs" == true ]]; then + # --- SYSTEMD-BOOT: /etc/kernel/cmdline --- + if grep -q "$iommu_param" "$cmdline_file"; then + msg_ok "$(translate "IOMMU already configured in /etc/kernel/cmdline")" + else + cp "$cmdline_file" "${cmdline_file}.bak" + # sed -i "s|\"$| $iommu_param iommu=pt|" "$cmdline_file" + sed -i "s|\s*$| $iommu_param iommu=pt|" "$cmdline_file" + msg_ok "$(translate "IOMMU parameters added to /etc/kernel/cmdline")" + fi + else + # --- GRUB --- + if grep -q "$iommu_param" "$grub_file"; then + msg_ok "$(translate "IOMMU already enabled in GRUB configuration")" + else + cp "$grub_file" "${grub_file}.bak" + sed -i "/GRUB_CMDLINE_LINUX_DEFAULT=/ s|\"$| $iommu_param iommu=pt\"|" "$grub_file" + msg_ok "$(translate "IOMMU enabled in GRUB configuration")" + fi + fi + + # Configure VFIO modules (avoid duplicates) + local modules_file="/etc/modules" + msg_info "$(translate "Checking VFIO modules...")" + local vfio_modules=("vfio" "vfio_iommu_type1" "vfio_pci" "vfio_virqfd") + for module in "${vfio_modules[@]}"; do + grep -q "^$module" "$modules_file" || echo "$module" >> "$modules_file" + done + msg_ok "$(translate "VFIO modules configured.")" + + # Blacklist conflicting drivers (avoid duplicates) + local blacklist_file="/etc/modprobe.d/blacklist.conf" + msg_info "$(translate "Checking conflicting drivers blacklist...")" + touch "$blacklist_file" + local blacklist_drivers=("nouveau" "lbm-nouveau" "amdgpu" "radeon" "nvidia" "nvidiafb") + for driver in "${blacklist_drivers[@]}"; do + grep -q "^blacklist $driver" "$blacklist_file" || echo "blacklist $driver" >> "$blacklist_file" + done + if ! grep -q "options nouveau modeset=0" "$blacklist_file"; then + echo "options nouveau modeset=0" >> "$blacklist_file" + fi + msg_ok "$(translate "Conflicting drivers blacklisted successfully.")" + + # Propagate the settings + msg_info "$(translate "Updating initramfs, GRUB, and EFI boot, patience...")" + update-initramfs -u -k all > /dev/null 2>&1 + if [[ "$uses_zfs" == true ]]; then + proxmox-boot-tool refresh > /dev/null 2>&1 + else + update-grub > /dev/null 2>&1 + fi + + msg_success "$(translate "IOMMU and VFIO setup completed")" +} + + + + + + +# ========================================================== + + + + + +customize_bashrc() { + msg_info2 "$(translate "Customizing bashrc for root user...")" + + local bashrc="/root/.bashrc" + local bash_profile="/root/.bash_profile" + + # Backup original .bashrc if it doesn't exist + if [ ! -f "${bashrc}.bak" ]; then + cp "$bashrc" "${bashrc}.bak" + fi + + # Function to add a line if it doesn't exist + add_line_if_not_exists() { + local line="$1" + local file="$2" + grep -qF -- "$line" "$file" || echo "$line" >> "$file" + } + + # Add custom configurations to .bashrc + add_line_if_not_exists 'export HISTTIMEFORMAT="%d/%m/%y %T "' "$bashrc" + add_line_if_not_exists 'export PS1='"'\u@\h:\W \$ '" "$bashrc" + add_line_if_not_exists "alias l='ls -CF'" "$bashrc" + add_line_if_not_exists "alias la='ls -A'" "$bashrc" + add_line_if_not_exists "alias ll='ls -alF'" "$bashrc" + add_line_if_not_exists "alias ls='ls --color=auto'" "$bashrc" + add_line_if_not_exists "alias grep='grep --color=auto'" "$bashrc" + add_line_if_not_exists "alias fgrep='fgrep --color=auto'" "$bashrc" + add_line_if_not_exists "alias egrep='egrep --color=auto'" "$bashrc" + add_line_if_not_exists "source /etc/profile.d/bash_completion.sh" "$bashrc" + add_line_if_not_exists 'export PS1="\[\e[31m\][\[\e[m\]\[\e[38;5;172m\]\u\[\e[m\]@\[\e[38;5;153m\]\h\[\e[m\] \[\e[38;5;214m\]\W\[\e[m\]\[\e[31m\]]\[\e[m\]\\$ "' "$bashrc" + + msg_ok "$(translate "Custom configurations added to .bashrc")" + + # Ensure .bashrc is sourced in .bash_profile + add_line_if_not_exists "source /root/.bashrc" "$bash_profile" + msg_ok "$(translate ".bashrc sourced in .bash_profile")" + + msg_success "$(translate "Bashrc customization completed")" +} + + + + +# ========================================================== + + + + +setup_motd() { + msg_info2 "$(translate "Configuring MOTD (Message of the Day) banner...")" + + local motd_file="/etc/motd" + local custom_message=" This system is optimised by: ProxMenux" + local changes_made=false + + msg_info "$(translate "Checking MOTD configuration...")" + + # Check if the custom message already exists + if grep -q "$custom_message" "$motd_file"; then + msg_ok "$(translate "Custom message added to MOTD")" + else + # Create a backup of the original MOTD file + if [ ! -f "${motd_file}.bak" ]; then + cp "$motd_file" "${motd_file}.bak" + msg_ok "$(translate "Backup of original MOTD created")" + fi + + # Add the custom message at the beginning of the file + echo -e "$custom_message\n\n$(cat $motd_file)" > "$motd_file" + changes_made=true + msg_ok "$(translate "Custom message added to MOTD")" + fi + + sed -i '/^$/N;/^\n$/D' "$motd_file" + + if $changes_made; then + msg_success "$(translate "MOTD configuration updated successfully")" + else + msg_success "$(translate "MOTD configuration updated successfully")" + fi +} + + + + + +# ========================================================== + + + + + +optimize_logrotate() { +msg_info2 "$(translate "Optimizing logrotate configuration...")" + + local logrotate_conf="/etc/logrotate.conf" + local backup_conf="${logrotate_conf}.bak" + + + if grep -q "# ProxMenux optimized configuration" "$logrotate_conf"; then + msg_ok "$(translate "Logrotate configuration already optimized.")" + else + cp "$logrotate_conf" "$backup_conf" + + msg_info "$(translate "Applying optimized logrotate configuration...")" + cat < "$logrotate_conf" +# ProxMenux optimized configuration +daily +su root adm +rotate 7 +create +compress +size=10M +delaycompress +copytruncate + +include /etc/logrotate.d +EOF + + systemctl restart logrotate > /dev/null 2>&1 + msg_ok "$(translate "Logrotate service restarted successfully")" + fi + msg_success "$(translate "Logrotate optimization completed")" +} + + + + + +# ========================================================== + + + + +remove_subscription_banner() { + + msg_info2 "$(translate "Removing Proxmox subscription nag banner...")" + + local JS_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js" + local GZ_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js.gz" + local APT_HOOK="/etc/apt/apt.conf.d/no-nag-script" + + + if [[ ! -f "$APT_HOOK" ]]; then + + cat <<'EOF' > "$APT_HOOK" +DPkg::Post-Invoke { "dpkg -V proxmox-widget-toolkit | grep -q '/proxmoxlib\.js$'; if [ $? -eq 1 ]; then { echo 'Removing subscription nag from UI...'; sed -i '/.*data\.status.*{/{s/\!//;s/active/NoMoreNagging/;s/Active/NoMoreNagging/}' /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js; rm -f /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js.gz; }; fi"; }; +EOF + msg_ok "$(translate "APT hook for nag removal created")" + else + msg_info "$(translate "APT hook for nag removal already exists")" + fi + + + if [[ -f "$JS_FILE" ]]; then + sed -i '/.*data\.status.*{/{s/\!//;s/active/NoMoreNagging/;s/Active/NoMoreNagging/}' "$JS_FILE" + + if [[ -f "$GZ_FILE" ]]; then + rm -f "$GZ_FILE" + msg_info "$(translate "Deleted proxmoxlib.js.gz to force browser refresh")" + fi + + touch "$JS_FILE" + msg_ok "$(translate "Patched proxmoxlib.js (banner should disappear after browser refresh)")" + else + msg_error "$(translate "proxmoxlib.js not found. Cannot patch subscription banner.")" + return 1 + fi + + + apt --reinstall install proxmox-widget-toolkit -y > /dev/null 2>&1 + + msg_success "$(translate "Subscription nag banner removed.")" +} + + + + + + +remove_subscription_banner_() { + msg_info2 "$(translate "Checking Proxmox subscription banner and nag status...")" + + local proxmox_js="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js" + local cron_file="/etc/cron.daily/xs-pve-nosub" + local apt_conf_file="/etc/apt/apt.conf.d/xs-pve-no-nag" + + # Check if all modifications are already applied + if grep -q "checked_command: function() {}" "$proxmox_js" && \ + [ -f "$cron_file" ] && \ + [ -f "$apt_conf_file" ] && \ + grep -q "NoMoreNagging" "$proxmox_js"; then + msg_ok "$(translate "No changes needed")" + msg_success "$(translate "Subscription banner and nag removal check completed")" + return 0 + fi + + + # Remove subscription banner + if [ -f "$proxmox_js" ]; then + if ! [ -f "$cron_file" ]; then + cat <<'EOF' > "$cron_file" +#!/bin/sh +# Remove subscription banner +sed -i "s/data.status !== 'Active'/false/g" /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js +sed -i "s/checked_command: function(orig_cmd) {/checked_command: function() {} || function(orig_cmd) {/g" /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js +EOF + chmod 755 "$cron_file" + msg_ok "$(translate "Cron job for banner removal created")" + else + msg_info "$(translate "Cron job for banner removal already exists")" + fi + bash "$cron_file" + msg_ok "$(translate "Banner removal script executed")" + else + msg_error "$(translate "proxmoxlib.js not found. Cannot remove banner.")" + fi + + # Remove nag using APT hook + if ! [ -f "$apt_conf_file" ]; then + echo "DPkg::Post-Invoke { \"dpkg -V proxmox-widget-toolkit | grep -q '/proxmoxlib\.js$'; if [ \$? -eq 1 ]; then { echo 'Removing subscription nag from UI...'; sed -i '/data.status/{s/\!//;s/Active/NoMoreNagging/}' /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js; }; fi\"; };" > "$apt_conf_file" + msg_ok "$(translate "APT configuration for nag removal created")" + else + msg_ok "$(translate "APT configuration for nag removal created")" + fi + + # Apply nag removal immediately and trigger APT hook + + if apt --reinstall install proxmox-widget-toolkit > /dev/null 2>&1; then + msg_ok "$(translate "proxmox-widget-toolkit reinstalled, triggering nag removal")" + else + msg_error "$(translate "Failed to reinstall proxmox-widget-toolkit")" + fi + msg_success "$(translate "Subscription banner and nag removal process completed")" +} + + + + + +# ========================================================== + + + + + +optimize_memory_settings() { + msg_info2 "$(translate "Optimizing memory settings...")" + NECESSARY_REBOOT=1 + + local sysctl_conf="/etc/sysctl.d/99-memory.conf" + + + if [ -f "$sysctl_conf" ] && grep -q "Memory Optimising" "$sysctl_conf"; then + msg_info "$(translate "Old memory configuration detected. Replacing with balanced optimization...")" + else + msg_info "$(translate "Applying balanced memory optimization settings...")" + fi + + cat < "$sysctl_conf" +# Balanced Memory Optimization +# Improve responsiveness without excessive memory reservation + +# Avoid unnecessary swapping +vm.swappiness = 10 + +# Lower dirty memory thresholds to free memory faster +vm.dirty_ratio = 15 +vm.dirty_background_ratio = 5 + +# Allow memory overcommit to reduce allocation issues +vm.overcommit_memory = 1 + +# Avoid excessive virtual memory areas (safe for most applications) +vm.max_map_count = 65530 +EOF + + if [ -f /proc/sys/vm/compaction_proactiveness ]; then + echo "vm.compaction_proactiveness = 20" >> "$sysctl_conf" + msg_ok "$(translate "Enabled memory compaction proactiveness")" + fi + + msg_ok "$(translate "Memory settings optimized successfully")" + msg_success "$(translate "Memory optimization completed.")" +} + + + + + +# ========================================================== + + + + + +optimize_vzdump() { + msg_info2 "$(translate "Optimizing vzdump backup speed...")" + + local vzdump_conf="/etc/vzdump.conf" + + # Configure bandwidth limit + msg_info "$(translate "Configuring bandwidth limit for vzdump...")" + if ! grep -q "^bwlimit: 0" "$vzdump_conf"; then + sed -i '/^#*bwlimit:/d' "$vzdump_conf" + echo "bwlimit: 0" >> "$vzdump_conf" + fi + msg_ok "$(translate "Bandwidth limit configured")" + + # Configure I/O priority + msg_info "$(translate "Configuring I/O priority for vzdump...")" + if ! grep -q "^ionice: 5" "$vzdump_conf"; then + sed -i '/^#*ionice:/d' "$vzdump_conf" + echo "ionice: 5" >> "$vzdump_conf" + fi + msg_ok "$(translate "I/O priority configured")" + + msg_success "$(translate "vzdump backup speed optimization completed")" +} + + + + + +# ========================================================== + + + + + +install_ovh_rtm() { + msg_info2 "$(translate "Detecting if this is an OVH server and installing OVH RTM if necessary...")" + + # Get the public IP and check if it belongs to OVH + msg_info "$(translate "Checking if the server belongs to OVH...")" + public_ip=$(curl -s ipinfo.io/ip) + is_ovh=$(whois -h v4.whois.cymru.com " -t $public_ip" | tail -n 1 | cut -d'|' -f3 | grep -i "ovh") + + if [ -n "$is_ovh" ]; then + msg_ok "$(translate "OVH server detected")" + + msg_info "$(translate "Installing OVH RTM (Real Time Monitoring)...")" + if wget -qO - https://last-public-ovh-infra-yak.snap.mirrors.ovh.net/yak/archives/apply.sh | OVH_PUPPET_MANIFEST=distribyak/catalog/master/puppet/manifests/common/rtmv2.pp bash > /dev/null 2>&1; then + msg_ok "$(translate "OVH RTM installed successfully")" + else + msg_error "$(translate "Failed to install OVH RTM")" + fi + fi + msg_ok "$(translate "Server belongs to OVH")" + msg_success "$(translate "OVH server detection and RTM installation process completed")" +} + + + + +# ========================================================== + + + +enable_ha() { + msg_info2 "$(translate "Enabling High Availability (HA) services...")" + NECESSARY_REBOOT=1 + + msg_info "$(translate "Enabling High Availability (HA) services...")" + # Enable all necessary services + systemctl enable -q --now pve-ha-lrm pve-ha-crm corosync &>/dev/null + + + msg_ok "$(translate "High Availability services have been enabled successfully")" + msg_success "$(translate "High Availability setup completed")" + + +} + + + + +# ========================================================== + + + + + + +configure_fastfetch() { + msg_info2 "$(translate "Installing and configuring Fastfetch...")" + register_tool "fastfetch" true + + # Define paths + local fastfetch_bin="/usr/local/bin/fastfetch" + local fastfetch_config_dir="$HOME/.config/fastfetch" + local logos_dir="/usr/local/share/fastfetch/logos" + local fastfetch_config="$fastfetch_config_dir/config.jsonc" + + # Ensure directories exist + mkdir -p "$fastfetch_config_dir" + mkdir -p "$logos_dir" + + + if command -v fastfetch &> /dev/null; then + apt-get remove --purge -y fastfetch > /dev/null 2>&1 + rm -f /usr/bin/fastfetch /usr/local/bin/fastfetch + fi + + + msg_info "$(translate "Downloading the latest Fastfetch release...")" + local fastfetch_deb_url=$(curl -s https://api.github.com/repos/fastfetch-cli/fastfetch/releases/latest | + jq -r '.assets[] | select(.name | test("fastfetch-linux-amd64.deb")) | .browser_download_url') + + if [[ -z "$fastfetch_deb_url" ]]; then + msg_error "$(translate "Failed to retrieve Fastfetch download URL.")" + return 1 + fi + msg_ok "$(translate "Fastfetch download URL retrieved successfully.")" + + + wget -qO /tmp/fastfetch.deb "$fastfetch_deb_url" + if dpkg -i /tmp/fastfetch.deb > /dev/null 2>&1; then + apt-get install -f -y > /dev/null 2>&1 + msg_ok "$(translate "Fastfetch installed successfully")" + else + msg_error "$(translate "Failed to install Fastfetch.")" + return 1 + fi + + + rm -f /tmp/fastfetch.deb + + + if ! command -v fastfetch &> /dev/null; then + msg_error "$(translate "Fastfetch is not installed correctly.")" + return 1 + fi + + + if [ ! -f "$fastfetch_config" ]; then + echo '{"$schema": "https://github.com/fastfetch-cli/fastfetch/raw/dev/doc/json_schema.json", "modules": []}' > "$fastfetch_config" + fi + + fastfetch --gen-config-force > /dev/null 2>&1 + + while true; do + # Define logo options + local logo_options=("ProxMenux" "Proxmox (default)" "Comunidad Helper-Scripts" "Home-Labs-Club" "Proxmology" "Custom") + local choice + + choice=$(whiptail --title "$(translate "Fastfetch Logo Selection")" --menu "$(translate "Choose a logo for Fastfetch:")" 20 78 6 \ + "1" "${logo_options[0]}" \ + "2" "${logo_options[1]}" \ + "3" "${logo_options[2]}" \ + "4" "${logo_options[3]}" \ + "5" "${logo_options[4]}" \ + "6" "${logo_options[5]}" \ + 3>&1 1>&2 2>&3) + + case $choice in + 1) + msg_info "$(translate "Downloading ProxMenux logo...")" + local proxmenux_logo_path="$logos_dir/ProxMenux.txt" + if wget -qO "$proxmenux_logo_path" "https://raw.githubusercontent.com/MacRimi/ProxMenux/main/images/logos_txt/logo.txt"; then + jq --arg path "$proxmenux_logo_path" '. + {logo: $path}' "$fastfetch_config" > "${fastfetch_config}.tmp" && mv "${fastfetch_config}.tmp" "$fastfetch_config" + msg_ok "$(translate "ProxMenux logo applied")" + else + msg_error "$(translate "Failed to download ProxMenux logo")" + fi + break + ;; + 2) + msg_info "$(translate "Using default Proxmox logo...")" + jq 'del(.logo)' "$fastfetch_config" > "${fastfetch_config}.tmp" && mv "${fastfetch_config}.tmp" "$fastfetch_config" + msg_ok "$(translate "Default Proxmox logo applied")" + break + ;; + 3) + msg_info "$(translate "Downloading Helper-Scripts logo...")" + local helper_scripts_logo_path="$logos_dir/Helper_Scripts.txt" + if wget -qO "$helper_scripts_logo_path" "https://raw.githubusercontent.com/MacRimi/ProxMenux/main/images/logos_txt/Helper_Scripts.txt"; then + jq --arg path "$helper_scripts_logo_path" '. + {logo: $path}' "$fastfetch_config" > "${fastfetch_config}.tmp" && mv "${fastfetch_config}.tmp" "$fastfetch_config" + msg_ok "$(translate "Helper-Scripts logo applied")" + else + msg_error "$(translate "Failed to download Helper-Scripts logo")" + fi + break + ;; + 4) + msg_info "$(translate "Downloading Home-Labs-Club logo...")" + local home_lab_club_logo_path="$logos_dir/home_labsclub.txt" + if wget -qO "$home_lab_club_logo_path" "https://raw.githubusercontent.com/MacRimi/ProxMenux/main/images/logos_txt/home_labsclub.txt"; then + jq --arg path "$home_lab_club_logo_path" '. + {logo: $path}' "$fastfetch_config" > "${fastfetch_config}.tmp" && mv "${fastfetch_config}.tmp" "$fastfetch_config" + msg_ok "$(translate "Home-Lab-Club logo applied")" + else + msg_error "$(translate "Failed to download Home-Lab-Club logo")" + fi + break + ;; + 5) + msg_info "$(translate "Downloading Proxmology logo...")" + local proxmology_logo_path="$logos_dir/proxmology.txt" + if wget -qO "$proxmology_logo_path" "https://raw.githubusercontent.com/MacRimi/ProxMenux/main/images/logos_txt/proxmology.txt"; then + jq --arg path "$proxmology_logo_path" '. + {logo: $path}' "$fastfetch_config" > "${fastfetch_config}.tmp" && mv "${fastfetch_config}.tmp" "$fastfetch_config" + msg_ok "$(translate "Proxmology logo applied")" + else + msg_error "$(translate "Failed to download Proxmology logo")" + fi + break + ;; + 6) + whiptail --title "$(translate "Custom Logo Instructions")" --msgbox "$(translate "To use a custom Fastfetch logo, place your ASCII logo file in:\n\n/usr/local/share/fastfetch/logos/\n\nThe file should not exceed 35 lines to fit properly in the terminal.\n\nPress OK to continue and select your logo.")" 15 70 + + local logo_files=($(ls "$logos_dir"/*.txt 2>/dev/null)) + + if [ ${#logo_files[@]} -eq 0 ]; then + whiptail --title "$(translate "No Custom Logos Found")" --msgbox "$(translate "No custom logos were found in /usr/local/share/fastfetch/logos/.\n\nPlease add a logo and try again.")" 10 60 + continue + fi + + local menu_items=() + local index=1 + for file in "${logo_files[@]}"; do + menu_items+=("$index" "$(basename "$file")") + index=$((index+1)) + done + + local selected_logo_index + selected_logo_index=$(whiptail --title "$(translate "Select a Custom Logo")" --menu "$(translate "Choose a custom logo:")" 20 70 10 "${menu_items[@]}" 3>&1 1>&2 2>&3) + + if [ -z "$selected_logo_index" ]; then + continue + fi + + local selected_logo="${logo_files[$((selected_logo_index-1))]}" + jq --arg path "$selected_logo" '. + {logo: $path}' "$fastfetch_config" > "${fastfetch_config}.tmp" && mv "${fastfetch_config}.tmp" "$fastfetch_config" + msg_ok "$(translate "Custom logo applied: $(basename "$selected_logo")")" + break + ;; + *) + msg_warn "$(translate "You must select a logo to continue.")" + ;; + esac + done + + # Modify Fastfetch modules to display custom title + msg_info "$(translate "Modifying Fastfetch configuration...")" + + jq '.modules |= map(select(. != "title"))' "$fastfetch_config" > "${fastfetch_config}.tmp" && mv "${fastfetch_config}.tmp" "$fastfetch_config" + + jq 'del(.modules[] | select(type == "object" and .type == "custom"))' "$fastfetch_config" > "${fastfetch_config}.tmp" && mv "${fastfetch_config}.tmp" "$fastfetch_config" + + jq '.modules |= [{"type": "custom", "format": "\u001b[1;38;5;166mSystem optimised by ProxMenux\u001b[0m"}] + .' "$fastfetch_config" > "${fastfetch_config}.tmp" && mv "${fastfetch_config}.tmp" "$fastfetch_config" + + msg_ok "$(translate "Fastfetch now displays: System optimised by: ProxMenux")" + + fastfetch --gen-config > /dev/null 2>&1 + msg_ok "$(translate "Fastfetch configuration updated")" + + sed -i '/fastfetch/d' ~/.bashrc ~/.profile /etc/profile + rm -f /etc/update-motd.d/99-fastfetch + + echo "clear && fastfetch" >> ~/.bashrc + msg_ok "$(translate "Fastfetch will start automatically in the console")" + + msg_success "$(translate "Fastfetch installation and configuration completed")" + +} + + + + + +# ========================================================== + + + + + + +add_repo_test() { + msg_info2 "$(translate "Enable Proxmox testing repository...")" + # Enable Proxmox testing repository + if [ ! -f /etc/apt/sources.list.d/pve-testing-repo.list ] || ! grep -q "pvetest" /etc/apt/sources.list.d/pve-testing-repo.list; then + msg_info "$(translate "Enabling Proxmox testing repository...")" + echo -e "deb http://download.proxmox.com/debian/pve ${OS_CODENAME} pvetest\\n" > /etc/apt/sources.list.d/pve-testing-repo.list + msg_ok "$(translate "Proxmox testing repository enabled")" + fi + msg_success "$(translate "Proxmox testing repository has been successfully enabled")" +} + + + + + + +# ========================================================== + + + + + +configure_figurine() { + msg_info2 "$(translate "Installing and configuring Figurine...")" + + # Variables + local version="1.3.0" + local file="figurine_linux_amd64_v${version}.tar.gz" + local url="https://github.com/arsham/figurine/releases/download/v${version}/${file}" + local temp_dir + temp_dir=$(mktemp -d) + local install_dir="/usr/local/bin" + local profile_script="/etc/profile.d/figurine.sh" + local bin_path="${install_dir}/figurine" + + msg_info "$(translate "Downloading Figurine v${version}...")" + if command -v figurine &> /dev/null; then + rm -f "$bin_path" > /dev/null 2>&1 + msg_ok "$(translate "Previous installation removed")" + + fi + + wget -qO "${temp_dir}/${file}" "$url" > /dev/null 2>&1 + msg_ok "$(translate "Download completed")" + + + msg_info "$(translate "Extracting package...")" + tar -xf "${temp_dir}/${file}" -C "${temp_dir}" > /dev/null 2>&1 + msg_ok "$(translate "Extraction successful")" + + + if [[ ! -f "${temp_dir}/deploy/figurine" ]]; then + msg_error "$(translate "Binary not found in extracted content.")" + return 1 + fi + + + msg_info "$(translate "Installing binary to ${install_dir}...")" + mv "${temp_dir}/deploy/figurine" "$bin_path" > /dev/null 2>&1 + chmod +x "$bin_path" > /dev/null 2>&1 + msg_ok "$(translate "Binary installed")" + + + msg_info "$(translate "Creating figurine welcome message at ${profile_script}...")" + cat << 'EOF' > "$profile_script" +/usr/local/bin/figurine -f "3d.flf" $(hostname) +EOF + chmod +x "$profile_script" > /dev/null 2>&1 + msg_ok "$(translate "Welcome message script created")" + + + local bashrc="$HOME/.bashrc" + if ! grep -q "alias aptup=" "$bashrc"; then + msg_info "$(translate "Adding useful aliases to ~/.bashrc...")" + cat << 'EOF' >> "$bashrc" + +# Aliases personalizados +alias aptup='apt update && apt dist-upgrade' + +alias lxcclean='bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/pve/clean-lxcs.sh)"' +alias lxcupdate='bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/pve/update-lxcs.sh)"' +alias kernelclean='bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/pve/kernel-clean.sh)"' +alias cpugov='bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/pve/scaling-governor.sh)"' + +alias updatecerts='pvecm updatecerts' +alias seqwrite='sync; fio --randrepeat=1 --ioengine=libaio --direct=1 --name=test --filename=test --bs=4M --size=32G --readwrite=write --ramp_time=4' +alias seqread='sync; fio --randrepeat=1 --ioengine=libaio --direct=1 --name=test --filename=test --bs=4M --size=32G --readwrite=read --ramp_time=4' +alias ranwrite='sync; fio --randrepeat=1 --ioengine=libaio --direct=1 --name=test --filename=test --bs=4k --size=4G --readwrite=randwrite --ramp_time=4' +alias ranread='sync; fio --randrepeat=1 --ioengine=libaio --direct=1 --name=test --filename=test --bs=4k --size=4G --readwrite=randread --ramp_time=4' +alias l='ls -CF' +alias la='ls -A' +alias ll='ls -alF' +alias ls='ls --color=auto' +alias grep='grep --color=auto' +alias fgrep='fgrep --color=auto' +alias egrep='egrep --color=auto' +EOF + msg_ok "$(translate "Aliases added to .bashrc")" + else + msg_info "$(translate "Aliases already present. Skipping addition.")" + msg_ok "$(translate "Aliases added to .bashrc")" + fi + + + msg_info "$(translate "Cleaning up temporary files...")" + rm -rf "$temp_dir" > /dev/null 2>&1 + msg_ok "$(translate "Cleanup completed")" + + msg_success "$(translate "Figurine installation and configuration completed successfully.")" +} + + + + +# ========================================================== + + + + + +update_pve_appliance_manager() { + msg_info "$(translate "Updating PVE application manager...")" + if pveam update > /dev/null 2>&1; then + msg_ok "$(translate "PVE application manager updated")" + else + msg_warn "$(translate "No updates or failed to fetch templates")" + fi +} + + + + +# ========================================================== + + + + + +# ========================================================== +# Auxiliary help functions +# ========================================================== + +cleanup_duplicate_repos() { + + local sources_file="/etc/apt/sources.list" + local temp_file=$(mktemp) + local cleaned_count=0 + declare -A seen_repos + + while IFS= read -r line || [[ -n "$line" ]]; do + + if [[ "$line" =~ ^[[:space:]]*# ]] || [[ -z "$line" ]]; then + echo "$line" >> "$temp_file" + continue + fi + + + if [[ "$line" =~ ^deb ]]; then + + read -r _ url dist components <<< "$line" + + local key="${url}_${dist}" + + if [[ -v "seen_repos[$key]" ]]; then + + echo "# $line" >> "$temp_file" + cleaned_count=$((cleaned_count + 1)) + else + + echo "$line" >> "$temp_file" + seen_repos[$key]="$components" + fi + else + + echo "$line" >> "$temp_file" + fi + done < "$sources_file" + + + mv "$temp_file" "$sources_file" + chmod 644 "$sources_file" + + + local pve_files=(/etc/apt/sources.list.d/*proxmox*.list /etc/apt/sources.list.d/*pve*.list) + local pve_content="deb http://download.proxmox.com/debian/pve ${OS_CODENAME} pve-no-subscription" + local pve_public_repo="/etc/apt/sources.list.d/pve-public-repo.list" + local pve_public_repo_exists=false + local pve_repo_count=0 + + + if [ -f "$pve_public_repo" ] && grep -q "^deb.*pve-no-subscription" "$pve_public_repo"; then + pve_public_repo_exists=true + pve_repo_count=1 + fi + + for file in "${pve_files[@]}"; do + if [ -f "$file" ] && grep -q "^deb.*pve-no-subscription" "$file"; then + if ! $pve_public_repo_exists && [[ "$file" == "$pve_public_repo" ]]; then + + sed -i 's/^# *deb/deb/' "$file" + pve_public_repo_exists=true + pve_repo_count=1 + elif [[ "$file" != "$pve_public_repo" ]]; then + + sed -i 's/^deb/# deb/' "$file" + cleaned_count=$((cleaned_count + 1)) + fi + fi + done + +apt update +} + + + + + + + + +lvm_repair_check() { + msg_info "$(translate "Checking and repairing old LVM PV headers (if needed)...")" + + pvs_output=$(LC_ALL=C pvs -v 2>&1 | grep "old PV header") + + if [ -z "$pvs_output" ]; then + msg_ok "$(translate "No PVs with old headers found.")" + return + fi + + declare -A vg_map + while read -r line; do + pv=$(echo "$line" | grep -o '/dev/[^ ]*') + vg=$(pvs -o vg_name --noheadings "$pv" | awk '{print $1}') + if [ -n "$vg" ]; then + vg_map["$vg"]=1 + fi + done <<< "$pvs_output" + + for vg in "${!vg_map[@]}"; do + msg_warn "$(translate "Old PV header(s) found in VG $vg. Updating metadata...")" + vgck --updatemetadata "$vg" + vgchange -ay "$vg" + if [ $? -ne 0 ]; then + msg_warn "$(translate "Metadata update failed for VG $vg. Review manually.")" + else + msg_ok "$(translate "Metadata updated successfully for VG $vg")" + fi + done + +} + + + + + + + + +# ========================================================== + + + + + + + + +# Main menu function +main_menu() { + local HEADER + if [[ "$LANGUAGE" == "es" ]]; then + HEADER="Seleccione las opciones a configurar:\n\n Descripción | Categoría" + else + HEADER="$(translate "Choose options to configure:")\n\n Description | Category" + fi + + declare -A category_order=( + ["Basic Settings"]=1 ["System"]=2 ["Hardware"]=3 ["Virtualization"]=4 + ["Network"]=5 ["Storage"]=6 ["Security"]=7 ["Customization"]=8 + ["Monitoring"]=9 ["Performance"]=10 ["Optional"]=11 + ) + + local options=( + "Basic Settings|Update and upgrade system|APTUPGRADE" + "Basic Settings|Synchronize time automatically|TIMESYNC" + "Basic Settings|Skip downloading additional languages|NOAPTLANG" + "Basic Settings|Install common system utilities|UTILS" + "System|Optimize journald|JOURNALD" + "System|Optimize logrotate|LOGROTATE" + "System|Increase various system limits|LIMITS" + "System|Ensure entropy pools are populated|ENTROPY" + "System|Optimize Memory|MEMORYFIXES" + "System|Enable fast reboots|KEXEC" + "System|Enable restart on kernel panic|KERNELPANIC" + "System|Install kernel headers|KERNELHEADERS" + "Optional|Apply AMD CPU fixes|AMDFIXES" + "Virtualization|Install relevant guest agent|GUESTAGENT" + "Virtualization|Enable VFIO IOMMU support|VFIO_IOMMU" + "Virtualization|KSM control daemon|KSMTUNED" + "Network|Force APT to use IPv4|APTIPV4" + "Network|Apply network optimizations|NET" + "Network|Install Open vSwitch|OPENVSWITCH" + "Network|Enable TCP BBR/Fast Open control|TCPFASTOPEN" + "Storage|Optimize ZFS ARC size|ZFSARC" + "Storage|Install ZFS auto-snapshot|ZFSAUTOSNAPSHOT" + "Storage|Increase vzdump backup speed|VZDUMP" + "Security|Disable portmapper/rpcbind|DISABLERPC" + "Security|Protect web interface with fail2ban|FAIL2BAN" + "Security|Install Lynis security tool|LYNIS" + "Customization|Customize bashrc|BASHRC" + "Customization|Set up custom MOTD banner|MOTD" + "Customization|Remove subscription banner|NOSUBBANNER" + "Monitoring|Install OVH Real Time Monitoring|OVHRTM" + "Performance|Use pigz for faster gzip compression|PIGZ" + "Optional|Install and configure Fastfetch|FASTFETCH" + "Optional|Update Proxmox VE Appliance Manager|PVEAM" + "Optional|Add latest Ceph support|CEPH" + "Optional|Add Proxmox testing repository|REPOTEST" + "Optional|Enable High Availability services|ENABLE_HA" + "Optional|Install Figurine|FIGURINE" + ) + + IFS=$'\n' sorted_options=($(for option in "${options[@]}"; do + IFS='|' read -r category description function_name <<< "$option" + printf "%d|%s|%s|%s\n" "${category_order[$category]:-999}" "$category" "$description" "$function_name" + done | sort -n | cut -d'|' -f2-)) + unset IFS + + local max_desc_length=0 + local temp_descriptions=() + + for option in "${sorted_options[@]}"; do + IFS='|' read -r category description function_name <<< "$option" + local desc_translated="$(translate "$description")" + temp_descriptions+=("$desc_translated") + + local desc_length=${#desc_translated} + if [ $desc_length -gt $max_desc_length ]; then + max_desc_length=$desc_length + fi + done + + if [ $max_desc_length -gt 50 ]; then + max_desc_length=50 + fi + + local checklist_items=() + local i=1 + local desc_index=0 + local previous_category="" + + for option in "${sorted_options[@]}"; do + IFS='|' read -r category description function_name <<< "$option" + + + if [[ "$category" != "$previous_category" && "$category" == "Optional" && -n "$previous_category" ]]; then + checklist_items+=("" "==============================================================" "") + fi + + local desc_translated="${temp_descriptions[$desc_index]}" + desc_index=$((desc_index + 1)) + + + if [ ${#desc_translated} -gt $max_desc_length ]; then + desc_translated="${desc_translated:0:$((max_desc_length-3))}..." + fi + + + local spaces_needed=$((max_desc_length - ${#desc_translated})) + local padding="" + for ((j=0; j&1 + selected_indices=$(dialog --clear \ + --backtitle "ProxMenux" \ + --title "$(translate "Post-Installation Options")" \ + --checklist "$HEADER" 22 80 15 \ + "${checklist_items[@]}" \ + 2>&1 1>&3) + + local dialog_exit=$? + exec 3>&- + + if [[ $dialog_exit -ne 0 || -z "$selected_indices" ]]; then + exit 0 + fi + + + + +declare -A selected_functions +read -ra indices_array <<< "$selected_indices" + +for index in "${indices_array[@]}"; do + if [[ -z "$index" ]] || ! [[ "$index" =~ ^[0-9]+$ ]]; then + continue + fi + + + local item_index=$(( (index - 1) * 3 + 1 )) + if [[ $item_index -lt ${#checklist_items[@]} ]]; then + local selected_line="${checklist_items[$item_index]}" + if [[ "$selected_line" =~ ^.*(\-\-\-|===+).*$ ]]; then + return 1 + fi + fi + + + option=${sorted_options[$((index - 1))]} + IFS='|' read -r _ description function_name <<< "$option" + selected_functions[$function_name]=1 + [[ "$function_name" == "FASTFETCH" ]] && selected_functions[MOTD]=0 +done + + + + + + clear + show_proxmenux_logo + msg_title "$SCRIPT_TITLE" + + for option in "${sorted_options[@]}"; do + IFS='|' read -r _ description function_name <<< "$option" + if [[ ${selected_functions[$function_name]} -eq 1 ]]; then + case $function_name in + APTUPGRADE) apt_upgrade ;; + TIMESYNC) configure_time_sync ;; + NOAPTLANG) skip_apt_languages ;; + UTILS) install_system_utils ;; + JOURNALD) optimize_journald ;; + LOGROTATE) optimize_logrotate ;; + LIMITS) increase_system_limits ;; + ENTROPY) configure_entropy ;; + MEMORYFIXES) optimize_memory_settings ;; + KEXEC) enable_kexec ;; + KERNELPANIC) configure_kernel_panic ;; + KERNELHEADERS) install_kernel_headers ;; + AMDFIXES) apply_amd_fixes ;; + GUESTAGENT) install_guest_agent ;; + VFIO_IOMMU) enable_vfio_iommu ;; + KSMTUNED) configure_ksmtuned ;; + APTIPV4) force_apt_ipv4 ;; + NET) apply_network_optimizations ;; + OPENVSWITCH) install_openvswitch ;; + TCPFASTOPEN) enable_tcp_fast_open ;; + ZFSARC) optimize_zfs_arc ;; + ZFSAUTOSNAPSHOT) install_zfs_auto_snapshot ;; + VZDUMP) optimize_vzdump ;; + DISABLERPC) disable_rpc ;; + FAIL2BAN) install_fail2ban ;; + LYNIS) install_lynis ;; + BASHRC) customize_bashrc ;; + MOTD) setup_motd ;; + NOSUBBANNER) remove_subscription_banner ;; + OVHRTM) install_ovh_rtm ;; + PIGZ) configure_pigz ;; + FASTFETCH) configure_fastfetch ;; + CEPH) install_ceph ;; + REPOTEST) add_repo_test ;; + ENABLE_HA) enable_ha ;; + FIGURINE) configure_figurine ;; + PVEAM) update_pve_appliance_manager ;; + *) echo "Option $function_name not implemented yet" ;; + esac + fi + done + + if [[ "$NECESSARY_REBOOT" -eq 1 ]]; then + whiptail --title "Reboot Required" \ + --yesno "$(translate "Some changes require a reboot to take effect. Do you want to restart now?")" 10 60 + if [[ $? -eq 0 ]]; then + msg_info "$(translate "Removing no longer required packages and purging old cached updates...")" + apt-get -y autoremove >/dev/null 2>&1 + apt-get -y autoclean >/dev/null 2>&1 + msg_ok "$(translate "Cleanup finished")" + msg_success "$(translate "Press Enter to continue...")" + read -r + msg_warn "$(translate "Rebooting the system...")" + reboot + else + msg_info "$(translate "Removing no longer required packages and purging old cached updates...")" + apt-get -y autoremove >/dev/null 2>&1 + apt-get -y autoclean >/dev/null 2>&1 + msg_ok "$(translate "Cleanup finished")" + msg_info2 "$(translate "You can reboot later manually.")" + msg_success "$(translate "Press Enter to continue...")" + read -r + exit 0 + fi + fi + + msg_success "$(translate "All changes applied. No reboot required.")" + msg_success "$(translate "Press Enter to return to menu...")" + read -r + clear +} + + + + +main_menu + diff --git a/scripts/post_install/uninstall-tools.sh b/scripts/post_install/uninstall-tools.sh new file mode 100644 index 0000000..17a8a58 --- /dev/null +++ b/scripts/post_install/uninstall-tools.sh @@ -0,0 +1,606 @@ +#!/bin/bash +# ========================================================== +# ProxMenux - Complete Uninstall Optimizations Script +# ========================================================== +# Author : MacRimi +# Copyright : (c) 2024 MacRimi +# License : MIT (https://raw.githubusercontent.com/MacRimi/ProxMenux/main/LICENSE) +# Version : 1.0 +# Last Updated: 06/07/2025 +# ========================================================== +# Description: +# This script provides a complete uninstallation and rollback system +# for all post-installation optimizations applied by ProxMenux. +# +# It allows administrators to safely revert any changes made during the +# optimization process, restoring the system to its original state. +# +# This ensures full control over system configurations and gives users +# the confidence to apply, test, and undo ProxMenux enhancements as needed. +# ========================================================== + + +REPO_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main" +RETURN_SCRIPT="$REPO_URL/scripts/menus/menu_post_install.sh" +BASE_DIR="/usr/local/share/proxmenux" +UTILS_FILE="$BASE_DIR/utils.sh" +TOOLS_JSON="$BASE_DIR/installed_tools.json" +VENV_PATH="/opt/googletrans-env" + +if [[ -f "$UTILS_FILE" ]]; then + source "$UTILS_FILE" +fi + +load_language +initialize_cache + +# Tool registration system +ensure_tools_json() { + [ -f "$TOOLS_JSON" ] || echo "{}" > "$TOOLS_JSON" +} + +register_tool() { + local tool="$1" + local state="$2" + ensure_tools_json + jq --arg t "$tool" --argjson v "$state" '.[$t]=$v' "$TOOLS_JSON" > "$TOOLS_JSON.tmp" && mv "$TOOLS_JSON.tmp" "$TOOLS_JSON" +} + +################################################################ + +uninstall_fastfetch() { + if ! command -v fastfetch &>/dev/null; then + msg_warn "$(translate "Fastfetch is not installed.")" + return 0 + fi + + msg_info2 "$(translate "Uninstalling Fastfetch...")" + rm -f /usr/local/bin/fastfetch /usr/bin/fastfetch + rm -rf "$HOME/.config/fastfetch" + rm -rf /usr/local/share/fastfetch + sed -i '/fastfetch/d' "$HOME/.bashrc" "$HOME/.profile" 2>/dev/null + rm -f /etc/profile.d/fastfetch.sh /etc/update-motd.d/99-fastfetch + dpkg -r fastfetch &>/dev/null + + msg_ok "$(translate "Fastfetch removed from system")" + register_tool "fastfetch" false +} + +################################################################ + +uninstall_figurine() { + if ! command -v figurine &>/dev/null; then + msg_warn "$(translate "Figurine is not installed.")" + return 0 + fi + + msg_info2 "$(translate "Uninstalling Figurine...")" + rm -f /usr/local/bin/figurine + rm -f /etc/profile.d/figurine.sh + sed -i '/figurine/d' "$HOME/.bashrc" "$HOME/.profile" 2>/dev/null + + msg_ok "$(translate "Figurine removed from system")" + register_tool "figurine" false +} + +################################################################ + +uninstall_kexec() { + if ! dpkg -s kexec-tools >/dev/null 2>&1 && [ ! -f /etc/systemd/system/kexec-pve.service ]; then + msg_warn "$(translate "kexec-tools is not installed or already removed.")" + return 0 + fi + + msg_info2 "$(translate "Uninstalling kexec-tools and removing custom service...")" + systemctl disable --now kexec-pve.service &>/dev/null + rm -f /etc/systemd/system/kexec-pve.service + sed -i "/alias reboot-quick='systemctl kexec'/d" /root/.bash_profile + apt-get purge -y kexec-tools >/dev/null 2>&1 + + msg_ok "$(translate "kexec-tools and related settings removed")" + register_tool "kexec" false +} + +################################################################ + +uninstall_apt_upgrade() { + msg_info "$(translate "Restoring enterprise repositories...")" + + # Re-enable enterprise repos + if [ -f /etc/apt/sources.list.d/pve-enterprise.list ]; then + sed -i "s/^#deb/deb/g" /etc/apt/sources.list.d/pve-enterprise.list + fi + + if [ -f /etc/apt/sources.list.d/ceph.list ]; then + sed -i "s/^#deb/deb/g" /etc/apt/sources.list.d/ceph.list + fi + + # Remove public repo + rm -f /etc/apt/sources.list.d/pve-public-repo.list + + # Remove firmware warning config + rm -f /etc/apt/apt.conf.d/no-bookworm-firmware.conf + + apt-get update > /dev/null 2>&1 + + msg_ok "$(translate "Enterprise repositories restored")" + register_tool "apt_upgrade" false +} + +################################################################ + +uninstall_subscription_banner() { + msg_info "$(translate "Restoring subscription banner...")" + + # Remove APT hook + rm -f /etc/apt/apt.conf.d/no-nag-script + + # Reinstall proxmox-widget-toolkit to restore original + apt --reinstall install proxmox-widget-toolkit -y >/dev/null 2>&1 + + msg_ok "$(translate "Subscription banner restored")" + register_tool "subscription_banner" false +} + +################################################################ + +uninstall_time_sync() { + msg_info "$(translate "Resetting time synchronization...")" + + # Reset to UTC (safe default) + timedatectl set-timezone UTC >/dev/null 2>&1 + + msg_ok "$(translate "Time synchronization reset to UTC")" + register_tool "time_sync" false +} + +################################################################ + +uninstall_apt_languages() { + msg_info "$(translate "Restoring APT language downloads...")" + + # Remove the configuration that disables translations + rm -f /etc/apt/apt.conf.d/99-disable-translations + + msg_ok "$(translate "APT language downloads restored")" + register_tool "apt_languages" false +} + +################################################################ + +uninstall_journald() { + msg_info "$(translate "Restoring default journald configuration...")" + + # Restore default journald configuration + cat > /etc/systemd/journald.conf << 'EOF' +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# Entries in this file show the compile time defaults. +# You can change settings by editing this file. +# Defaults can be restored by simply deleting this file. +# +# See journald.conf(5) for details. + +[Journal] +#Storage=auto +#Compress=yes +#Seal=yes +#SplitMode=uid +#SyncIntervalSec=5m +#RateLimitInterval=30s +#RateLimitBurst=1000 +#SystemMaxUse= +#SystemKeepFree= +#SystemMaxFileSize= +#RuntimeMaxUse= +#RuntimeKeepFree= +#RuntimeMaxFileSize= +#MaxRetentionSec= +#MaxFileSec=1month +#ForwardToSyslog=yes +#ForwardToKMsg=no +#ForwardToConsole=no +#ForwardToWall=yes +#TTYPath=/dev/console +#MaxLevelStore=debug +#MaxLevelSyslog=debug +#MaxLevelKMsg=notice +#MaxLevelConsole=info +#MaxLevelWall=emerg +EOF + + systemctl restart systemd-journald.service >/dev/null 2>&1 + + msg_ok "$(translate "Default journald configuration restored")" + register_tool "journald" false +} + +################################################################ + +uninstall_logrotate() { + msg_info "$(translate "Restoring original logrotate configuration...")" + + # Restore from backup if it exists + if [ -f /etc/logrotate.conf.bak ]; then + mv /etc/logrotate.conf.bak /etc/logrotate.conf + systemctl restart logrotate >/dev/null 2>&1 + msg_ok "$(translate "Original logrotate configuration restored")" + else + msg_warn "$(translate "No backup found, logrotate configuration not changed")" + fi + + register_tool "logrotate" false +} + +################################################################ + +uninstall_system_limits() { + msg_info "$(translate "Removing system limits optimizations...")" + + # Remove ProxMenux sysctl configurations + rm -f /etc/sysctl.d/99-maxwatches.conf + rm -f /etc/sysctl.d/99-maxkeys.conf + rm -f /etc/sysctl.d/99-swap.conf + rm -f /etc/sysctl.d/99-fs.conf + + # Remove ProxMenux limits configuration + rm -f /etc/security/limits.d/99-limits.conf + + # Remove systemd limits (restore defaults) + for file in /etc/systemd/system.conf /etc/systemd/user.conf; do + if [ -f "$file" ]; then + sed -i '/^DefaultLimitNOFILE=256000/d' "$file" + fi + done + + # Remove PAM limits + for file in /etc/pam.d/common-session /etc/pam.d/runuser-l; do + if [ -f "$file" ]; then + sed -i '/^session required pam_limits.so/d' "$file" + fi + done + + # Remove ulimit from profile + if [ -f /root/.profile ]; then + sed -i '/ulimit -n 256000/d' /root/.profile + fi + + # Reload sysctl + sysctl --system >/dev/null 2>&1 + + msg_ok "$(translate "System limits optimizations removed")" + register_tool "system_limits" false +} + +################################################################ + +uninstall_entropy() { + msg_info "$(translate "Removing entropy generation optimization...")" + + # Stop and disable haveged + systemctl stop haveged >/dev/null 2>&1 + systemctl disable haveged >/dev/null 2>&1 + + # Remove haveged package + apt-get purge -y haveged >/dev/null 2>&1 + + # Remove configuration + rm -f /etc/default/haveged + + msg_ok "$(translate "Entropy generation optimization removed")" + register_tool "entropy" false +} + +################################################################ + +uninstall_memory_settings() { + msg_info "$(translate "Removing memory optimizations...")" + + # Remove ProxMenux memory configuration + rm -f /etc/sysctl.d/99-memory.conf + + # Reload sysctl + sysctl --system >/dev/null 2>&1 + + msg_ok "$(translate "Memory optimizations removed")" + register_tool "memory_settings" false +} + +################################################################ + +uninstall_kernel_panic() { + msg_info "$(translate "Removing kernel panic configuration...")" + + # Remove ProxMenux kernel panic configuration + rm -f /etc/sysctl.d/99-kernelpanic.conf + + # Reload sysctl + sysctl --system >/dev/null 2>&1 + + msg_ok "$(translate "Kernel panic configuration removed")" + register_tool "kernel_panic" false +} + +################################################################ + +uninstall_apt_ipv4() { + msg_info "$(translate "Removing APT IPv4 configuration...")" + + # Remove IPv4 force configuration + rm -f /etc/apt/apt.conf.d/99-force-ipv4 + + msg_ok "$(translate "APT IPv4 configuration removed")" + register_tool "apt_ipv4" false +} + +################################################################ + +uninstall_network_optimization() { + msg_info "$(translate "Removing network optimizations...")" + + # Remove ProxMenux network configuration + rm -f /etc/sysctl.d/99-network.conf + + # Remove interfaces.d source line if we added it + local interfaces_file="/etc/network/interfaces" + if [ -f "$interfaces_file" ]; then + # Only remove if it's the last line and looks like our addition + if tail -1 "$interfaces_file" | grep -q "^source /etc/network/interfaces.d/\*$"; then + sed -i '$d' "$interfaces_file" + fi + fi + + # Reload sysctl + sysctl --system >/dev/null 2>&1 + + msg_ok "$(translate "Network optimizations removed")" + register_tool "network_optimization" false +} + +################################################################ + +uninstall_disable_rpc() { + msg_info "$(translate "Re-enabling RPC services...")" + + # Re-enable and start rpcbind + systemctl enable rpcbind >/dev/null 2>&1 + systemctl start rpcbind >/dev/null 2>&1 + + msg_ok "$(translate "RPC services re-enabled")" + register_tool "disable_rpc" false +} + +################################################################ + +uninstall_bashrc_custom() { + msg_info "$(translate "Restoring original bashrc...")" + + # Restore original bashrc from backup + if [ -f /root/.bashrc.bak ]; then + mv /root/.bashrc.bak /root/.bashrc + msg_ok "$(translate "Original bashrc restored")" + else + # Remove ProxMenux customizations manually + if [ -f /root/.bashrc ]; then + # Remove our customization block + sed -i '/# ProxMenux customizations/,/source \/etc\/profile\.d\/bash_completion\.sh/d' /root/.bashrc + fi + msg_ok "$(translate "ProxMenux customizations removed from bashrc")" + fi + + # Remove bash_profile source line if we added it + if [ -f /root/.bash_profile ]; then + sed -i '/source \/root\/\.bashrc/d' /root/.bash_profile + fi + + register_tool "bashrc_custom" false +} + +################################################################ + +uninstall_log2ram() { + if [[ ! -f /etc/log2ram.conf ]] && ! systemctl list-units --all | grep -q log2ram; then + msg_warn "$(translate "log2ram is not installed.")" + return 0 + fi + + msg_info "$(translate "Uninstalling log2ram...")" + + # Stop and disable services and timers + systemctl stop log2ram >/dev/null 2>&1 + systemctl disable log2ram >/dev/null 2>&1 + systemctl stop log2ram-daily.timer >/dev/null 2>&1 + systemctl disable log2ram-daily.timer >/dev/null 2>&1 + + # Remove cron jobs + rm -f /etc/cron.d/log2ram + rm -f /etc/cron.d/log2ram-auto-sync + + # Remove config and binaries + rm -f /usr/local/bin/log2ram-check.sh + rm -f /usr/sbin/log2ram + rm -f /etc/log2ram.conf* + rm -f /etc/systemd/system/log2ram.service + rm -f /etc/systemd/system/log2ram-daily.timer + rm -f /etc/systemd/system/log2ram-daily.service + + # Clean up log2ram mount if active + if [ -d /var/log.hdd ]; then + if [ -d /var/log ] && mountpoint -q /var/log; then + rsync -a /var/log/ /var/log.hdd/ >/dev/null 2>&1 + umount /var/log >/dev/null 2>&1 + fi + rm -rf /var/log.hdd + fi + + systemctl daemon-reexec >/dev/null 2>&1 + systemctl daemon-reload >/dev/null 2>&1 + + msg_ok "$(translate "log2ram completely removed")" + register_tool "log2ram" false +} + +################################################################ + +migrate_installed_tools() { + if [[ -f "$TOOLS_JSON" ]]; then + return + fi + + clear + show_proxmenux_logo + msg_info "$(translate 'Detecting previous optimizations...')" + + echo "{}" > "$TOOLS_JSON" + local updated=false + + + + # APT configurations + if [[ -f /etc/apt/apt.conf.d/99-force-ipv4 ]]; then + jq '. + {"apt_ipv4": true}' "$TOOLS_JSON" > "$TOOLS_JSON.tmp" && mv "$TOOLS_JSON.tmp" "$TOOLS_JSON" + updated=true + fi + + if [[ -f /etc/apt/apt.conf.d/99-disable-translations ]]; then + jq '. + {"apt_languages": true}' "$TOOLS_JSON" > "$TOOLS_JSON.tmp" && mv "$TOOLS_JSON.tmp" "$TOOLS_JSON" + updated=true + fi + + # System configurations + if [[ -f /etc/sysctl.d/99-memory.conf ]]; then + jq '. + {"memory_settings": true}' "$TOOLS_JSON" > "$TOOLS_JSON.tmp" && mv "$TOOLS_JSON.tmp" "$TOOLS_JSON" + updated=true + fi + + if [[ -f /etc/sysctl.d/99-network.conf ]]; then + jq '. + {"network_optimization": true}' "$TOOLS_JSON" > "$TOOLS_JSON.tmp" && mv "$TOOLS_JSON.tmp" "$TOOLS_JSON" + updated=true + fi + + if [[ -f /etc/sysctl.d/99-kernelpanic.conf ]]; then + jq '. + {"kernel_panic": true}' "$TOOLS_JSON" > "$TOOLS_JSON.tmp" && mv "$TOOLS_JSON.tmp" "$TOOLS_JSON" + updated=true + fi + + if [[ -f /etc/security/limits.d/99-limits.conf ]]; then + jq '. + {"system_limits": true}' "$TOOLS_JSON" > "$TOOLS_JSON.tmp" && mv "$TOOLS_JSON.tmp" "$TOOLS_JSON" + updated=true + fi + + # Services + if systemctl is-active --quiet log2ram 2>/dev/null; then + jq '. + {"log2ram": true}' "$TOOLS_JSON" > "$TOOLS_JSON.tmp" && mv "$TOOLS_JSON.tmp" "$TOOLS_JSON" + updated=true + fi + + if dpkg -l | grep -q haveged; then + jq '. + {"entropy": true}' "$TOOLS_JSON" > "$TOOLS_JSON.tmp" && mv "$TOOLS_JSON.tmp" "$TOOLS_JSON" + updated=true + fi + + # Bashrc customization + if grep -q "# ProxMenux customizations" /root/.bashrc 2>/dev/null; then + jq '. + {"bashrc_custom": true}' "$TOOLS_JSON" > "$TOOLS_JSON.tmp" && mv "$TOOLS_JSON.tmp" "$TOOLS_JSON" + updated=true + fi + + # Subscription banner + if [[ -f /etc/apt/apt.conf.d/no-nag-script ]]; then + jq '. + {"subscription_banner": true}' "$TOOLS_JSON" > "$TOOLS_JSON.tmp" && mv "$TOOLS_JSON.tmp" "$TOOLS_JSON" + updated=true + fi + + if [[ "$updated" == true ]]; then + sleep 2 + msg_ok "$(translate 'Optimizations detected and ready to revert.')" + sleep 1 + fi +} + +################################################################ + +show_uninstall_menu() { + ensure_tools_json + migrate_installed_tools + + mapfile -t tools_installed < <(jq -r 'to_entries | map(select(.value==true)) | .[].key' "$TOOLS_JSON") + + if [[ ${#tools_installed[@]} -eq 0 ]]; then + dialog --backtitle "ProxMenux" --title "ProxMenux" \ + --msgbox "\n\n$(translate "No optimizations detected to uninstall.")" 10 60 + return 0 + fi + + local menu_options=() + for tool in "${tools_installed[@]}"; do + case "$tool" in + lvm_repair) desc="LVM PV Headers Repair";; + repo_cleanup) desc="Repository Cleanup";; + apt_upgrade) desc="APT Upgrade & Repository Config";; + subscription_banner) desc="Subscription Banner Removal";; + time_sync) desc="Time Synchronization";; + apt_languages) desc="APT Language Skip";; + journald) desc="Journald Optimization";; + logrotate) desc="Logrotate Optimization";; + system_limits) desc="System Limits Increase";; + entropy) desc="Entropy Generation (haveged)";; + memory_settings) desc="Memory Settings Optimization";; + kernel_panic) desc="Kernel Panic Configuration";; + apt_ipv4) desc="APT IPv4 Force";; + network_optimization) desc="Network Optimizations";; + disable_rpc) desc="RPC/rpcbind Disable";; + bashrc_custom) desc="Bashrc Customization";; + log2ram) desc="Log2ram (SSD Protection)";; + *) desc="$tool";; + esac + menu_options+=("$tool" "$desc" "off") + done + + selected_tools=$(dialog --backtitle "ProxMenux" \ + --title "$(translate "Uninstall Optimizations")" \ + --checklist "$(translate "Select optimizations to uninstall:")" 20 70 12 \ + "${menu_options[@]}" 3>&1 1>&2 2>&3) + + local dialog_result=$? + if [[ $dialog_result -ne 0 || -z "$selected_tools" ]]; then + return 0 + fi + + # Show confirmation + if ! dialog --backtitle "ProxMenux" \ + --title "$(translate "Confirm Uninstallation")" \ + --yesno "\n\n$(translate "Are you sure you want to uninstall the selected optimizations.")" 10 60; then + return 0 + fi + + # Execute uninstallations + for tool in $selected_tools; do + tool=$(echo "$tool" | tr -d '"') + if declare -f "uninstall_$tool" > /dev/null 2>&1; then + clear + show_proxmenux_logo + "uninstall_$tool" + else + msg_warn "$(translate "No uninstaller found for:") $tool" + fi + done + + msg_success "$(translate "Selected optimizations have been uninstalled.")" + msg_warn "$(translate "A system reboot is recommended to ensure all changes take effect.")" + + if dialog --backtitle "ProxMenux" \ + --title "$(translate "Reboot Recommended")" \ + --yesno "$(translate "Do you want to reboot now?")" 8 50; then + reboot + fi +} + +################################################################ + +show_uninstall_menu diff --git a/scripts/uninstall-tools.sh b/scripts/uninstall-tools.sh index 0f4bf98..17a8a58 100644 --- a/scripts/uninstall-tools.sh +++ b/scripts/uninstall-tools.sh @@ -1,19 +1,25 @@ #!/bin/bash - # ========================================================== -# ProxMenux - Uninstall Tools Menu for Proxmox +# ProxMenux - Complete Uninstall Optimizations Script # ========================================================== # Author : MacRimi # Copyright : (c) 2024 MacRimi # License : MIT (https://raw.githubusercontent.com/MacRimi/ProxMenux/main/LICENSE) -# Version : 1.1 -# Last Updated: 19/06/2025 +# Version : 1.0 +# Last Updated: 06/07/2025 # ========================================================== # Description: -# This script provides a dynamic menu for uninstalling optional tools -# installed through ProxMenux on Proxmox Virtual Environment (VE). +# This script provides a complete uninstallation and rollback system +# for all post-installation optimizations applied by ProxMenux. +# +# It allows administrators to safely revert any changes made during the +# optimization process, restoring the system to its original state. +# +# This ensures full control over system configurations and gives users +# the confidence to apply, test, and undo ProxMenux enhancements as needed. # ========================================================== + REPO_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main" RETURN_SCRIPT="$REPO_URL/scripts/menus/menu_post_install.sh" BASE_DIR="/usr/local/share/proxmenux" @@ -24,64 +30,22 @@ VENV_PATH="/opt/googletrans-env" if [[ -f "$UTILS_FILE" ]]; then source "$UTILS_FILE" fi + load_language initialize_cache - +# Tool registration system ensure_tools_json() { - [ -f "$TOOLS_JSON" ] || echo "{}" > "$TOOLS_JSON" + [ -f "$TOOLS_JSON" ] || echo "{}" > "$TOOLS_JSON" } register_tool() { - local tool="$1" - local state="$2" - ensure_tools_json - jq --arg t "$tool" --argjson v "$state" '.[$t]=$v' "$TOOLS_JSON" > "$TOOLS_JSON.tmp" && mv "$TOOLS_JSON.tmp" "$TOOLS_JSON" + local tool="$1" + local state="$2" + ensure_tools_json + jq --arg t "$tool" --argjson v "$state" '.[$t]=$v' "$TOOLS_JSON" > "$TOOLS_JSON.tmp" && mv "$TOOLS_JSON.tmp" "$TOOLS_JSON" } - - -migrate_installed_tools() { - local TOOLS_JSON="/usr/local/share/proxmenux/installed_tools.json" - - if [[ -f "$TOOLS_JSON" ]]; then - return - fi - clear - show_proxmenux_logo - msg_info "$(translate 'Detecting previous adjustments...')" - - echo "{}" > "$TOOLS_JSON" - local updated=false - - # --- Fastfetch --- - if command -v fastfetch >/dev/null 2>&1 || [[ -x /usr/local/bin/fastfetch ]] || [[ -x /usr/bin/fastfetch ]]; then - jq '. + {"fastfetch": true}' "$TOOLS_JSON" > "$TOOLS_JSON.tmp" && mv "$TOOLS_JSON.tmp" "$TOOLS_JSON" - updated=true - fi - - # --- Figurine --- - if command -v figurine >/dev/null 2>&1 || [[ -x /usr/local/bin/figurine ]]; then - jq '. + {"figurine": true}' "$TOOLS_JSON" > "$TOOLS_JSON.tmp" && mv "$TOOLS_JSON.tmp" "$TOOLS_JSON" - updated=true - fi - - # --- Kexec --- - if dpkg -s kexec-tools >/dev/null 2>&1 && systemctl list-unit-files | grep -q kexec-pve.service; then - jq '. + {"kexec": true}' "$TOOLS_JSON" > "$TOOLS_JSON.tmp" && mv "$TOOLS_JSON.tmp" "$TOOLS_JSON" - updated=true - fi - - - if [[ "$updated" == true ]]; then - sleep 2 - msg_ok "$(translate 'Adjustments detected and ready to revert.')" - sleep 1 - fi -} - - - ################################################################ uninstall_fastfetch() { @@ -139,53 +103,504 @@ uninstall_kexec() { ################################################################ -show_uninstall_menu() { - ensure_tools_json - mapfile -t tools_installed < <(jq -r 'to_entries | map(select(.value==true)) | .[].key' "$TOOLS_JSON") - if [[ ${#tools_installed[@]} -eq 0 ]]; then - dialog --backtitle "ProxMenux" --title "ProxMenux" --msgbox "\n\n$(translate "No uninstallable tools detected.")" 10 60 +uninstall_apt_upgrade() { + msg_info "$(translate "Restoring enterprise repositories...")" + + # Re-enable enterprise repos + if [ -f /etc/apt/sources.list.d/pve-enterprise.list ]; then + sed -i "s/^#deb/deb/g" /etc/apt/sources.list.d/pve-enterprise.list + fi + + if [ -f /etc/apt/sources.list.d/ceph.list ]; then + sed -i "s/^#deb/deb/g" /etc/apt/sources.list.d/ceph.list + fi + + # Remove public repo + rm -f /etc/apt/sources.list.d/pve-public-repo.list + + # Remove firmware warning config + rm -f /etc/apt/apt.conf.d/no-bookworm-firmware.conf + + apt-get update > /dev/null 2>&1 + + msg_ok "$(translate "Enterprise repositories restored")" + register_tool "apt_upgrade" false +} + +################################################################ + +uninstall_subscription_banner() { + msg_info "$(translate "Restoring subscription banner...")" + + # Remove APT hook + rm -f /etc/apt/apt.conf.d/no-nag-script + + # Reinstall proxmox-widget-toolkit to restore original + apt --reinstall install proxmox-widget-toolkit -y >/dev/null 2>&1 + + msg_ok "$(translate "Subscription banner restored")" + register_tool "subscription_banner" false +} + +################################################################ + +uninstall_time_sync() { + msg_info "$(translate "Resetting time synchronization...")" + + # Reset to UTC (safe default) + timedatectl set-timezone UTC >/dev/null 2>&1 + + msg_ok "$(translate "Time synchronization reset to UTC")" + register_tool "time_sync" false +} + +################################################################ + +uninstall_apt_languages() { + msg_info "$(translate "Restoring APT language downloads...")" + + # Remove the configuration that disables translations + rm -f /etc/apt/apt.conf.d/99-disable-translations + + msg_ok "$(translate "APT language downloads restored")" + register_tool "apt_languages" false +} + +################################################################ + +uninstall_journald() { + msg_info "$(translate "Restoring default journald configuration...")" + + # Restore default journald configuration + cat > /etc/systemd/journald.conf << 'EOF' +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# Entries in this file show the compile time defaults. +# You can change settings by editing this file. +# Defaults can be restored by simply deleting this file. +# +# See journald.conf(5) for details. + +[Journal] +#Storage=auto +#Compress=yes +#Seal=yes +#SplitMode=uid +#SyncIntervalSec=5m +#RateLimitInterval=30s +#RateLimitBurst=1000 +#SystemMaxUse= +#SystemKeepFree= +#SystemMaxFileSize= +#RuntimeMaxUse= +#RuntimeKeepFree= +#RuntimeMaxFileSize= +#MaxRetentionSec= +#MaxFileSec=1month +#ForwardToSyslog=yes +#ForwardToKMsg=no +#ForwardToConsole=no +#ForwardToWall=yes +#TTYPath=/dev/console +#MaxLevelStore=debug +#MaxLevelSyslog=debug +#MaxLevelKMsg=notice +#MaxLevelConsole=info +#MaxLevelWall=emerg +EOF + + systemctl restart systemd-journald.service >/dev/null 2>&1 + + msg_ok "$(translate "Default journald configuration restored")" + register_tool "journald" false +} + +################################################################ + +uninstall_logrotate() { + msg_info "$(translate "Restoring original logrotate configuration...")" + + # Restore from backup if it exists + if [ -f /etc/logrotate.conf.bak ]; then + mv /etc/logrotate.conf.bak /etc/logrotate.conf + systemctl restart logrotate >/dev/null 2>&1 + msg_ok "$(translate "Original logrotate configuration restored")" + else + msg_warn "$(translate "No backup found, logrotate configuration not changed")" + fi + + register_tool "logrotate" false +} + +################################################################ + +uninstall_system_limits() { + msg_info "$(translate "Removing system limits optimizations...")" + + # Remove ProxMenux sysctl configurations + rm -f /etc/sysctl.d/99-maxwatches.conf + rm -f /etc/sysctl.d/99-maxkeys.conf + rm -f /etc/sysctl.d/99-swap.conf + rm -f /etc/sysctl.d/99-fs.conf + + # Remove ProxMenux limits configuration + rm -f /etc/security/limits.d/99-limits.conf + + # Remove systemd limits (restore defaults) + for file in /etc/systemd/system.conf /etc/systemd/user.conf; do + if [ -f "$file" ]; then + sed -i '/^DefaultLimitNOFILE=256000/d' "$file" + fi + done + + # Remove PAM limits + for file in /etc/pam.d/common-session /etc/pam.d/runuser-l; do + if [ -f "$file" ]; then + sed -i '/^session required pam_limits.so/d' "$file" + fi + done + + # Remove ulimit from profile + if [ -f /root/.profile ]; then + sed -i '/ulimit -n 256000/d' /root/.profile + fi + + # Reload sysctl + sysctl --system >/dev/null 2>&1 + + msg_ok "$(translate "System limits optimizations removed")" + register_tool "system_limits" false +} + +################################################################ + +uninstall_entropy() { + msg_info "$(translate "Removing entropy generation optimization...")" + + # Stop and disable haveged + systemctl stop haveged >/dev/null 2>&1 + systemctl disable haveged >/dev/null 2>&1 + + # Remove haveged package + apt-get purge -y haveged >/dev/null 2>&1 + + # Remove configuration + rm -f /etc/default/haveged + + msg_ok "$(translate "Entropy generation optimization removed")" + register_tool "entropy" false +} + +################################################################ + +uninstall_memory_settings() { + msg_info "$(translate "Removing memory optimizations...")" + + # Remove ProxMenux memory configuration + rm -f /etc/sysctl.d/99-memory.conf + + # Reload sysctl + sysctl --system >/dev/null 2>&1 + + msg_ok "$(translate "Memory optimizations removed")" + register_tool "memory_settings" false +} + +################################################################ + +uninstall_kernel_panic() { + msg_info "$(translate "Removing kernel panic configuration...")" + + # Remove ProxMenux kernel panic configuration + rm -f /etc/sysctl.d/99-kernelpanic.conf + + # Reload sysctl + sysctl --system >/dev/null 2>&1 + + msg_ok "$(translate "Kernel panic configuration removed")" + register_tool "kernel_panic" false +} + +################################################################ + +uninstall_apt_ipv4() { + msg_info "$(translate "Removing APT IPv4 configuration...")" + + # Remove IPv4 force configuration + rm -f /etc/apt/apt.conf.d/99-force-ipv4 + + msg_ok "$(translate "APT IPv4 configuration removed")" + register_tool "apt_ipv4" false +} + +################################################################ + +uninstall_network_optimization() { + msg_info "$(translate "Removing network optimizations...")" + + # Remove ProxMenux network configuration + rm -f /etc/sysctl.d/99-network.conf + + # Remove interfaces.d source line if we added it + local interfaces_file="/etc/network/interfaces" + if [ -f "$interfaces_file" ]; then + # Only remove if it's the last line and looks like our addition + if tail -1 "$interfaces_file" | grep -q "^source /etc/network/interfaces.d/\*$"; then + sed -i '$d' "$interfaces_file" + fi + fi + + # Reload sysctl + sysctl --system >/dev/null 2>&1 + + msg_ok "$(translate "Network optimizations removed")" + register_tool "network_optimization" false +} + +################################################################ + +uninstall_disable_rpc() { + msg_info "$(translate "Re-enabling RPC services...")" + + # Re-enable and start rpcbind + systemctl enable rpcbind >/dev/null 2>&1 + systemctl start rpcbind >/dev/null 2>&1 + + msg_ok "$(translate "RPC services re-enabled")" + register_tool "disable_rpc" false +} + +################################################################ + +uninstall_bashrc_custom() { + msg_info "$(translate "Restoring original bashrc...")" + + # Restore original bashrc from backup + if [ -f /root/.bashrc.bak ]; then + mv /root/.bashrc.bak /root/.bashrc + msg_ok "$(translate "Original bashrc restored")" + else + # Remove ProxMenux customizations manually + if [ -f /root/.bashrc ]; then + # Remove our customization block + sed -i '/# ProxMenux customizations/,/source \/etc\/profile\.d\/bash_completion\.sh/d' /root/.bashrc + fi + msg_ok "$(translate "ProxMenux customizations removed from bashrc")" + fi + + # Remove bash_profile source line if we added it + if [ -f /root/.bash_profile ]; then + sed -i '/source \/root\/\.bashrc/d' /root/.bash_profile + fi + + register_tool "bashrc_custom" false +} + +################################################################ + +uninstall_log2ram() { + if [[ ! -f /etc/log2ram.conf ]] && ! systemctl list-units --all | grep -q log2ram; then + msg_warn "$(translate "log2ram is not installed.")" return 0 fi + msg_info "$(translate "Uninstalling log2ram...")" + + # Stop and disable services and timers + systemctl stop log2ram >/dev/null 2>&1 + systemctl disable log2ram >/dev/null 2>&1 + systemctl stop log2ram-daily.timer >/dev/null 2>&1 + systemctl disable log2ram-daily.timer >/dev/null 2>&1 + + # Remove cron jobs + rm -f /etc/cron.d/log2ram + rm -f /etc/cron.d/log2ram-auto-sync + + # Remove config and binaries + rm -f /usr/local/bin/log2ram-check.sh + rm -f /usr/sbin/log2ram + rm -f /etc/log2ram.conf* + rm -f /etc/systemd/system/log2ram.service + rm -f /etc/systemd/system/log2ram-daily.timer + rm -f /etc/systemd/system/log2ram-daily.service + + # Clean up log2ram mount if active + if [ -d /var/log.hdd ]; then + if [ -d /var/log ] && mountpoint -q /var/log; then + rsync -a /var/log/ /var/log.hdd/ >/dev/null 2>&1 + umount /var/log >/dev/null 2>&1 + fi + rm -rf /var/log.hdd + fi + + systemctl daemon-reexec >/dev/null 2>&1 + systemctl daemon-reload >/dev/null 2>&1 + + msg_ok "$(translate "log2ram completely removed")" + register_tool "log2ram" false +} + +################################################################ + +migrate_installed_tools() { + if [[ -f "$TOOLS_JSON" ]]; then + return + fi + + clear + show_proxmenux_logo + msg_info "$(translate 'Detecting previous optimizations...')" + + echo "{}" > "$TOOLS_JSON" + local updated=false + + + + # APT configurations + if [[ -f /etc/apt/apt.conf.d/99-force-ipv4 ]]; then + jq '. + {"apt_ipv4": true}' "$TOOLS_JSON" > "$TOOLS_JSON.tmp" && mv "$TOOLS_JSON.tmp" "$TOOLS_JSON" + updated=true + fi + + if [[ -f /etc/apt/apt.conf.d/99-disable-translations ]]; then + jq '. + {"apt_languages": true}' "$TOOLS_JSON" > "$TOOLS_JSON.tmp" && mv "$TOOLS_JSON.tmp" "$TOOLS_JSON" + updated=true + fi + + # System configurations + if [[ -f /etc/sysctl.d/99-memory.conf ]]; then + jq '. + {"memory_settings": true}' "$TOOLS_JSON" > "$TOOLS_JSON.tmp" && mv "$TOOLS_JSON.tmp" "$TOOLS_JSON" + updated=true + fi + + if [[ -f /etc/sysctl.d/99-network.conf ]]; then + jq '. + {"network_optimization": true}' "$TOOLS_JSON" > "$TOOLS_JSON.tmp" && mv "$TOOLS_JSON.tmp" "$TOOLS_JSON" + updated=true + fi + + if [[ -f /etc/sysctl.d/99-kernelpanic.conf ]]; then + jq '. + {"kernel_panic": true}' "$TOOLS_JSON" > "$TOOLS_JSON.tmp" && mv "$TOOLS_JSON.tmp" "$TOOLS_JSON" + updated=true + fi + + if [[ -f /etc/security/limits.d/99-limits.conf ]]; then + jq '. + {"system_limits": true}' "$TOOLS_JSON" > "$TOOLS_JSON.tmp" && mv "$TOOLS_JSON.tmp" "$TOOLS_JSON" + updated=true + fi + + # Services + if systemctl is-active --quiet log2ram 2>/dev/null; then + jq '. + {"log2ram": true}' "$TOOLS_JSON" > "$TOOLS_JSON.tmp" && mv "$TOOLS_JSON.tmp" "$TOOLS_JSON" + updated=true + fi + + if dpkg -l | grep -q haveged; then + jq '. + {"entropy": true}' "$TOOLS_JSON" > "$TOOLS_JSON.tmp" && mv "$TOOLS_JSON.tmp" "$TOOLS_JSON" + updated=true + fi + + # Bashrc customization + if grep -q "# ProxMenux customizations" /root/.bashrc 2>/dev/null; then + jq '. + {"bashrc_custom": true}' "$TOOLS_JSON" > "$TOOLS_JSON.tmp" && mv "$TOOLS_JSON.tmp" "$TOOLS_JSON" + updated=true + fi + + # Subscription banner + if [[ -f /etc/apt/apt.conf.d/no-nag-script ]]; then + jq '. + {"subscription_banner": true}' "$TOOLS_JSON" > "$TOOLS_JSON.tmp" && mv "$TOOLS_JSON.tmp" "$TOOLS_JSON" + updated=true + fi + + if [[ "$updated" == true ]]; then + sleep 2 + msg_ok "$(translate 'Optimizations detected and ready to revert.')" + sleep 1 + fi +} + +################################################################ + +show_uninstall_menu() { + ensure_tools_json + migrate_installed_tools + + mapfile -t tools_installed < <(jq -r 'to_entries | map(select(.value==true)) | .[].key' "$TOOLS_JSON") + + if [[ ${#tools_installed[@]} -eq 0 ]]; then + dialog --backtitle "ProxMenux" --title "ProxMenux" \ + --msgbox "\n\n$(translate "No optimizations detected to uninstall.")" 10 60 + return 0 + fi + local menu_options=() for tool in "${tools_installed[@]}"; do case "$tool" in - fastfetch) desc="Fastfetch";; - figurine) desc="Figurine";; - kexec) desc="Kexec fast reboot";; - *) desc="$tool";; + lvm_repair) desc="LVM PV Headers Repair";; + repo_cleanup) desc="Repository Cleanup";; + apt_upgrade) desc="APT Upgrade & Repository Config";; + subscription_banner) desc="Subscription Banner Removal";; + time_sync) desc="Time Synchronization";; + apt_languages) desc="APT Language Skip";; + journald) desc="Journald Optimization";; + logrotate) desc="Logrotate Optimization";; + system_limits) desc="System Limits Increase";; + entropy) desc="Entropy Generation (haveged)";; + memory_settings) desc="Memory Settings Optimization";; + kernel_panic) desc="Kernel Panic Configuration";; + apt_ipv4) desc="APT IPv4 Force";; + network_optimization) desc="Network Optimizations";; + disable_rpc) desc="RPC/rpcbind Disable";; + bashrc_custom) desc="Bashrc Customization";; + log2ram) desc="Log2ram (SSD Protection)";; + *) desc="$tool";; esac menu_options+=("$tool" "$desc" "off") done - + selected_tools=$(dialog --backtitle "ProxMenux" \ - --title "$(translate "Uninstall Tools")" \ - --checklist "$(translate "Select settings") post-install $(translate "to uninstall:")" 20 60 12 \ - "${menu_options[@]}" 3>&1 1>&2 2>&3) + --title "$(translate "Uninstall Optimizations")" \ + --checklist "$(translate "Select optimizations to uninstall:")" 20 70 12 \ + "${menu_options[@]}" 3>&1 1>&2 2>&3) + local dialog_result=$? - if [[ $dialog_result -ne 0 || -z "$selected_tools" ]]; then return 0 - fi - + + # Show confirmation + if ! dialog --backtitle "ProxMenux" \ + --title "$(translate "Confirm Uninstallation")" \ + --yesno "\n\n$(translate "Are you sure you want to uninstall the selected optimizations.")" 10 60; then + return 0 + fi + + # Execute uninstallations for tool in $selected_tools; do tool=$(echo "$tool" | tr -d '"') if declare -f "uninstall_$tool" > /dev/null 2>&1; then - clear - show_proxmenux_logo - "uninstall_$tool" + clear + show_proxmenux_logo + "uninstall_$tool" else msg_warn "$(translate "No uninstaller found for:") $tool" fi done - - msg_success "$(translate "All related files and settings have been removed. Reboot required.")" - msg_success "$(translate "Press Enter to return to menu...")" - read -r - + + msg_success "$(translate "Selected optimizations have been uninstalled.")" + msg_warn "$(translate "A system reboot is recommended to ensure all changes take effect.")" + + if dialog --backtitle "ProxMenux" \ + --title "$(translate "Reboot Recommended")" \ + --yesno "$(translate "Do you want to reboot now?")" 8 50; then + reboot + fi } + ################################################################ -migrate_installed_tools -show_uninstall_menu \ No newline at end of file +show_uninstall_menu