2025-02-24 23:44:47 +01:00
#!/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)
2025-03-11 18:25:19 +01:00
# Version : 1.2
# Last Updated: 11/03/2025
2025-02-24 23:44:47 +01:00
# ==========================================================
# 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)
#
2025-03-07 19:01:44 +01:00
# 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
#
2025-02-24 23:44:47 +01:00
# 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
2025-02-25 18:17:24 +01:00
SCRIPT_TITLE = "Customizable post-installation optimization script"
2025-02-24 23:44:47 +01:00
# ==========================================================
enable_kexec( ) {
msg_info2 " $( translate "Configuring kexec for quick reboots..." ) "
NECESSARY_REBOOT = 1
# 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
2025-03-05 22:12:33 +01:00
if [ ! -f /root/.bash_profile ] ; then
touch /root/.bash_profile
fi
2025-02-24 23:44:47 +01:00
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
2025-05-02 22:12:59 +02:00
2025-02-24 23:44:47 +01:00
# 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
2025-05-02 21:40:52 +02:00
# Disable enterprise Proxmox Ceph repo
2025-02-24 23:44:47 +01:00
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..." ) "
2025-05-02 21:40:52 +02:00
echo " deb http://download.proxmox.com/debian/pve ${ OS_CODENAME } pve-no-subscription " > /etc/apt/sources.list.d/pve-public-repo.list
2025-02-24 23:44:47 +01:00
msg_ok " $( translate "Free public Proxmox repository enabled" ) "
fi
2025-03-03 17:52:56 +01:00
# # 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
2025-02-24 23:44:47 +01:00
2025-03-11 19:35:59 +01:00
# ======================================================
# Configure main Debian repositories
# ======================================================
2025-03-11 18:24:49 +01:00
2025-03-11 19:45:11 +01:00
sources_file = "/etc/apt/sources.list"
need_update = false
2025-03-11 19:35:59 +01:00
2025-03-11 19:45:11 +01:00
# Reemplazar ftp.es.debian.org por deb.debian.org si existe
sed -i 's|ftp.es.debian.org|deb.debian.org|g' " $sources_file "
2025-03-11 19:35:59 +01:00
2025-03-11 19:45:11 +01:00
# 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
2025-03-11 19:35:59 +01:00
2025-03-11 19:45:11 +01:00
# 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
2025-03-11 19:35:59 +01:00
2025-03-11 19:45:11 +01:00
# 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
2025-03-11 19:35:59 +01:00
2025-03-11 19:45:11 +01:00
# 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
2025-03-11 19:35:59 +01:00
2025-03-11 19:45:11 +01:00
msg_ok " $( translate "Debian repositories configured correctly" ) "
2025-03-11 19:35:59 +01:00
# ===================================================
2025-02-24 23:44:47 +01:00
# 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
2025-03-06 23:15:32 +01:00
2025-02-24 23:44:47 +01:00
# update proxmox and install system utils
2025-03-06 23:15:32 +01:00
msg_info " $( translate "Performing packages upgrade..." ) "
2025-02-24 23:44:47 +01:00
apt-get install pv -y > /dev/null 2>& 1
total_packages = $( apt-get -s dist-upgrade | grep "^Inst" | wc -l)
2025-03-06 23:15:32 +01:00
2025-02-24 23:44:47 +01:00
if [ " $total_packages " -eq 0 ] ; then
total_packages = 1
fi
2025-03-06 23:15:32 +01:00
msg_ok " $( translate "Packages upgrade successfull" ) "
tput civis
tput sc
2025-03-05 21:37:30 +01:00
2025-03-06 23:15:32 +01:00
2025-02-24 23:44:47 +01:00
(
/usr/bin/env DEBIAN_FRONTEND = noninteractive apt-get -y -o Dpkg::Options::= '--force-confdef' dist-upgrade 2>& 1 | \
while IFS = read -r line; do
2025-03-05 21:37:30 +01:00
if [ [ " $line " = ~ ^( Setting up| Unpacking| Preparing to unpack| Processing triggers for ) ] ] ; then
2025-03-06 23:15:32 +01:00
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 "
2025-05-28 23:26:58 +02:00
2025-03-06 23:15:32 +01:00
done
2025-02-24 23:44:47 +01:00
fi
done
)
if [ $? -eq 0 ] ; then
2025-03-06 23:15:32 +01:00
tput rc
tput ed
2025-02-24 23:44:47 +01:00
msg_ok " $( translate "System upgrade completed" ) "
fi
2025-03-06 23:15:32 +01:00
2025-03-06 23:22:44 +01:00
2025-03-06 23:15:32 +01:00
2025-02-24 23:44:47 +01:00
# 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
2025-05-04 11:57:14 +02:00
lvm_repair_check
2025-03-09 20:50:06 +01:00
2025-03-11 18:24:49 +01:00
cleanup_duplicate_repos
2025-03-09 20:50:06 +01:00
2025-02-24 23:44:47 +01:00
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 <<EOF > /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 <<EOF > " $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" ) "
}
# ==========================================================
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( ) {
2025-05-02 22:12:59 +02:00
2025-02-24 23:44:47 +01:00
msg_info2 " $( translate "Installing common system utilities..." ) "
2025-05-02 22:12:59 +02:00
if [ [ " $LANGUAGE " != "en" ] ] ; then
msg_lang " $( translate "Generating automatic translations..." ) "
fi
2025-02-24 23:44:47 +01:00
2025-05-02 22:12:59 +02:00
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
2025-03-06 23:15:32 +01:00
2025-05-02 22:12:59 +02:00
selected_packages = ( $choices )
2025-02-24 23:44:47 +01:00
2025-05-02 22:12:59 +02:00
if [ ${# selected_packages [@] } -eq 0 ] ; then
msg_warn " $( translate "No packages selected for installation" ) "
return
fi
2025-02-24 23:44:47 +01:00
2025-05-02 22:12:59 +02:00
tput civis
tput sc
2025-03-06 23:15:32 +01:00
2025-05-02 22:12:59 +02:00
for package in " ${ selected_packages [@] } " ; do
if dpkg -s " $package " >/dev/null 2>& 1; then
continue
2025-02-24 23:44:47 +01:00
fi
2025-03-06 23:15:32 +01:00
tput rc
tput ed
2025-02-24 23:44:47 +01:00
2025-05-02 22:12:59 +02:00
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 "
sleep 0.2
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" ) "
2025-02-24 23:44:47 +01:00
msg_success " $( translate "Common system utilities installation completed" ) "
}
2025-05-02 22:12:59 +02:00
2025-02-24 23:44:47 +01:00
# ==========================================================
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 <<EOF > /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 <<EOF > " $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 <<EOF > " $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 <<EOF > " $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 <<EOF > " $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 - <<EOF
#!/bin/sh
PATH = /bin:\$ PATH
GZIP = "-1"
exec /usr/bin/pigz "\$@"
EOF
then
cat <<EOF > /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( ) {
2025-03-02 18:30:30 +01:00
msg_info2 " $( translate "Installing and configuring Fail2Ban to protect the web interface..." ) "
2025-03-02 23:13:22 +01:00
2025-03-02 18:30:30 +01:00
# 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" ) "
2025-02-24 23:44:47 +01:00
else
2025-03-02 18:30:30 +01:00
msg_error " $( translate "Failed to install Fail2Ban" ) "
return 1
2025-02-24 23:44:47 +01:00
fi
2025-03-02 18:30:30 +01:00
mkdir -p /etc/fail2ban/jail.d /etc/fail2ban/filter.d
msg_info " $( translate "Configuring Proxmox filter..." ) "
cat > /etc/fail2ban/filter.d/proxmox.conf << EOF
2025-02-24 23:44:47 +01:00
[ Definition]
failregex = pvedaemon\[ .*authentication failure; rhost = <HOST> user = .* msg = .*
ignoreregex =
EOF
2025-03-02 18:30:30 +01:00
msg_ok " $( translate "Proxmox filter configured" ) "
2025-02-24 23:44:47 +01:00
2025-03-02 18:30:30 +01:00
msg_info " $( translate "Configuring Proxmox jail..." ) "
cat > /etc/fail2ban/jail.d/proxmox.conf << EOF
2025-02-24 23:44:47 +01:00
[ proxmox]
enabled = true
port = https,http,8006,8007
filter = proxmox
logpath = /var/log/daemon.log
maxretry = 3
bantime = 3600
findtime = 600
EOF
2025-03-02 18:30:30 +01:00
msg_ok " $( translate "Proxmox jail configured" ) "
2025-02-24 23:44:47 +01:00
2025-03-02 18:30:30 +01:00
msg_info " $( translate "Configuring general Fail2Ban settings..." ) "
cat > /etc/fail2ban/jail.local << EOF
2025-02-24 23:44:47 +01:00
[ DEFAULT]
ignoreip = 127.0.0.1
bantime = 86400
maxretry = 2
findtime = 1800
2025-03-02 18:30:30 +01:00
2025-02-24 23:44:47 +01:00
[ 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
2025-03-02 18:30:30 +01:00
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
2025-02-24 23:44:47 +01:00
fi
2025-03-02 18:30:30 +01:00
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" ) "
2025-02-24 23:44:47 +01:00
else
2025-03-02 18:30:30 +01:00
msg_error " $( translate "Fail2Ban is NOT running! Checking logs..." ) "
journalctl -u fail2ban --no-pager -n 20
2025-02-24 23:44:47 +01:00
fi
2025-03-02 23:13:22 +01:00
2025-03-02 18:30:30 +01:00
msg_info " $( translate "Checking Fail2Ban socket..." ) "
if [ -S /var/run/fail2ban/fail2ban.sock ] ; then
msg_ok " $( translate "Fail2Ban socket exists!" ) "
2025-02-24 23:44:47 +01:00
else
2025-03-02 18:30:30 +01:00
msg_warn " $( translate "Warning: Fail2Ban socket does not exist!" ) "
2025-02-24 23:44:47 +01:00
fi
2025-03-02 23:13:22 +01:00
2025-03-02 18:30:30 +01:00
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" ) "
2025-02-24 23:44:47 +01:00
else
2025-03-02 18:30:30 +01:00
msg_error " $( translate "fail2ban-client could not communicate with the server" ) "
2025-02-24 23:44:47 +01:00
fi
2025-03-02 23:13:22 +01:00
2025-03-02 18:30:30 +01:00
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!" ) "
2025-02-24 23:44:47 +01:00
}
# ==========================================================
2025-05-05 11:53:40 +02:00
install_lynis_( ) {
2025-02-24 23:44:47 +01:00
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
2025-05-05 11:53:40 +02:00
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..." ) "
2025-05-05 12:00:48 +02:00
apt-get update -qq >/dev/null 2>& 1
apt-get install -y git >/dev/null 2>& 1
msg_ok " $( translate "Git installed" ) "
2025-05-05 11:53:40 +02:00
fi
2025-05-05 12:02:50 +02:00
if [ -d /opt/lynis ] ; then
rm -rf /opt/lynis >/dev/null 2>& 1
fi
2025-05-05 11:53:40 +02:00
msg_info " $( translate "Cloning Lynis from GitHub..." ) "
2025-05-05 12:00:48 +02:00
if git clone --quiet https://github.com/CISOfy/lynis.git /opt/lynis >/dev/null 2>& 1; then
2025-05-09 11:41:16 +02:00
# 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
2025-05-05 11:53:40 +02:00
msg_ok " $( translate "Lynis installed successfully from GitHub" ) "
else
msg_warn " $( translate "Failed to clone Lynis from GitHub" ) "
return 1
fi
2025-05-09 11:41:16 +02:00
if /usr/local/bin/lynis show version >/dev/null 2>& 1; then
2025-02-24 23:44:47 +01:00
msg_success " $( translate "Lynis is ready to use" ) "
else
msg_warn " $( translate "Lynis installation could not be verified" ) "
fi
}
2025-05-09 19:31:44 +02:00
2025-02-24 23:44:47 +01:00
# ==========================================================
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" ) "
2025-03-09 20:50:06 +01:00
iommu_param = "intel_iommu=on"
2025-02-24 23:44:47 +01:00
elif [ [ " $cpu_info " = = *"AuthenticAMD" * ] ] ; then
msg_info " $( translate "Detected AMD CPU" ) "
2025-03-09 20:50:06 +01:00
iommu_param = "amd_iommu=on"
2025-02-24 23:44:47 +01:00
else
msg_warning " $( translate "Unknown CPU type. IOMMU might not be properly enabled." ) "
return 1
fi
if [ [ " $uses_zfs " = = true ] ] ; then
2025-03-09 20:50:06 +01:00
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
2025-02-24 23:44:47 +01:00
else
cp " $cmdline_file " " ${ cmdline_file } .bak "
2025-03-09 20:50:06 +01:00
sed -i " /^.*root=ZFS=/ s| $| $iommu_param iommu=pt| " " $cmdline_file "
2025-02-24 23:44:47 +01:00
msg_ok " $( translate "IOMMU and additional parameters added for ZFS" ) "
fi
else
2025-03-09 20:50:06 +01:00
2025-02-24 23:44:47 +01:00
if grep -q " $iommu_param " " $grub_file " ; then
2025-03-09 20:50:06 +01:00
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
2025-02-24 23:44:47 +01:00
else
cp " $grub_file " " ${ grub_file } .bak "
2025-03-09 20:50:06 +01:00
sed -i " /GRUB_CMDLINE_LINUX_DEFAULT=/ s|\" $| $iommu_param iommu=pt\"| " " $grub_file "
2025-02-24 23:44:47 +01:00
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..." ) "
2025-03-05 21:37:30 +01:00
touch " $blacklist_file "
2025-02-24 23:44:47 +01:00
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" ) "
}
# ==========================================================
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"
2025-05-08 09:05:23 +02:00
local custom_message = " This system is optimised by: ProxMenux"
2025-02-24 23:44:47 +01:00
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 <<EOF > " $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 "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"
2025-05-13 17:01:25 +02:00
if [ -f " $sysctl_conf " ] && grep -q "Memory Optimising" " $sysctl_conf " ; then
msg_info " $( translate "Old memory configuration detected. Replacing with balanced optimization..." ) "
2025-02-24 23:44:47 +01:00
else
2025-05-13 17:01:25 +02:00
msg_info " $( translate "Applying balanced memory optimization settings..." ) "
fi
cat <<EOF > " $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
2025-02-24 23:44:47 +01:00
vm.overcommit_memory = 1
2025-05-13 17:01:25 +02:00
# Avoid excessive virtual memory areas (safe for most applications)
vm.max_map_count = 65530
2025-02-24 23:44:47 +01:00
EOF
2025-05-13 17:01:25 +02:00
if [ -f /proc/sys/vm/compaction_proactiveness ] ; then
echo "vm.compaction_proactiveness = 20" >> " $sysctl_conf "
msg_ok " $( translate "Enabled memory compaction proactiveness" ) "
2025-02-24 23:44:47 +01:00
fi
2025-05-13 17:01:25 +02:00
msg_ok " $( translate "Memory settings optimized successfully" ) "
msg_success " $( translate "Memory optimization completed." ) "
2025-02-24 23:44:47 +01:00
}
# ==========================================================
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..." ) "
# 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 "
2025-03-02 23:13:22 +01:00
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
2025-05-09 10:55:23 +02:00
msg_ok " $( translate "Fastfetch download URL retrieved successfully." ) "
2025-03-02 23:13:22 +01:00
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
2025-02-24 23:44:47 +01:00
msg_ok " $( translate "Fastfetch installed successfully" ) "
else
2025-03-02 23:13:22 +01:00
msg_error " $( translate "Failed to install Fastfetch." ) "
return 1
2025-02-24 23:44:47 +01:00
fi
2025-03-02 23:13:22 +01:00
rm -f /tmp/fastfetch.deb
if ! command -v fastfetch & > /dev/null; then
msg_error " $( translate "Fastfetch is not installed correctly." ) "
return 1
fi
2025-03-02 19:50:59 +01:00
if [ ! -f " $fastfetch_config " ] ; then
2025-03-02 23:13:22 +01:00
echo '{"$schema": "https://github.com/fastfetch-cli/fastfetch/raw/dev/doc/json_schema.json", "modules": []}' > " $fastfetch_config "
2025-03-02 19:50:59 +01:00
fi
2025-03-02 23:13:22 +01:00
fastfetch --gen-config-force > /dev/null 2>& 1
2025-02-24 23:44:47 +01:00
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 "
2025-03-05 21:37:30 +01:00
if wget -qO " $proxmenux_logo_path " "https://raw.githubusercontent.com/MacRimi/ProxMenux/main/images/logos_txt/logo.txt" ; then
2025-02-24 23:44:47 +01:00
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
2025-03-02 23:13:22 +01:00
# Modify Fastfetch modules to display custom title
msg_info " $( translate "Modifying Fastfetch configuration..." ) "
2025-02-24 23:44:47 +01:00
2025-03-02 23:13:22 +01:00
jq '.modules |= map(select(. != "title"))' " $fastfetch_config " > " ${ fastfetch_config } .tmp " && mv " ${ fastfetch_config } .tmp " " $fastfetch_config "
2025-02-24 23:44:47 +01:00
2025-03-02 23:13:22 +01:00
jq 'del(.modules[] | select(type == "object" and .type == "custom"))' " $fastfetch_config " > " ${ fastfetch_config } .tmp " && mv " ${ fastfetch_config } .tmp " " $fastfetch_config "
2025-02-24 23:44:47 +01:00
2025-03-02 23:13:22 +01:00
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 "
2025-02-24 23:44:47 +01:00
2025-03-02 23:13:22 +01:00
msg_ok " $( translate "Fastfetch now displays: System optimised by: ProxMenux" ) "
2025-02-24 23:44:47 +01:00
2025-03-02 23:13:22 +01:00
fastfetch --gen-config > /dev/null 2>& 1
msg_ok " $( translate "Fastfetch configuration updated" ) "
2025-02-24 23:44:47 +01:00
2025-03-02 23:13:22 +01:00
sed -i '/fastfetch/d' ~/.bashrc ~/.profile /etc/profile
rm -f /etc/update-motd.d/99-fastfetch
2025-02-24 23:44:47 +01:00
2025-03-02 23:13:22 +01:00
echo "clear && fastfetch" >> ~/.bashrc
msg_ok " $( translate "Fastfetch will start automatically in the console" ) "
2025-02-24 23:44:47 +01:00
2025-03-02 23:13:22 +01:00
msg_success " $( translate "Fastfetch installation and configuration completed" ) "
2025-02-24 23:44:47 +01:00
}
2025-05-04 11:57:14 +02:00
# ==========================================================
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" ) "
}
# ==========================================================
2025-05-07 18:36:14 +02:00
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." ) "
}
# ==========================================================
2025-05-27 19:58:03 +02:00
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
}
# ==========================================================
2025-05-04 11:57:14 +02:00
# ==========================================================
# Auxiliary help functions
# ==========================================================
2025-03-11 18:24:49 +01:00
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
2025-05-04 11:57:14 +02:00
2025-03-11 18:24:49 +01:00
if [ [ " $line " = ~ ^[ [ :space:] ] *# ] ] || [ [ -z " $line " ] ] ; then
echo " $line " >> " $temp_file "
continue
fi
2025-05-04 11:57:14 +02:00
2025-03-11 18:24:49 +01:00
if [ [ " $line " = ~ ^deb ] ] ; then
2025-05-04 11:57:14 +02:00
2025-03-11 18:24:49 +01:00
read -r _ url dist components <<< " $line "
2025-05-04 11:57:14 +02:00
2025-03-11 18:24:49 +01:00
local key = " ${ url } _ ${ dist } "
if [ [ -v " seen_repos[ $key ] " ] ] ; then
2025-05-04 11:57:14 +02:00
2025-03-11 18:24:49 +01:00
echo " # $line " >> " $temp_file "
cleaned_count = $(( cleaned_count + 1 ))
else
2025-05-04 11:57:14 +02:00
2025-03-11 18:24:49 +01:00
echo " $line " >> " $temp_file "
seen_repos[ $key ] = " $components "
fi
else
2025-05-04 11:57:14 +02:00
2025-03-11 18:24:49 +01:00
echo " $line " >> " $temp_file "
fi
done < " $sources_file "
2025-05-04 11:57:14 +02:00
2025-03-11 18:24:49 +01:00
mv " $temp_file " " $sources_file "
chmod 644 " $sources_file "
2025-05-04 11:57:14 +02:00
2025-03-11 18:24:49 +01:00
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
2025-05-04 11:57:14 +02:00
2025-03-11 18:24:49 +01:00
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
2025-05-04 11:57:14 +02:00
2025-03-11 18:24:49 +01:00
sed -i 's/^# *deb/deb/' " $file "
pve_public_repo_exists = true
pve_repo_count = 1
elif [ [ " $file " != " $pve_public_repo " ] ] ; then
2025-05-04 11:57:14 +02:00
2025-03-11 18:24:49 +01:00
sed -i 's/^deb/# deb/' " $file "
cleaned_count = $(( cleaned_count + 1 ))
fi
fi
done
apt update
}
2025-02-24 23:44:47 +01:00
2025-03-03 17:52:56 +01:00
2025-05-04 11:57:14 +02:00
lvm_repair_check( ) {
msg_info " $( translate "Checking and repairing old LVM PV headers (if needed)..." ) "
2025-03-03 17:52:56 +01:00
2025-05-04 11:57:14 +02:00
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
2025-03-03 17:52:56 +01:00
fi
2025-05-04 11:57:14 +02:00
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
2025-03-03 17:52:56 +01:00
}
2025-02-24 23:44:47 +01:00
2025-05-04 11:57:14 +02:00
2025-02-24 23:44:47 +01:00
# ==========================================================
2025-03-03 17:52:56 +01:00
2025-05-04 11:57:14 +02:00
2025-02-24 23:44:47 +01:00
# Main menu function
main_menu( ) {
2025-05-28 23:26:58 +02:00
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
2025-05-02 19:11:17 +02:00
declare -A category_order = (
2025-05-28 23:26:58 +02:00
[ "Basic Settings" ] = 1 [ "System" ] = 2 [ "Hardware" ] = 3 [ "Virtualization" ] = 4
[ "Network" ] = 5 [ "Storage" ] = 6 [ "Security" ] = 7 [ "Customization" ] = 8
[ "Monitoring" ] = 9 [ "Performance" ] = 10 [ "Optional" ] = 11
2025-05-02 19:11:17 +02:00
)
local options = (
2025-02-24 23:44:47 +01:00
"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"
2025-05-27 19:58:03 +02:00
"Optional|Update Proxmox VE Appliance Manager|PVEAM"
2025-02-24 23:44:47 +01:00
"Optional|Add latest Ceph support|CEPH"
2025-03-03 17:52:56 +01:00
"Optional|Add Proxmox testing repository|REPOTEST"
2025-02-24 23:44:47 +01:00
"Optional|Enable High Availability services|ENABLE_HA"
2025-05-07 18:36:14 +02:00
"Optional|Install Figurine|FIGURINE"
2025-05-02 19:11:17 +02:00
)
2025-02-24 23:44:47 +01:00
2025-05-02 19:11:17 +02:00
IFS = $'\n' sorted_options = ( $( for option in " ${ options [@] } " ; do
2025-02-24 23:44:47 +01:00
IFS = '|' read -r category description function_name <<< " $option "
printf "%d|%s|%s|%s\n" " ${ category_order [ $category ] :- 999 } " " $category " " $description " " $function_name "
2025-05-02 19:11:17 +02:00
done | sort -n | cut -d'|' -f2-) )
unset IFS
2025-03-09 21:53:20 +01:00
2025-05-28 23:26:58 +02:00
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
2025-03-09 21:53:20 +01:00
2025-05-28 23:26:58 +02:00
local checklist_items = ( )
2025-05-02 19:11:17 +02:00
local i = 1
2025-05-28 23:26:58 +02:00
local desc_index = 0
2025-05-02 19:11:17 +02:00
local previous_category = ""
2025-03-09 21:53:20 +01:00
2025-05-02 19:11:17 +02:00
for option in " ${ sorted_options [@] } " ; do
2025-02-24 23:44:47 +01:00
IFS = '|' read -r category description function_name <<< " $option "
2025-05-28 23:26:58 +02:00
2025-03-09 21:53:20 +01:00
2025-05-02 19:11:17 +02:00
if [ [ " $category " != " $previous_category " && " $category " = = "Optional" && -n " $previous_category " ] ] ; then
2025-05-28 23:26:58 +02:00
checklist_items += ( "" "==============================================================" "" )
2025-02-24 23:44:47 +01:00
fi
2025-05-28 23:26:58 +02:00
local desc_translated = " ${ temp_descriptions [ $desc_index ] } "
desc_index = $(( desc_index + 1 ))
2025-02-24 23:44:47 +01:00
2025-05-28 23:26:58 +02:00
if [ ${# desc_translated } -gt $max_desc_length ] ; then
desc_translated = " ${ desc_translated : 0 : $(( max_desc_length-3)) } ... "
fi
2025-05-07 18:40:39 +02:00
2025-05-28 23:26:58 +02:00
local spaces_needed = $(( max_desc_length - ${# desc_translated } ))
local padding = ""
for ( ( j = 0; j<spaces_needed; j++) ) ; do
padding += " "
2025-03-09 21:53:20 +01:00
done
2025-05-28 23:26:58 +02:00
local line = " ${ desc_translated } ${ padding } | ${ category } "
2025-02-24 23:44:47 +01:00
2025-05-28 23:26:58 +02:00
checklist_items += ( " $i " " $line " "off" )
2025-05-02 19:11:17 +02:00
i = $(( i + 1 ))
previous_category = " $category "
done
2025-02-24 23:44:47 +01:00
2025-05-28 23:26:58 +02:00
exec 3>& 1
selected_indices = $( dialog --clear \
--backtitle "ProxMenux" \
--title " $( translate "Post-Installation Options" ) " \
--checklist " $HEADER " 22 80 15 \
" ${ checklist_items [@] } " \
2>& 1 1>& 3)
2025-02-24 23:44:47 +01:00
2025-05-28 23:26:58 +02:00
local dialog_exit = $?
exec 3>& -
2025-02-24 23:44:47 +01:00
2025-05-28 23:26:58 +02:00
if [ [ $dialog_exit -ne 0 || -z " $selected_indices " ] ] ; then
2025-05-02 19:11:17 +02:00
exit 0
fi
2025-02-24 23:44:47 +01:00
2025-05-07 18:40:39 +02:00
2025-02-24 23:44:47 +01:00
2025-05-28 23:26:58 +02:00
declare -A selected_functions
read -ra indices_array <<< " $selected_indices "
2025-05-07 18:40:39 +02:00
2025-05-28 23:26:58 +02:00
for index in " ${ indices_array [@] } " ; do
if [ [ -z " $index " ] ] || ! [ [ " $index " = ~ ^[ 0-9] +$ ] ] ; then
continue
fi
2025-02-24 23:44:47 +01:00
2025-05-28 23:26:58 +02:00
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
2025-02-24 23:44:47 +01:00
2025-05-28 23:26:58 +02:00
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
2025-02-24 23:44:47 +01:00
2025-05-02 19:11:17 +02:00
2025-05-28 23:26:58 +02:00
clear
show_proxmenux_logo
msg_title " $SCRIPT_TITLE "
2025-05-02 19:11:17 +02:00
2025-05-28 23:26:58 +02:00
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
2025-02-24 23:44:47 +01:00
2025-05-28 23:26:58 +02:00
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
2025-02-24 23:44:47 +01:00
else
2025-05-28 23:26:58 +02:00
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
2025-02-24 23:44:47 +01:00
fi
fi
2025-05-28 23:26:58 +02:00
msg_success " $( translate "All changes applied. No reboot required." ) "
msg_success " $( translate "Press Enter to return to menu..." ) "
read -r
clear
2025-02-24 23:44:47 +01:00
}
2025-02-25 19:49:05 +01:00
2025-05-22 19:04:41 +02:00
2025-02-24 23:44:47 +01:00
main_menu