Last active
May 29, 2025 03:29
-
-
Save anhvandev/3927f0dcaf485c0828ffcbd75542d2b5 to your computer and use it in GitHub Desktop.
Basic security for vps
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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