#!/usr/bin/env bash
set -e
### ===== CUSTOM OPTIONS =====
CUSTOM_PORT=2222
ALLOW_PASSWORD_LOGIN=true
NEWUSER="americio"
SSH_PUBLIC_KEY="ssh-ed25519 AAAA...your_key_here"
PASSWORDLESS_SUDO=false
BANTIME="1h"
FINDTIME="10m"
MAXRETRY=3
### ===========================
FILE=/etc/ssh/sshd_config
echo "πΎ Backing up original sshd_config"
sudo cp "$FILE" "$FILE.backup.$(date +%F-%H%M%S)"
echo "βοΈ Writing secure sshd_config..."
sudo tee "$FILE" >/dev/null <<EOF
# ===== SECURE SSH CONFIG (FRESH VPS) =====
# Port (change from default 22 reduces random bots)
Port ${CUSTOM_PORT}
# To disable IPv6 for SSH, uncomment:
# AddressFamily inet
# Authentication
PermitRootLogin no
PasswordAuthentication $( [ "$ALLOW_PASSWORD_LOGIN" = true ] && echo "yes" || echo "no" )
PermitEmptyPasswords no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys
# Security limits
MaxAuthTries 3
LoginGraceTime 20
MaxSessions 2
# Keepalive
ClientAliveInterval 120
ClientAliveCountMax 2
# Logging
LogLevel VERBOSE
# Default "Include" directory
Include /etc/ssh/sshd_config.d/*.conf
EOF
echo "π€ Validating new SSH configuration..."
if sudo sshd -t; then
echo "β
SSH config OK. Reloading sshd..."
sudo systemctl reload sshd
echo "π SSH hardened successfully."
else
echo "β SSH config ERROR. Restoring backup..."
sudo cp "$FILE.backup"* "$FILE"
exit 1
fi
echo "===================================================================="
echo "πͺ Creating user: $NEWUSER"
if id "$NEWUSER" >/dev/null 2>&1; then
echo "π» User already exists, skipping."
else
sudo adduser --disabled-password --gecos "" "$NEWUSER"
GENPASS=$(openssl rand -base64 16)
echo "$NEWUSER:$GENPASS" | sudo chpasswd
echo "π Temporary password for $NEWUSER: $GENPASS"
fi
echo "π€ Adding $NEWUSER to sudo group"
sudo usermod -aG sudo "$NEWUSER"
sudo usermod -aG adm "$NEWUSER"
sudo usermod -s /bin/bash "$NEWUSER"
echo "ποΈ Setting up SSH directory"
sudo mkdir -p /home/$NEWUSER/.ssh
echo "$SSH_PUBLIC_KEY" | sudo tee /home/$NEWUSER/.ssh/authorized_keys >/dev/null
sudo chmod 700 /home/$NEWUSER/.ssh
sudo chmod 600 /home/$NEWUSER/.ssh/authorized_keys
sudo chown -R $NEWUSER:$NEWUSER /home/$NEWUSER/.ssh
if [ "$PASSWORDLESS_SUDO" = true ]; then
echo "π Enabling passwordless sudo for $NEWUSER"
echo "$NEWUSER ALL=(ALL) NOPASSWD:ALL" | sudo tee /etc/sudoers.d/90-$NEWUSER >/dev/null
sudo chmod 440 /etc/sudoers.d/90-$NEWUSER
fi
echo "π Locking root password (optional safety)"
sudo passwd -l root
echo "π Secure sudo user setup completed."
echo "Now log in as: ssh $NEWUSER@your-server -p $CUSTOM_PORT"
echo "===================================================================="
echo "π‘οΈ Configuring UFW firewall with safe defaults"
sudo ufw --force reset
sudo ufw default deny incoming
sudo ufw default allow outgoing
echo "π Allowing SSH port: $CUSTOM_PORT and other common ports (http, https)"
sudo ufw limit ${CUSTOM_PORT}/tcp
sudo ufw allow http
sudo ufw allow https
echo "π Enabling firewall..."
sudo ufw --force enable
echo "π UFW status:"
sudo ufw status verbose
echo "π UFW firewall configuration completed."
echo "===================================================================="
echo "π¦ Installing fail2ban..."
sudo apt update
sudo apt install -y fail2ban
JAIL_LOCAL="/etc/fail2ban/jail.local"
echo "π Writing fail2ban jail.local..."
sudo tee $JAIL_LOCAL >/dev/null <<EOF
[DEFAULT]
bantime = ${BANTIME}
findtime = ${FINDTIME}
maxretry = ${MAXRETRY}
backend = systemd
banaction = ufw
ignoreip = 127.0.0.1/8 ::1
[sshd]
enabled = true
port = ${CUSTOM_PORT}
filter = sshd
backend = systemd
logpath = journal
EOF
echo "π Restarting Fail2ban..."
sudo systemctl enable --now fail2ban
sudo systemctl status fail2ban
sudo fail2ban-client status sshd
sudo systemctl restart fail2ban
sleep 2
echo "π Fail2ban SSH jail status:"
sudo fail2ban-client status sshd
echo "π Fail2ban installed & configured."
echo "===================================================================="
echo "π¦ Unattended Security Updates (auto-patch for critical CVEs)"
sudo apt install -y unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgrades
if false; then
echo "===================================================================="
echo "π Enabling SSH 2FA (TOTP via Google Authenticator)"
echo "π¦ Installing TOTP PAM module..."
sudo apt install -y libpam-google-authenticator
echo "πͺͺ Generating TOTP secret for user: $NEWUSER"
sudo -u "$NEWUSER" bash -c "google-authenticator -t -d -f -r 3 -R 30 -W --quiet"
echo "π Backing up SSH PAM config..."
sudo cp /etc/pam.d/sshd /etc/pam.d/sshd.bak.$(date +%F-%H%M%S)
echo "βοΈ Updating PAM rules for TOTP"
sudo sed -i '1i auth required pam_google_authenticator.so' /etc/pam.d/sshd
echo "π Backing up sshd_config..."
sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak.$(date +%F-%H%M%S)
echo "βοΈ Configuring SSH to require BOTH publickey & TOTP"
sudo sed -i 's/^#\?ChallengeResponseAuthentication.*/ChallengeResponseAuthentication yes/' /etc/ssh/sshd_config
sudo sed -i 's/^#\?KbdInteractiveAuthentication.*/KbdInteractiveAuthentication yes/' /etc/ssh/sshd_config
# Require BOTH:
# - publickey
# - keyboard-interactive (Google Authenticator)
if grep -q "^AuthenticationMethods" /etc/ssh/sshd_config; then
sudo sed -i 's/^AuthenticationMethods.*/AuthenticationMethods publickey,keyboard-interactive/' /etc/ssh/sshd_config
else
echo "AuthenticationMethods publickey,keyboard-interactive" | sudo tee -a /etc/ssh/sshd_config >/dev/null
fi
echo "π€ Validating SSH configuration with 2FA..."
if sudo sshd -t; then
echo "β
SSH config OK. Restarting sshd..."
sudo systemctl restart sshd
echo "π SSH 2FA enabled successfully."
else
echo "β ERROR: Invalid SSH config. Rolling back..."
sudo cp /etc/ssh/sshd_config.bak.* /etc/ssh/sshd_config
sudo cp /etc/pam.d/sshd.bak.* /etc/pam.d/sshd
sudo systemctl restart sshd
exit 1
fi
echo "β¨ 2FA Setup Completed."
echo "π IMPORTANT: Open a NEW terminal and test login before closing this session."
echo "SSH Login now requires:"
echo " 1) SSH private key"
echo " 2) Google Authenticator 6-digit TOTP code"
echo "===================================================================="
echo "π Installing hardened auditd rules..."
sudo tee /etc/audit/rules.d/hardening.rules >/dev/null << "EOF"
## ===============================
## HARDENED AUDITD SECURITY RULES
## ===============================
# Track all commands executed via sudo
-w /var/log/sudo.log -p wa -k sudo_logs
# Track changes to privileged binaries (SUID/SGID)
-w /usr/bin -p wa -k privileged-bins
-w /usr/sbin -p wa -k privileged-bins
# Track changes to essential system config
-w /etc/passwd -p wa -k passwd_changes
-w /etc/group -p wa -k group_changes
-w /etc/shadow -p wa -k shadow_changes
-w /etc/sudoers -p wa -k sudoers_changes
-w /etc/sudoers.d/ -p wa -k sudoers_changes
# Track SSH configuration changes
-w /etc/ssh/sshd_config -p wa -k ssh_config
-w /etc/ssh/sshd_config.d/ -p wa -k ssh_config
# Track all login/logout events, including failures
-w /var/log/faillog -p wa -k auth_failures
-w /var/log/lastlog -p wa -k auth_changes
-w /var/log/tallylog -p wa -k auth_failures
# Track modification to systemd service files
-w /etc/systemd/system/ -p wa -k systemd_changes
-w /usr/lib/systemd/system/ -p wa -k systemd_changes
# Track kernel module loading/unloading
-w /sbin/modprobe -p x -k kernel_modules
-w /usr/sbin/modprobe -p x -k kernel_modules
# Record attempts to become root
-w /bin/su -p x -k root_escalation
# Monitor unauthorized file permission or attribute changes
-a always,exit -F arch=b64 -S chmod,chown,chgrp,fchmod,fchown -k perms
-a always,exit -F arch=b32 -S chmod,chown,chgrp,fchmod,fchown -k perms
# Detect root-level shell spawning (/bin/bash executed by UID 0)
-a always,exit -F arch=b64 -S execve -F uid=0 -k root_shell
-a always,exit -F arch=b32 -S execve -F uid=0 -k root_shell
# Detect modifications to firewall rules
-w /etc/ufw/ -p wa -k ufw_changes
-w /etc/firewalld/ -p wa -k firewalld_changes
# Track crontab modifications (cron persistence attacks)
-w /etc/crontab -p wa -k cron_changes
-w /etc/cron.d/ -p wa -k cron_changes
-w /etc/cron.daily/ -p wa -k cron_changes
-w /etc/cron.hourly/ -p wa -k cron_changes
# Ensure audit configuration itself is immutable
-e 2
EOF
echo "π Reloading audit rules..."
sudo augenrules --load
sudo systemctl restart auditd
echo "β
auditd hardening rules installed!"
fi
echo "π― Finished initial VPS Setup! Goodbye!"