Skip to content

Instantly share code, notes, and snippets.

@anhvandev
Last active May 29, 2025 03:29
Show Gist options
  • Save anhvandev/3927f0dcaf485c0828ffcbd75542d2b5 to your computer and use it in GitHub Desktop.
Save anhvandev/3927f0dcaf485c0828ffcbd75542d2b5 to your computer and use it in GitHub Desktop.
Basic security for vps
#!/bin/bash
# Linux Server Hardening Script for AlmaLinux and Ubuntu
# Author: Van Nguyen
# Version: 1.0
set -euo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Global variables
OS_TYPE=""
SSH_PORT=""
NEW_USER=""
USER_PASSWORD=""
LOG_FILE="/var/log/server_hardening.log"
# Logging function
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a $LOG_FILE
}
# Print colored output
print_status() {
echo -e "${GREEN}[INFO]${NC} $1"
log "INFO: $1"
}
print_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
log "WARNING: $1"
}
print_error() {
echo -e "${RED}[ERROR]${NC} $1"
log "ERROR: $1"
}
# Generate random password
generate_password() {
# Generate 16-character password with mixed case, numbers and symbols
USER_PASSWORD=$(openssl rand -base64 32 | tr -d "=+/" | cut -c1-16)
if [[ -z "$USER_PASSWORD" ]]; then
# Fallback method if openssl fails
USER_PASSWORD=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9!@#$%^&*' | fold -w 16 | head -n 1)
fi
}
# Detect OS function
detect_os() {
if [[ -f /etc/redhat-release ]]; then
OS_TYPE="rhel"
print_status "Detected RHEL-based system (AlmaLinux/CentOS/RHEL)"
elif [[ -f /etc/debian_version ]]; then
OS_TYPE="debian"
print_status "Detected Debian-based system (Ubuntu/Debian)"
else
print_error "Unsupported operating system"
exit 1
fi
}
# Check if running as root
check_root() {
if [[ $EUID -ne 0 ]]; then
print_error "This script must be run as root"
exit 1
fi
}
# Step 1: Update system
update_system() {
print_status "Step 1: Updating system packages..."
if [[ $OS_TYPE == "debian" ]]; then
export DEBIAN_FRONTEND=noninteractive
apt-get update -y
apt-get upgrade -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold"
apt-get autoremove -y
apt-get autoclean
elif [[ $OS_TYPE == "rhel" ]]; then
dnf update -y --nobest --skip-broken
dnf autoremove -y
fi
print_status "System update completed"
}
# Step 2: Create sudo user and setup SSH keys
setup_user_and_ssh() {
print_status "Step 2: Setting up user and SSH configuration..."
# Get username from user
read -p "Enter username for new sudo user: " NEW_USER
# Generate random password for the user
generate_password
# Create user
if ! id "$NEW_USER" &>/dev/null; then
useradd -m -s /bin/bash "$NEW_USER"
echo "$NEW_USER:$USER_PASSWORD" | chpasswd
usermod -aG sudo "$NEW_USER" 2>/dev/null || usermod -aG wheel "$NEW_USER"
print_status "Created user: $NEW_USER with random password"
else
print_warning "User $NEW_USER already exists"
echo "$NEW_USER:$USER_PASSWORD" | chpasswd
print_status "Updated password for existing user: $NEW_USER"
fi
# Setup SSH directory and generate keys
USER_HOME="/home/$NEW_USER"
SSH_DIR="$USER_HOME/.ssh"
mkdir -p "$SSH_DIR"
chmod 700 "$SSH_DIR"
# Generate SSH key pair
ssh-keygen -t rsa -b 4096 -f "$SSH_DIR/id_rsa" -N "" -C "$NEW_USER@$(hostname)"
cp "$SSH_DIR/id_rsa.pub" "$SSH_DIR/authorized_keys"
chmod 600 "$SSH_DIR/authorized_keys"
chmod 600 "$SSH_DIR/id_rsa"
chown -R "$NEW_USER:$NEW_USER" "$SSH_DIR"
print_status "SSH keys generated and configured"
# Configure SSH daemon
cp /etc/ssh/sshd_config /etc/ssh/sshd_config.backup
# Disable root login and password authentication
sed -i 's/#*PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config
sed -i 's/#*PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config
sed -i 's/#*PubkeyAuthentication.*/PubkeyAuthentication yes/' /etc/ssh/sshd_config
sed -i 's/#*ChallengeResponseAuthentication.*/ChallengeResponseAuthentication no/' /etc/ssh/sshd_config
print_status "SSH configuration updated"
}
# Step 3: Setup firewall and random SSH port
setup_firewall() {
print_status "Step 3: Configuring firewall and SSH port..."
# Generate random SSH port between 10000-65000
SSH_PORT=$(shuf -i 10000-65000 -n 1)
if [[ $OS_TYPE == "debian" ]]; then
# Install and configure UFW
apt-get install -y ufw
ufw --force reset
ufw default deny incoming
ufw default allow outgoing
ufw allow $SSH_PORT/tcp
ufw allow 80/tcp
ufw allow 443/tcp
ufw --force enable
print_status "UFW configured with SSH port $SSH_PORT"
elif [[ $OS_TYPE == "rhel" ]]; then
# Install and configure firewalld
dnf install -y firewalld
systemctl enable firewalld
systemctl start firewalld
firewall-cmd --permanent --remove-service=ssh
firewall-cmd --permanent --add-port=$SSH_PORT/tcp
firewall-cmd --permanent --add-service=http
firewall-cmd --permanent --add-service=https
firewall-cmd --reload
print_status "Firewalld configured with SSH port $SSH_PORT"
fi
# Update SSH port in configuration
sed -i "s/#*Port.*/Port $SSH_PORT/" /etc/ssh/sshd_config
# Handle SELinux for SSH port change
if command -v semanage &> /dev/null; then
semanage port -a -t ssh_port_t -p tcp $SSH_PORT 2>/dev/null || \
semanage port -m -t ssh_port_t -p tcp $SSH_PORT
print_status "SELinux policy updated for SSH port $SSH_PORT"
fi
}
# Step 4: Setup swap if not exists
setup_swap() {
print_status "Step 4: Checking and configuring swap..."
if [[ $(swapon --show | wc -l) -eq 0 ]]; then
# Calculate swap size (half of RAM, max 10GB)
TOTAL_RAM=$(free -m | awk '/^Mem:/{print $2}')
SWAP_SIZE=$(($TOTAL_RAM / 2))
if [[ $SWAP_SIZE -gt 10240 ]]; then
SWAP_SIZE=10240
fi
print_status "Creating ${SWAP_SIZE}MB swap file..."
fallocate -l ${SWAP_SIZE}M /swapfile
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
# Add to fstab for persistence
if ! grep -q "/swapfile" /etc/fstab; then
echo '/swapfile none swap sw 0 0' >> /etc/fstab
fi
print_status "Swap configured successfully"
else
print_status "Swap already configured"
fi
}
# Step 5: Install and configure fail2ban
setup_fail2ban() {
print_status "Step 5: Installing and configuring fail2ban..."
if [[ $OS_TYPE == "debian" ]]; then
apt-get install -y fail2ban
elif [[ $OS_TYPE == "rhel" ]]; then
dnf install -y epel-release
dnf install -y fail2ban
fi
# Configure fail2ban for SSH
cat > /etc/fail2ban/jail.local << EOF
[DEFAULT]
bantime = 3600
findtime = 600
maxretry = 10
[sshd]
enabled = true
port = $SSH_PORT
filter = sshd
logpath = /var/log/auth.log
maxretry = 10
bantime = 3600
EOF
# For RHEL-based systems, adjust log path
if [[ $OS_TYPE == "rhel" ]]; then
sed -i 's|/var/log/auth.log|/var/log/secure|' /etc/fail2ban/jail.local
fi
systemctl enable fail2ban
systemctl restart fail2ban
print_status "Fail2ban configured and started"
}
# Step 6: Display summary and reboot option
display_summary() {
print_status "Step 6: Hardening completed! Here's the summary:"
echo -e "\n${BLUE}=== SERVER HARDENING SUMMARY ===${NC}"
echo -e "OS Type: ${GREEN}$([[ $OS_TYPE == "debian" ]] && echo "Ubuntu/Debian" || echo "AlmaLinux/RHEL")${NC}"
echo -e "New SSH Port: ${GREEN}$SSH_PORT${NC}"
echo -e "New User: ${GREEN}$NEW_USER${NC}"
echo -e "User Password: ${YELLOW}$USER_PASSWORD${NC}"
echo -e "SSH Key Location: ${GREEN}/home/$NEW_USER/.ssh/id_rsa${NC}"
echo -e "Firewall: ${GREEN}$([[ $OS_TYPE == "debian" ]] && echo "UFW" || echo "Firewalld") - Active${NC}"
echo -e "Fail2ban: ${GREEN}Active (1h ban, 10 max retries)${NC}"
echo -e "Swap: ${GREEN}$(free -h | awk '/^Swap:/{print $2}') configured${NC}"
echo -e "Root SSH: ${RED}Disabled${NC}"
echo -e "Password Auth: ${RED}Disabled${NC}"
echo -e "\n${YELLOW}IMPORTANT CREDENTIALS:${NC}"
echo -e "Username: ${GREEN}$NEW_USER${NC}"
echo -e "Password: ${YELLOW}$USER_PASSWORD${NC}"
echo -e "SSH Port: ${GREEN}$SSH_PORT${NC}"
echo -e "Private Key: ${GREEN}/home/$NEW_USER/.ssh/id_rsa${NC}"
echo -e "\n${YELLOW}CONNECTION METHODS:${NC}"
echo -e "1. SSH with key: ${GREEN}ssh -i /path/to/key -p $SSH_PORT $NEW_USER@your_server_ip${NC}"
echo -e "2. Console login: ${GREEN}Username: $NEW_USER, Password: $USER_PASSWORD${NC}"
echo -e "\n${RED}SECURITY NOTES:${NC}"
echo -e "• SSH password authentication is DISABLED"
echo -e "• Root SSH login is DISABLED"
echo -e "• Copy the private key before closing this session!"
echo -e "• Save these credentials in a secure location"
echo -e "• Test SSH connection before closing current session!"
# Save credentials to a file for reference
cat > /root/server_credentials.txt << EOF
=== SERVER HARDENING CREDENTIALS ===
Date: $(date)
OS Type: $([[ $OS_TYPE == "debian" ]] && echo "Ubuntu/Debian" || echo "AlmaLinux/RHEL")
New SSH Port: $SSH_PORT
Username: $NEW_USER
Password: $USER_PASSWORD
SSH Key Location: /home/$NEW_USER/.ssh/id_rsa
Connection Commands:
SSH with key: ssh -i /path/to/key -p $SSH_PORT $NEW_USER@your_server_ip
Console login: Username: $NEW_USER, Password: $USER_PASSWORD
IMPORTANT:
- Copy the private key from /home/$NEW_USER/.ssh/id_rsa
- SSH password authentication is DISABLED
- Root SSH login is DISABLED
EOF
print_status "Credentials saved to: ${GREEN}/root/server_credentials.txt${NC}"
# Ask for reboot
echo -e "\n"
read -p "Do you want to reboot the system now? (y/N): " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
print_status "Rebooting system in 10 seconds..."
sleep 10
reboot
else
print_status "Please reboot manually when convenient"
print_warning "Remember to restart SSH service: systemctl restart sshd"
fi
}
# Main function
main() {
clear
echo -e "${BLUE}=== Linux Server Hardening Script ===${NC}"
echo -e "${BLUE}=== AlmaLinux & Ubuntu Compatible ===${NC}\n"
check_root
detect_os
print_status "Starting server hardening process..."
update_system
setup_user_and_ssh
setup_firewall
setup_swap
setup_fail2ban
# Restart SSH service
systemctl restart sshd
display_summary
}
# Run main function
main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment