ProxMenux/scripts/customizable_post_install.sh

2375 lines
79 KiB
Bash
Raw Normal View History

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)
# Version : 1.0
# Last Updated: 24/02/2025
# ==========================================================
# Description:
# This script automates post-installation configurations and optimizations
# for Proxmox Virtual Environment (VE). It allows for a variety of system
# customizations, including kernel optimizations, memory management, network
# tweaks, and virtualization environment adjustments. The script facilitates
# easy installation of useful tools and security enhancements, including
# fail2ban, ZFS auto-snapshot, and more.
#
# This script is based on the work of Adrian Jon Kriel from eXtremeSHOK.com,
# and it was originally published as a post-installation script for Proxmox under the
# BSD License.
#
# Copyright (c) Adrian Jon Kriel :: admin@extremeshok.com
# Script updates can be found at: https://github.com/extremeshok/xshok-proxmox
#
# License: BSD (Berkeley Software Distribution)
#
# Key features:
# - Configures system memory and kernel settings for better performance.
# - Enables IOMMU and VFIO for PCI passthrough and virtualization optimizations.
# - Installs essential tools such as kernel headers, system utilities, and networking tools.
# - Optimizes journald, achievement, and other system services for better efficiency.
# - Enables guest agents for virtualization platforms such as KVM, VMware, and VirtualBox.
# - Updates the system, adds correct repositories, and optimizes system features such as memory, network settings, and more.
# - Provides a wide range of additional options for customization and optimization.
# - Offers interactive selection of features using an easy-to-use menu-driven interface.
# - And many more...
#
# ==========================================================
# Configuration
REPO_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main"
BASE_DIR="/usr/local/share/proxmenux"
UTILS_FILE="$BASE_DIR/utils.sh"
VENV_PATH="/opt/googletrans-env"
if [[ -f "$UTILS_FILE" ]]; then
source "$UTILS_FILE"
fi
load_language
initialize_cache
# ==========================================================
# VARIBLES
OS_CODENAME="$(grep "VERSION_CODENAME=" /etc/os-release | cut -d"=" -f 2 | xargs )"
RAM_SIZE_GB=$(( $(vmstat -s | grep -i "total memory" | xargs | cut -d" " -f 1) / 1024 / 1000))
NECESSARY_REBOOT=0
SCRIPT_TITLE="Customizable script settings for post-installation of Proxmox"
# ==========================================================
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
# Add alias for reboot-quick
if ! grep -q "alias reboot-quick='systemctl kexec'" /root/.bash_profile; then
echo "alias reboot-quick='systemctl kexec'" >> /root/.bash_profile
msg_ok "$(translate "reboot-quick alias added")"
else
msg_ok "$(translate "reboot-quick alias added")"
fi
msg_success "$(translate "kexec configured successfully. Use the command: reboot-quick")"
}
# ==========================================================
apt_upgrade() {
msg_info2 "$(translate "Configuring Proxmox repositories")"
NECESSARY_REBOOT=1
# Disable enterprise proxmox repo
if [ -f /etc/apt/sources.list.d/pve-enterprise.list ] && grep -q "^deb" /etc/apt/sources.list.d/pve-enterprise.list; then
msg_info "$(translate "Disabling enterprise Proxmox repository...")"
sed -i "s/^deb/#deb/g" /etc/apt/sources.list.d/pve-enterprise.list
msg_ok "$(translate "Enterprise Proxmox repository disabled")"
fi
# Disable enterprise proxmox ceph repo
if [ -f /etc/apt/sources.list.d/ceph.list ] && grep -q "^deb" /etc/apt/sources.list.d/ceph.list; then
msg_info "$(translate "Disabling enterprise Proxmox Ceph repository...")"
sed -i "s/^deb/#deb/g" /etc/apt/sources.list.d/ceph.list
msg_ok "$(translate "Enterprise Proxmox Ceph repository disabled")"
fi
# Enable free public proxmox repo
if [ ! -f /etc/apt/sources.list.d/pve-public-repo.list ] || ! grep -q "pve-no-subscription" /etc/apt/sources.list.d/pve-public-repo.list; then
msg_info "$(translate "Enabling free public Proxmox repository...")"
echo -e "deb http://download.proxmox.com/debian/pve ${OS_CODENAME} pve-no-subscription\\n" > /etc/apt/sources.list.d/pve-public-repo.list
msg_ok "$(translate "Free public Proxmox repository enabled")"
fi
# Enable Proxmox testing repository
if [ ! -f /etc/apt/sources.list.d/pve-testing-repo.list ] || ! grep -q "pvetest" /etc/apt/sources.list.d/pve-testing-repo.list; then
msg_info "$(translate "Enabling Proxmox testing repository...")"
echo -e "deb http://download.proxmox.com/debian/pve ${OS_CODENAME} pvetest\\n" > /etc/apt/sources.list.d/pve-testing-repo.list
msg_ok "$(translate "Proxmox testing repository enabled")"
fi
# Configure main Debian repositories
if ! grep -q "${OS_CODENAME}-security" /etc/apt/sources.list; then
msg_info "$(translate "Configuring main Debian repositories...")"
cat <<EOF > /etc/apt/sources.list
deb http://deb.debian.org/debian ${OS_CODENAME} main contrib non-free non-free-firmware
deb http://deb.debian.org/debian ${OS_CODENAME}-updates main contrib non-free non-free-firmware
deb http://security.debian.org/debian-security ${OS_CODENAME}-security main contrib non-free non-free-firmware
EOF
msg_ok "$(translate "Main Debian repositories configured")"
fi
# Disable non-free firmware warnings
if [ ! -f /etc/apt/apt.conf.d/no-bookworm-firmware.conf ]; then
msg_info "$(translate "Disabling non-free firmware warnings...")"
echo 'APT::Get::Update::SourceListWarnings::NonFreeFirmware "false";' > /etc/apt/apt.conf.d/no-bookworm-firmware.conf
msg_ok "$(translate "Non-free firmware warnings disabled")"
fi
# Update package lists
msg_info "$(translate "Updating package lists...")"
if apt-get update > /dev/null 2>&1; then
msg_ok "$(translate "Package lists updated")"
else
msg_error "$(translate "Failed to update package lists")"
return 1
fi
# Remove conflicting utilities
msg_info "$(translate "Removing conflicting utilities...")"
if /usr/bin/env DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::='--force-confdef' purge ntp openntpd systemd-timesyncd > /dev/null 2>&1; then
msg_ok "$(translate "Conflicting utilities removed")"
else
msg_error "$(translate "Failed to remove conflicting utilities")"
fi
# update proxmox and install system utils
msg_info "$(translate "Performing system upgrade...")"
apt-get install pv -y > /dev/null 2>&1
total_packages=$(apt-get -s dist-upgrade | grep "^Inst" | wc -l)
if [ "$total_packages" -eq 0 ]; then
total_packages=1
fi
upgraded_packages=0
(
/usr/bin/env DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::='--force-confdef' dist-upgrade 2>&1 | \
while IFS= read -r line; do
if [[ $line == "Setting up"* ]] || [[ $line == "Unpacking"* ]]; then
((upgraded_packages++))
progress=$((upgraded_packages * 100 / total_packages))
printf "\r$(translate "Progress"): [%-50s] %3d%%" $(printf "#%.0s" $(seq 1 $((progress/2)))) $progress
fi
done
)
if [ $? -eq 0 ]; then
printf "\r%-$(($(tput cols)-1))s\r" " "
msg_ok "$(translate "System upgrade completed")"
fi
# update PVE application manager
msg_info "$(translate "Updating PVE application manager, patience...")"
total_steps=$(pveam update 2>&1 | grep -E "^(Downloading|Importing)" | wc -l)
[ $total_steps -eq 0 ] && total_steps=1
current_step=0
(
pveam update 2>&1 | while IFS= read -r line; do
if [[ $line == "Downloading"* ]] || [[ $line == "Importing"* ]]; then
((current_step++))
progress=$((current_step * 100 / total_steps))
printf "\r$(translate "Progress"): [%-50s] %3d%%" $(printf "#%.0s" $(seq 1 $((progress/2)))) $progress
fi
done
)
if [ $? -eq 0 ]; then
printf "\r%-$(($(tput cols)-1))s\r" " "
msg_ok "$(translate "PVE application manager updated")"
fi
# Install additional Proxmox packages
msg_info "$(translate "Installing additional Proxmox packages...")"
if /usr/bin/env DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::='--force-confdef' install zfsutils-linux proxmox-backup-restore-image chrony > /dev/null 2>&1; then
msg_ok "$(translate "Additional Proxmox packages installed")"
else
msg_error "$(translate "Failed to install additional Proxmox packages")"
fi
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() {
msg_info2 "$(translate "Installing common system utilities...")"
# List of packages to install
packages=(
axel curl dialog dnsutils dos2unix gnupg-agent grc htop btop iftop iotop
iperf3 ipset iptraf-ng mlocate msr-tools nano net-tools omping
software-properties-common sshpass tmux unzip vim vim-nox wget whois zip
libguestfs-tools
)
total_packages=${#packages[@]}
installed_packages=0
packages_to_install=()
# Check which packages need to be installed
for package in "${packages[@]}"; do
if ! dpkg -s "$package" >/dev/null 2>&1; then
packages_to_install+=("$package")
else
((installed_packages++))
fi
done
if [ ${#packages_to_install[@]} -eq 0 ]; then
msg_ok "$(translate "Missing system utilities installed successfully")"
else
msg_info "$(translate "Installing missing system utilities...")"
(
for package in "${packages_to_install[@]}"; do
/usr/bin/env DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::='--force-confdef' install "$package" > /dev/null 2>&1
((installed_packages++))
progress=$((installed_packages * 100 / total_packages))
printf "\r$(translate "Progress"): [%-50s] %3d%%" $(printf "#%.0s" $(seq 1 $((progress/2)))) $progress
done
)
if [ $? -eq 0 ]; then
printf "\r%-$(($(tput cols)-1))s\r" " "
msg_ok "$(translate "Missing system utilities installed successfully")"
fi
fi
msg_success "$(translate "Common system utilities installation completed")"
}
# ==========================================================
configure_entropy() {
msg_info2 "$(translate "Configuring entropy generation to prevent slowdowns...")"
# Install haveged
msg_info "$(translate "Installing haveged...")"
/usr/bin/env DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::='--force-confdef' install haveged > /dev/null 2>&1
msg_ok "$(translate "haveged installed successfully")"
# Configure haveged
msg_info "$(translate "Configuring haveged...")"
cat <<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() {
msg_info2 "$(translate "Installing and configuring fail2ban to protect the web interface...")"
# Install fail2ban
if ! dpkg -s fail2ban >/dev/null 2>&1; then
msg_info "$(translate "Installing fail2ban...")"
if /usr/bin/env DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::='--force-confdef' install fail2ban > /dev/null 2>&1; then
msg_ok "$(translate "fail2ban installed successfully")"
else
msg_error "$(translate "Failed to install fail2ban")"
return 1
fi
else
msg_ok "$(translate "fail2ban installed successfully")"
fi
# Configure Proxmox filter
msg_info "$(translate "Configuring Proxmox filter for fail2ban...")"
local proxmox_filter="/etc/fail2ban/filter.d/proxmox.conf"
if [ ! -f "$proxmox_filter" ] || ! grep -q "pvedaemon\[.*authentication failure" "$proxmox_filter"; then
cat <<EOF > "$proxmox_filter"
[Definition]
failregex = pvedaemon\[.*authentication failure; rhost=<HOST> user=.* msg=.*
ignoreregex =
EOF
msg_ok "$(translate "Proxmox filter configured")"
else
msg_ok "$(translate "Proxmox filter configured")"
fi
# Configure Proxmox jail
msg_info "$(translate "Configuring Proxmox jail for fail2ban...")"
local proxmox_jail="/etc/fail2ban/jail.d/proxmox.conf"
if [ ! -f "$proxmox_jail" ] || ! grep -q "\[proxmox\]" "$proxmox_jail"; then
cat <<EOF > "$proxmox_jail"
[proxmox]
enabled = true
port = https,http,8006,8007
filter = proxmox
logpath = /var/log/daemon.log
maxretry = 3
# 1 hour
bantime = 3600
findtime = 600
EOF
msg_ok "$(translate "Proxmox jail configured")"
else
msg_ok "$(translate "Proxmox jail configured")"
fi
# Configure general fail2ban settings
msg_info "$(translate "Configuring general fail2ban settings...")"
local jail_local="/etc/fail2ban/jail.local"
if [ ! -f "$jail_local" ] || ! grep -q "\[DEFAULT\]" "$jail_local"; then
cat <<EOF > "$jail_local"
[DEFAULT]
ignoreip = 127.0.0.1
bantime = 86400
maxretry = 2
findtime = 1800
[ssh-iptables]
enabled = true
filter = sshd
action = iptables[name=SSH, port=ssh, protocol=tcp]
logpath = /var/log/auth.log
maxretry = 2
findtime = 3600
bantime = 32400
EOF
msg_ok "$(translate "General fail2ban settings configured")"
else
msg_ok "$(translate "General fail2ban settings configured")"
fi
# Enable fail2ban service
msg_info "$(translate "Enabling fail2ban service...")"
if systemctl is-enabled fail2ban >/dev/null 2>&1; then
msg_ok "$(translate "fail2ban service enabled")"
else
if systemctl enable fail2ban > /dev/null 2>&1; then
msg_ok "$(translate "fail2ban service enabled")"
else
msg_error "$(translate "Failed to enable fail2ban service")"
return 1
fi
fi
# Test fail2ban configuration
msg_info "$(translate "Testing fail2ban configuration...")"
if fail2ban-regex /var/log/daemon.log /etc/fail2ban/filter.d/proxmox.conf > /dev/null 2>&1; then
msg_ok "$(translate "fail2ban configuration test passed")"
else
msg_warn "$(translate "fail2ban configuration test failed. Please check the configuration manually.")"
fi
# Restart fail2ban to apply changes
msg_info "$(translate "Restarting fail2ban service...")"
if systemctl restart fail2ban > /dev/null 2>&1; then
msg_ok "$(translate "fail2ban service restarted successfully")"
else
msg_error "$(translate "Failed to restart fail2ban service")"
return 1
fi
msg_success "$(translate "fail2ban installation and configuration completed")"
}
# ==========================================================
install_lynis() {
msg_info2 "$(translate "Installing Lynis security scan tool...")"
# Install Lynis directly from Debian repositories
msg_info "$(translate "Installing Lynis packages...")"
(
/usr/bin/env DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::='--force-confdef' install lynis 2>&1 | \
while IFS= read -r line; do
if [[ $line == *"Installing"* ]] || [[ $line == *"Unpacking"* ]]; then
printf "\r%-$(($(tput cols)-1))s\r" " " # Clear current line
printf "\r%s" "$line"
fi
done
)
if [ $? -eq 0 ]; then
printf "\r%-$(($(tput cols)-1))s\r" " " # Clear final line
msg_ok "$(translate "Lynis installed successfully")"
else
printf "\r%-$(($(tput cols)-1))s\r" " " # Clear final line
msg_warn "$(translate "Failed to install Lynis")"
fi
# Verify installation
if command -v lynis >/dev/null 2>&1; then
msg_success "$(translate "Lynis is ready to use")"
else
msg_warn "$(translate "Lynis installation could not be verified")"
fi
}
# ==========================================================
install_guest_agent() {
msg_info2 "$(translate "Detecting virtualization and installing guest agent...")"
NECESSARY_REBOOT=1
local virt_env=""
local guest_agent=""
# Detect virtualization environment
if [ "$(dmidecode -s system-manufacturer | xargs)" == "QEMU" ] || [ "$(systemd-detect-virt | xargs)" == "kvm" ]; then
virt_env="QEMU/KVM"
guest_agent="qemu-guest-agent"
elif [ "$(systemd-detect-virt | xargs)" == "vmware" ]; then
virt_env="VMware"
guest_agent="open-vm-tools"
elif [ "$(systemd-detect-virt | xargs)" == "oracle" ]; then
virt_env="VirtualBox"
guest_agent="virtualbox-guest-utils"
else
msg_ok "$(translate "Guest agent detection completed")"
msg_success "$(translate "Guest agent installation process completed")"
return
fi
# Install guest agent
if [ -n "$guest_agent" ]; then
msg_info "$(translate "Installing $guest_agent for $virt_env...")"
if /usr/bin/env DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::='--force-confdef' install $guest_agent > /dev/null 2>&1; then
msg_ok "$(translate "$guest_agent installed successfully")"
else
msg_error "$(translate "Failed to install $guest_agent")"
fi
fi
msg_success "$(translate "Guest agent installation process completed")"
}
# ==========================================================
configure_ksmtuned() {
msg_info2 "$(translate "Installing and configuring KSM (Kernel Samepage Merging) daemon...")"
NECESSARY_REBOOT=1
# Install ksm-control-daemon
msg_info "$(translate "Installing ksm-control-daemon...")"
if /usr/bin/env DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::='--force-confdef' install ksm-control-daemon > /dev/null 2>&1; then
msg_ok "$(translate "ksm-control-daemon installed successfully")"
fi
# Determine RAM size and set KSM parameters
if [[ RAM_SIZE_GB -le 16 ]]; then
KSM_THRES_COEF=50
KSM_SLEEP_MSEC=80
msg_info "$(translate "RAM <= 16GB: Setting KSM to start at 50% full")"
elif [[ RAM_SIZE_GB -le 32 ]]; then
KSM_THRES_COEF=40
KSM_SLEEP_MSEC=60
msg_info "$(translate "RAM <= 32GB: Setting KSM to start at 60% full")"
elif [[ RAM_SIZE_GB -le 64 ]]; then
KSM_THRES_COEF=30
KSM_SLEEP_MSEC=40
msg_info "$(translate "RAM <= 64GB: Setting KSM to start at 70% full")"
elif [[ RAM_SIZE_GB -le 128 ]]; then
KSM_THRES_COEF=20
KSM_SLEEP_MSEC=20
msg_info "$(translate "RAM <= 128GB: Setting KSM to start at 80% full")"
else
KSM_THRES_COEF=10
KSM_SLEEP_MSEC=10
msg_info "$(translate "RAM > 128GB: Setting KSM to start at 90% full")"
fi
# Update ksmtuned configuration
if sed -i -e "s/\# KSM_THRES_COEF=.*/KSM_THRES_COEF=${KSM_THRES_COEF}/g" /etc/ksmtuned.conf && \
sed -i -e "s/\# KSM_SLEEP_MSEC=.*/KSM_SLEEP_MSEC=${KSM_SLEEP_MSEC}/g" /etc/ksmtuned.conf; then
msg_ok "$(translate "ksmtuned configuration updated successfully")"
fi
# Enable ksmtuned service
if systemctl enable ksmtuned > /dev/null 2>&1; then
msg_ok "$(translate "ksmtuned service enabled successfully")"
fi
msg_success "$(translate "KSM configuration completed")"
}
# ==========================================================
enable_vfio_iommu() {
msg_info2 "$(translate "Enabling IOMMU and configuring VFIO for PCI passthrough...")"
NECESSARY_REBOOT=1
# Detect if system uses ZFS
local uses_zfs=false
local cmdline_file="/etc/kernel/cmdline"
if [[ -f "$cmdline_file" && $(grep -q "root=ZFS=" "$cmdline_file") ]]; then
uses_zfs=true
fi
# Enable IOMMU
local cpu_info=$(cat /proc/cpuinfo)
local grub_file="/etc/default/grub"
local iommu_param=""
local additional_params="pcie_acs_override=downstream,multifunction nofb nomodeset video=vesafb:off,efifb:off"
if [[ "$cpu_info" == *"GenuineIntel"* ]]; then
msg_info "$(translate "Detected Intel CPU")"
iommu_param="intel_iommu=on iommu=pt"
elif [[ "$cpu_info" == *"AuthenticAMD"* ]]; then
msg_info "$(translate "Detected AMD CPU")"
iommu_param="amd_iommu=on iommu=pt"
else
msg_warning "$(translate "Unknown CPU type. IOMMU might not be properly enabled.")"
return 1
fi
if [[ "$uses_zfs" == true ]]; then
if grep -q "$iommu_param" "$cmdline_file" && grep -q "$additional_params" "$cmdline_file"; then
msg_ok "$(translate "IOMMU and additional parameters already configured for ZFS")"
else
cp "$cmdline_file" "${cmdline_file}.bak"
sed -i "/^.*root=ZFS=/ s|$| $iommu_param $additional_params|" "$cmdline_file"
msg_ok "$(translate "IOMMU and additional parameters added for ZFS")"
fi
else
if grep -q "$iommu_param" "$grub_file"; then
msg_ok "$(translate "IOMMU enabled in GRUB configuration")"
else
cp "$grub_file" "${grub_file}.bak"
sed -i "/GRUB_CMDLINE_LINUX_DEFAULT=/ s|\"$| $iommu_param\"|" "$grub_file"
msg_ok "$(translate "IOMMU enabled in GRUB configuration")"
fi
fi
# Configure VFIO modules (avoid duplicates)
local modules_file="/etc/modules"
msg_info "$(translate "Checking VFIO modules...")"
local vfio_modules=("vfio" "vfio_iommu_type1" "vfio_pci" "vfio_virqfd")
for module in "${vfio_modules[@]}"; do
grep -q "^$module" "$modules_file" || echo "$module" >> "$modules_file"
done
msg_ok "$(translate "VFIO modules configured.)")"
# Blacklist conflicting drivers (avoid duplicates)
local blacklist_file="/etc/modprobe.d/blacklist.conf"
msg_info "$(translate "Checking conflicting drivers blacklist...")"
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"
local custom_message="This system is optimised by: ProxMenux"
local changes_made=false
msg_info "$(translate "Checking MOTD configuration...")"
# Check if the custom message already exists
if grep -q "$custom_message" "$motd_file"; then
msg_ok "$(translate "Custom message added to MOTD")"
else
# Create a backup of the original MOTD file
if [ ! -f "${motd_file}.bak" ]; then
cp "$motd_file" "${motd_file}.bak"
msg_ok "$(translate "Backup of original MOTD created")"
fi
# Add the custom message at the beginning of the file
echo -e "$custom_message\n\n$(cat $motd_file)" > "$motd_file"
changes_made=true
msg_ok "$(translate "Custom message added to MOTD")"
fi
sed -i '/^$/N;/^\n$/D' "$motd_file"
if $changes_made; then
msg_success "$(translate "MOTD configuration updated successfully")"
else
msg_success "$(translate "MOTD configuration updated successfully")"
fi
}
# ==========================================================
optimize_logrotate() {
msg_info2 "$(translate "Optimizing logrotate configuration...")"
local logrotate_conf="/etc/logrotate.conf"
local backup_conf="${logrotate_conf}.bak"
if grep -q "# ProxMenux optimized configuration" "$logrotate_conf"; then
msg_ok "$(translate "Logrotate configuration already optimized.")"
else
cp "$logrotate_conf" "$backup_conf"
msg_info "$(translate "Applying optimized logrotate configuration...")"
cat <<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"
# Check if the configuration file already exists and has the correct content
if [ -f "$sysctl_conf" ] && \
grep -q "Memory Optimising" "$sysctl_conf" && \
grep -q "vm.min_free_kbytes = 1048576" "$sysctl_conf" && \
grep -q "vm.nr_hugepages = 2000" "$sysctl_conf" && \
grep -q "vm.max_map_count = 1048576" "$sysctl_conf" && \
grep -q "vm.overcommit_memory = 1" "$sysctl_conf"; then
msg_ok "$(translate "Memory settings already optimized")"
else
msg_info "$(translate "Applying memory optimization settings...")"
# Create or update the configuration file
cat <<EOF > "$sysctl_conf"
# Memory Optimising
## Bugfix: reserve 1024MB memory for system
vm.min_free_kbytes = 1048576
vm.nr_hugepages = 2000
# (Redis/MongoDB)
vm.max_map_count = 1048576
vm.overcommit_memory = 1
EOF
msg_ok "$(translate "Memory settings optimized successfully")"
fi
msg_success "$(translate "Memory optimization completed")"
}
# ==========================================================
optimize_vzdump() {
msg_info2 "$(translate "Optimizing vzdump backup speed...")"
local vzdump_conf="/etc/vzdump.conf"
# Configure bandwidth limit
msg_info "$(translate "Configuring bandwidth limit for vzdump...")"
if ! grep -q "^bwlimit: 0" "$vzdump_conf"; then
sed -i '/^#*bwlimit:/d' "$vzdump_conf"
echo "bwlimit: 0" >> "$vzdump_conf"
fi
msg_ok "$(translate "Bandwidth limit configured")"
# Configure I/O priority
msg_info "$(translate "Configuring I/O priority for vzdump...")"
if ! grep -q "^ionice: 5" "$vzdump_conf"; then
sed -i '/^#*ionice:/d' "$vzdump_conf"
echo "ionice: 5" >> "$vzdump_conf"
fi
msg_ok "$(translate "I/O priority configured")"
msg_success "$(translate "vzdump backup speed optimization completed")"
}
# ==========================================================
install_ovh_rtm() {
msg_info2 "$(translate "Detecting if this is an OVH server and installing OVH RTM if necessary...")"
# Get the public IP and check if it belongs to OVH
msg_info "$(translate "Checking if the server belongs to OVH...")"
public_ip=$(curl -s ipinfo.io/ip)
is_ovh=$(whois -h v4.whois.cymru.com " -t $public_ip" | tail -n 1 | cut -d'|' -f3 | grep -i "ovh")
if [ -n "$is_ovh" ]; then
msg_ok "$(translate "OVH server detected")"
msg_info "$(translate "Installing OVH RTM (Real Time Monitoring)...")"
if wget -qO - https://last-public-ovh-infra-yak.snap.mirrors.ovh.net/yak/archives/apply.sh | OVH_PUPPET_MANIFEST=distribyak/catalog/master/puppet/manifests/common/rtmv2.pp bash > /dev/null 2>&1; then
msg_ok "$(translate "OVH RTM installed successfully")"
else
msg_error "$(translate "Failed to install OVH RTM")"
fi
fi
msg_ok "$(translate "Server belongs to OVH")"
msg_success "$(translate "OVH server detection and RTM installation process completed")"
}
# ==========================================================
enable_ha() {
msg_info2 "$(translate "Enabling High Availability (HA) services...")"
NECESSARY_REBOOT=1
msg_info "$(translate "Enabling High Availability (HA) services...")"
# Enable all necessary services
systemctl enable -q --now pve-ha-lrm pve-ha-crm corosync &>/dev/null
msg_ok "$(translate "High Availability services have been enabled successfully")"
msg_success "$(translate "High Availability setup completed")"
}
# ==========================================================
configure_fastfetch() {
msg_info2 "$(translate "Installing and configuring Fastfetch...")"
# 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"
# Install Fastfetch if not already installed
if ! command -v fastfetch &> /dev/null; then
msg_info "$(translate "Downloading and installing Fastfetch...")"
wget -qO "$fastfetch_bin" "https://github.com/fastfetch-cli/fastfetch/releases/latest/download/fastfetch-linux-amd64"
chmod +x "$fastfetch_bin"
msg_ok "$(translate "Fastfetch installed successfully")"
else
msg_ok "$(translate "Fastfetch is already installed")"
fi
while true; do
# Define logo options
local logo_options=("ProxMenux" "Proxmox (default)" "Comunidad Helper-Scripts" "Home-Labs-Club" "Proxmology" "Custom")
local choice
choice=$(whiptail --title "$(translate "Fastfetch Logo Selection")" --menu "$(translate "Choose a logo for Fastfetch:")" 20 78 6 \
"1" "${logo_options[0]}" \
"2" "${logo_options[1]}" \
"3" "${logo_options[2]}" \
"4" "${logo_options[3]}" \
"5" "${logo_options[4]}" \
"6" "${logo_options[5]}" \
3>&1 1>&2 2>&3)
case $choice in
1)
msg_info "$(translate "Downloading ProxMenux logo...")"
local proxmenux_logo_path="$logos_dir/ProxMenux.txt"
if wget -qO "$proxmenux_logo_path" "https://raw.githubusercontent.com/MacRimi/ProxMenux/main/images/logos_txt/ProxMenux.txt"; then
jq --arg path "$proxmenux_logo_path" '. + {logo: $path}' "$fastfetch_config" > "${fastfetch_config}.tmp" && mv "${fastfetch_config}.tmp" "$fastfetch_config"
msg_ok "$(translate "ProxMenux logo applied")"
else
msg_error "$(translate "Failed to download ProxMenux logo")"
fi
break
;;
2)
msg_info "$(translate "Using default Proxmox logo...")"
jq 'del(.logo)' "$fastfetch_config" > "${fastfetch_config}.tmp" && mv "${fastfetch_config}.tmp" "$fastfetch_config"
msg_ok "$(translate "Default Proxmox logo applied")"
break
;;
3)
msg_info "$(translate "Downloading Helper-Scripts logo...")"
local helper_scripts_logo_path="$logos_dir/Helper_Scripts.txt"
if wget -qO "$helper_scripts_logo_path" "https://raw.githubusercontent.com/MacRimi/ProxMenux/main/images/logos_txt/Helper_Scripts.txt"; then
jq --arg path "$helper_scripts_logo_path" '. + {logo: $path}' "$fastfetch_config" > "${fastfetch_config}.tmp" && mv "${fastfetch_config}.tmp" "$fastfetch_config"
msg_ok "$(translate "Helper-Scripts logo applied")"
else
msg_error "$(translate "Failed to download Helper-Scripts logo")"
fi
break
;;
4)
msg_info "$(translate "Downloading Home-Labs-Club logo...")"
local home_lab_club_logo_path="$logos_dir/home_labsclub.txt"
if wget -qO "$home_lab_club_logo_path" "https://raw.githubusercontent.com/MacRimi/ProxMenux/main/images/logos_txt/home_labsclub.txt"; then
jq --arg path "$home_lab_club_logo_path" '. + {logo: $path}' "$fastfetch_config" > "${fastfetch_config}.tmp" && mv "${fastfetch_config}.tmp" "$fastfetch_config"
msg_ok "$(translate "Home-Lab-Club logo applied")"
else
msg_error "$(translate "Failed to download Home-Lab-Club logo")"
fi
break
;;
5)
msg_info "$(translate "Downloading Proxmology logo...")"
local proxmology_logo_path="$logos_dir/proxmology.txt"
if wget -qO "$proxmology_logo_path" "https://raw.githubusercontent.com/MacRimi/ProxMenux/main/images/logos_txt/proxmology.txt"; then
jq --arg path "$proxmology_logo_path" '. + {logo: $path}' "$fastfetch_config" > "${fastfetch_config}.tmp" && mv "${fastfetch_config}.tmp" "$fastfetch_config"
msg_ok "$(translate "Proxmology logo applied")"
else
msg_error "$(translate "Failed to download Proxmology logo")"
fi
break
;;
6)
whiptail --title "$(translate "Custom Logo Instructions")" --msgbox "$(translate "To use a custom Fastfetch logo, place your ASCII logo file in:\n\n/usr/local/share/fastfetch/logos/\n\nThe file should not exceed 35 lines to fit properly in the terminal.\n\nPress OK to continue and select your logo.")" 15 70
local logo_files=($(ls "$logos_dir"/*.txt 2>/dev/null))
if [ ${#logo_files[@]} -eq 0 ]; then
whiptail --title "$(translate "No Custom Logos Found")" --msgbox "$(translate "No custom logos were found in /usr/local/share/fastfetch/logos/.\n\nPlease add a logo and try again.")" 10 60
continue
fi
local menu_items=()
local index=1
for file in "${logo_files[@]}"; do
menu_items+=("$index" "$(basename "$file")")
index=$((index+1))
done
local selected_logo_index
selected_logo_index=$(whiptail --title "$(translate "Select a Custom Logo")" --menu "$(translate "Choose a custom logo:")" 20 70 10 "${menu_items[@]}" 3>&1 1>&2 2>&3)
if [ -z "$selected_logo_index" ]; then
continue
fi
local selected_logo="${logo_files[$((selected_logo_index-1))]}"
jq --arg path "$selected_logo" '. + {logo: $path}' "$fastfetch_config" > "${fastfetch_config}.tmp" && mv "${fastfetch_config}.tmp" "$fastfetch_config"
msg_ok "$(translate "Custom logo applied: $(basename "$selected_logo")")"
break
;;
*)
msg_warn "$(translate "You must select a logo to continue.")"
;;
esac
done
# Modify Fastfetch modules to display custom title
msg_info "$(translate "Modifying Fastfetch configuration...")"
# Eliminar "title" si existe en la configuración
jq '.modules |= map(select(. != "title"))' ~/.config/fastfetch/config.jsonc > ~/.config/fastfetch/config.jsonc.tmp && mv ~/.config/fastfetch/config.jsonc.tmp ~/.config/fastfetch/config.jsonc
# Asegurar que solo haya una entrada "custom"
jq 'del(.modules[] | select(type == "object" and .type == "custom"))' ~/.config/fastfetch/config.jsonc > ~/.config/fastfetch/config.jsonc.tmp && mv ~/.config/fastfetch/config.jsonc.tmp ~/.config/fastfetch/config.jsonc
# Agregar la entrada "custom" al inicio de los módulos si no existe
jq '.modules |= [{"type": "custom", "format": "\u001b[1;38;5;166mSystem optimised by ProxMenux\u001b[0m"}] + .' ~/.config/fastfetch/config.jsonc > ~/.config/fastfetch/config.jsonc.tmp && mv ~/.config/fastfetch/config.jsonc.tmp ~/.config/fastfetch/config.jsonc
msg_ok "$(translate "Fastfetch now displays: System optimised by: ProxMenux")"
# Regenerar configuración (evita que Fastfetch sobrescriba cambios)
fastfetch --gen-config > /dev/null 2>&1
msg_ok "$(translate "Fastfetch configuration updated")"
# Eliminar instancias previas de Fastfetch en bashrc y perfiles
sed -i '/fastfetch/d' ~/.bashrc ~/.profile /etc/profile
rm -f /etc/update-motd.d/99-fastfetch
# Agregar Fastfetch a ~/.bashrc para que se ejecute en cada inicio de sesión
echo "clear && fastfetch" >> ~/.bashrc
msg_ok "$(translate "Fastfetch will start automatically in the console")"
msg_success "$(translate "Fastfetch installation and configuration completed")"
}
# ==========================================================
# Main menu function
main_menu() {
local HEADER=$(printf " %-56s %10s" "$(translate "Description")" "$(translate "Category")")
# Define category order
declare -A category_order
category_order["Basic Settings"]=1
category_order["System"]=2
category_order["Hardware"]=3
category_order["Virtualization"]=4
category_order["Network"]=5
category_order["Storage"]=6
category_order["Security"]=7
category_order["Customization"]=8
category_order["Monitoring"]=9
category_order["Performance"]=10
category_order["Optional"]=11
# Define options with categories
local options=(
"Basic Settings|Update and upgrade system|APTUPGRADE"
"Basic Settings|Synchronize time automatically|TIMESYNC"
"Basic Settings|Skip downloading additional languages|NOAPTLANG"
"Basic Settings|Install common system utilities|UTILS"
"System|Optimize journald|JOURNALD"
"System|Optimize logrotate|LOGROTATE"
"System|Increase various system limits|LIMITS"
"System|Ensure entropy pools are populated|ENTROPY"
"System|Optimize Memory|MEMORYFIXES"
"System|Enable fast reboots|KEXEC"
"System|Enable restart on kernel panic|KERNELPANIC"
"System|Install kernel headers|KERNELHEADERS"
"Optional|Apply AMD CPU fixes|AMDFIXES"
"Virtualization|Install relevant guest agent|GUESTAGENT"
"Virtualization|Enable VFIO IOMMU support|VFIO_IOMMU"
"Virtualization|KSM control daemon|KSMTUNED"
"Network|Force APT to use IPv4|APTIPV4"
"Network|Apply network optimizations|NET"
"Network|Install Open vSwitch|OPENVSWITCH"
"Network|Enable TCP BBR/Fast Open control|TCPFASTOPEN"
"Storage|Optimize ZFS ARC size|ZFSARC"
"Storage|Install ZFS auto-snapshot|ZFSAUTOSNAPSHOT"
"Storage|Increase vzdump backup speed|VZDUMP"
"Security|Disable portmapper/rpcbind|DISABLERPC"
"Security|Protect web interface with fail2ban|FAIL2BAN"
"Security|Install Lynis security tool|LYNIS"
"Customization|Customize bashrc|BASHRC"
"Customization|Set up custom MOTD banner|MOTD"
"Customization|Remove subscription banner|NOSUBBANNER"
"Monitoring|Install OVH Real Time Monitoring|OVHRTM"
"Performance|Use pigz for faster gzip compression|PIGZ"
"Optional|Install and configure Fastfetch|FASTFETCH"
"Optional|Add latest Ceph support|CEPH"
"Optional|Enable High Availability services|ENABLE_HA"
)
# Sort options based on category order
IFS=$'\n' sorted_options=($(for option in "${options[@]}"; do
IFS='|' read -r category description function_name <<< "$option"
printf "%d|%s|%s|%s\n" "${category_order[$category]:-999}" "$category" "$description" "$function_name"
done | sort -n | cut -d'|' -f2-))
unset IFS
local menu_items=()
local i=1
local previous_category=""
for option in "${sorted_options[@]}"; do
IFS='|' read -r category description function_name <<< "$option"
translated_category=$(translate "$category")
translated_description=$(translate "$description")
# Set ON for all categories except Optional
state="ON"
if [ "$category" = "Optional" ]; then
state="OFF"
fi
# Add a separator before Optional category, but only once
if [ "$category" != "$previous_category" ] && [ "$category" = "Optional" ] && [ "$previous_category" != "" ]; then
menu_items+=("" "================================================================" "")
fi
menu_items+=("$i" "$(printf "%-50s %s" "$translated_description" "$translated_category")" "$state")
i=$((i+1))
previous_category="$category"
done
2025-02-25 17:44:17 +01:00
cleanup
2025-02-24 23:44:47 +01:00
local selected_indices=$(whiptail --title "$(translate "ProxMenux Custom Script for Post-Installation")" \
--checklist --separate-output \
"\n$HEADER\n\n$(translate "Choose options to configure:")\n$(translate "Use [SPACE] to select/deselect and [ENTER] to confirm:")" \
20 82 12 \
"${menu_items[@]}" \
3>&1 1>&2 2>&3)
if [ $? -ne 0 ]; then
echo "User cancelled. Exiting."
exit 0
fi
# Convert selected_indices to an array
IFS=$'\n' read -d '' -r -a selected_options <<< "$selected_indices"
declare -A selected_functions
if [ -n "$selected_indices" ]; then
msg_title "$SCRIPT_TITLE"
# Mark selected options and apply exclusion logic
for index in "${selected_options[@]}"; do
option=${sorted_options[$((index-1))]}
IFS='|' read -r category description function_name <<< "$option"
selected_functions[$function_name]=1
# If FASTFETCH is selected, unmark MOTD
if [[ "$function_name" == "FASTFETCH" ]]; then
selected_functions[MOTD]=0
fi
done
# Process selected options
for index in "${!sorted_options[@]}"; do
option=${sorted_options[$index]}
IFS='|' read -r category 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
;;
ENABLE_HA)
enable_ha
;;
*)
echo "Option $function_name not implemented yet"
;;
esac
fi
done
if [ "$NECESSARY_REBOOT" -eq 1 ]; then
whiptail --title "Reboot Required" --yesno "$(translate "Some changes require a reboot to take effect. Do you want to restart now?")" 10 60
if [ $? -eq 0 ]; then
msg_info "$(translate "Removing no longer required packages and purging old cached updates...")"
/usr/bin/env DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::='--force-confdef' autoremove >/dev/null 2>&1
/usr/bin/env DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::='--force-confdef' autoclean >/dev/null 2>&1
msg_ok "$(translate "Cleanup finished")"
msg_warn "$(translate "Rebooting the system...")"
reboot
else
msg_info "$(translate "Removing no longer required packages and purging old cached updates...")"
/usr/bin/env DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::='--force-confdef' autoremove >/dev/null 2>&1
/usr/bin/env DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::='--force-confdef' autoclean >/dev/null 2>&1
msg_ok "$(translate "Cleanup finished")"
msg_info2 "$(translate "You can reboot later manually.")"
fi
fi
msg_success "$(translate "All changes applied. No reboot required.")"
else
# Si no se seleccionaron opciones, salir sin hacer nada
exit 0
fi
}
# Main script execution
2025-02-25 17:44:17 +01:00
show_proxmenux_logo
if [[ "$LANGUAGE" != "en" ]]; then
msg_lang "$(translate "Generating automatic translations...")"
fi
2025-02-24 23:44:47 +01:00
main_menu