mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2025-06-28 12:16:53 +00:00
2370 lines
79 KiB
Bash
2370 lines
79 KiB
Bash
|
#!/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
|
||
|
|
||
|
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
|
||
|
show_proxmenux_logo "$YW"
|
||
|
main_menu
|
||
|
|