Ubuntu LTS Security Hardening Guide: Complete Server Setup for Beginners

Ubuntu LTS Security Hardening Guide: Complete Server Setup

Setting up a secure Ubuntu LTS server doesn’t have to be complicated. This guide walks you through enterprise-grade security hardening step by step, explaining why each security measure matters and how it protects your server.

Whether you’re setting up a web server, development environment, or personal cloud server, these security practices will protect you from common attacks and keep your data safe.

Prerequisites

What you’ll need:

  • Fresh Ubuntu LTS installation (20.04, 22.04, or 24.04)
  • Root or sudo access
  • Internet connection
  • Basic command-line familiarity

Why start with a fresh install?

  • Clean baseline free of pre-existing vulnerabilities
  • No conflicting configurations from previous setups
  • Easier to implement security from the ground up

Automated Security Hardening Scripts

Save time and reduce errors! Copy and run these automated scripts to implement all security measures with a single command.

Master Security Hardening Script

Why use automation?

  • Consistency: Same security measures every time
  • Speed: Implement all security in minutes, not hours
  • Accuracy: No manual typos or missed steps
  • Documentation: Scripts serve as living security documentation

⚠️ Important: Review each script before running. Understand what it does and customize variables for your environment.

#!/bin/bash
# Ubuntu LTS Security Hardening Master Script
# Run as root or with sudo
# Version: 1.0
# Author: Ubuntu Security Hardening Guide

set -e  # Exit on any error

# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

# Configuration variables - MODIFY THESE FOR YOUR ENVIRONMENT
NEW_USER="yourusername"  # Replace with your desired username
SSH_PORT="22"           # Change if you want non-standard SSH port
ADMIN_EMAIL="admin@yourdomain.com"  # For security notifications
TIMEZONE="America/New_York"  # Set your timezone

# Logging
LOG_FILE="/var/log/ubuntu-hardening-$(date +%Y%m%d-%H%M%S).log"
exec > >(tee -a "$LOG_FILE") 2>&1

echo -e "${BLUE}πŸ”’ Ubuntu LTS Security Hardening Script${NC}"
echo -e "${BLUE}=======================================${NC}"
echo -e "${YELLOW}Log file: $LOG_FILE${NC}"
echo -e "${YELLOW}Started at: $(date)${NC}"
echo ""

# Function to print status messages
print_status() {
    echo -e "${GREEN}βœ… $1${NC}"
}

print_warning() {
    echo -e "${YELLOW}⚠️  $1${NC}"
}

print_error() {
    echo -e "${RED}❌ $1${NC}"
}

# Check if running as root
if [[ $EUID -ne 0 ]]; then
   print_error "This script must be run as root or with sudo"
   exit 1
fi

echo -e "${BLUE}Step 1: System Updates${NC}"
echo "========================"

# Update system
print_status "Updating package lists..."
apt update

print_status "Upgrading packages..."
apt upgrade -y

print_status "Installing security tools..."
apt install -y curl wget ufw fail2ban rkhunter chkrootkit auditd unattended-upgrades apt-transport-https ca-certificates gnupg lsb-release

print_status "Configuring automatic security updates..."
cat > /etc/apt/apt.conf.d/50unattended-upgrades << EOF
Unattended-Upgrade::Allowed-Origins {
    "\${distro_id}:\${distro_codename}";
    "\${distro_id}:\${distro_codename}-security";
    "\${distro_id}:\${distro_codename}-updates";
};
Unattended-Upgrade::Mail "$ADMIN_EMAIL";
Unattended-Upgrade::Remove-Unused-Dependencies "true";
Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-Time "02:00";
EOF

print_status "Enabling unattended upgrades..."
dpkg-reconfigure --frontend=noninteractive unattended-upgrades

echo ""
echo -e "${BLUE}Step 2: User Management${NC}"
echo "======================="

# Create new user if it doesn't exist
if id "$NEW_USER" &>/dev/null; then
    print_warning "User $NEW_USER already exists"
else
    print_status "Creating new user: $NEW_USER"
    useradd -m -s /bin/bash "$NEW_USER"
    usermod -aG sudo "$NEW_USER"
    print_status "User created. Set password with: sudo passwd $NEW_USER"
fi

echo ""
echo -e "${BLUE}Step 3: SSH Hardening${NC}"
echo "===================="

print_status "Backing up SSH configuration..."
cp /etc/ssh/sshd_config /etc/ssh/sshd_config.backup.$(date +%Y%m%d_%H%M%S)

print_status "Configuring SSH security..."
cat >> /etc/ssh/sshd_config << EOF

# Security hardening - Added by hardening script
PasswordAuthentication no
PermitRootLogin no
Protocol 2
MaxAuthTries 3
ClientAliveInterval 300
ClientAliveCountMax 2
X11Forwarding no
AllowTcpForwarding no
PermitEmptyPasswords no
UseDNS no
EOF

print_status "Restarting SSH service..."
systemctl restart ssh

echo ""
echo -e "${BLUE}Step 4: Firewall Configuration${NC}"
echo "============================"

print_status "Configuring UFW firewall..."
ufw --force reset

print_status "Setting default policies..."
ufw default deny incoming
ufw default allow outgoing

print_status "Allowing SSH..."
ufw allow "$SSH_PORT"/tcp

print_status "Enabling firewall..."
ufw --force enable

echo ""
echo -e "${BLUE}Step 5: Fail2Ban Setup${NC}"
echo "===================="

print_status "Configuring Fail2Ban..."
cat > /etc/fail2ban/jail.local << EOF
[sshd]
enabled = true
port = $SSH_PORT
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 3600
findtime = 600

[nginx-http-auth]
enabled = true
port = http,https
filter = nginx-http-auth
logpath = /var/log/nginx/error.log
maxretry = 3
bantime = 3600

[nginx-noscript]
enabled = true
port = http,https
filter = nginx-noscript
logpath = /var/log/nginx/access.log
maxretry = 6
bantime = 3600
EOF

print_status "Enabling and starting Fail2Ban..."
systemctl enable fail2ban
systemctl restart fail2ban

echo ""
echo -e "${BLUE}Step 6: System Hardening${NC}"
echo "======================="

print_status "Configuring kernel security parameters..."
cat >> /etc/sysctl.conf << EOF

# Security hardening - Added by hardening script
net.ipv4.tcp_syncookies = 1
net.ipv4.ip_forward = 0
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.all.log_martians = 1
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.icmp_ignore_bogus_error_responses = 1
fs.suid_dumpable = 0
kernel.dmesg_restrict = 1
vm.mmap_min_addr = 65536
EOF

print_status "Applying kernel parameters..."
sysctl -p

print_status "Securing file permissions..."
chmod 600 /etc/shadow /etc/gshadow
chmod 644 /etc/passwd /etc/group

echo ""
echo -e "${BLUE}Step 7: Security Monitoring${NC}"
echo "========================="

print_status "Enabling auditd..."
systemctl enable auditd
systemctl start auditd

print_status "Installing logwatch..."
apt install -y logwatch

print_status "Configuring logwatch..."
cat > /etc/cron.daily/logwatch << EOF
#!/bin/bash
/usr/sbin/logwatch --output mail --mailto $ADMIN_EMAIL --detail high
EOF
chmod +x /etc/cron.daily/logwatch

echo ""
echo -e "${BLUE}Step 8: Backup Configuration${NC}"
echo "==========================="

print_status "Installing backup tools..."
apt install -y rsync duplicity

print_status "Creating backup script..."
cat > /usr/local/bin/ubuntu-backup.sh << 'EOF'
#!/bin/bash
# Ubuntu automated backup script

BACKUP_DIR="/var/backups"
SOURCE_DIR="/home"
DATE=$(date +%Y%m%d_%H%M%S)

# Create backup directory if it doesn't exist
mkdir -p "$BACKUP_DIR"

# Local backup
rsync -avz --delete "$SOURCE_DIR" "$BACKUP_DIR/daily_$DATE"

# Clean old backups (keep last 7)
find "$BACKUP_DIR" -name "daily_*" -type d -mtime +7 -exec rm -rf {} +

echo "Backup completed: $BACKUP_DIR/daily_$DATE"
EOF

chmod +x /usr/local/bin/ubuntu-backup.sh

print_status "Scheduling daily backups..."
echo "0 2 * * * /usr/local/bin/ubuntu-backup.sh" > /etc/cron.d/ubuntu-backup

echo ""
echo -e "${BLUE}Step 9: Security Validation${NC}"
echo "==========================="

print_status "Creating security validation script..."
cat > /usr/local/bin/ubuntu-security-check.sh << 'EOF'
#!/bin/bash
# Ubuntu Security Validation Script

echo "πŸ”’ Ubuntu Security Check"
echo "========================"

# SSH Security
echo "SSH Security:"
if grep -q "PasswordAuthentication no" /etc/ssh/sshd_config; then
    echo "βœ… Password authentication disabled"
else
    echo "❌ Password authentication enabled"
fi

if grep -q "PermitRootLogin no" /etc/ssh/sshd_config; then
    echo "βœ… Root login disabled"
else
    echo "❌ Root login enabled"
fi

# Firewall
echo -e "\nFirewall Status:"
ufw status | grep -E "(Status|active)"

# Fail2Ban
echo -e "\nFail2Ban Status:"
systemctl is-active fail2ban

# Updates
echo -e "\nSystem Updates:"
apt list --upgradable 2>/dev/null | grep -c "upgradable" | xargs echo "packages can be upgraded"

echo -e "\nSecurity check complete!"
EOF

chmod +x /usr/local/bin/ubuntu-security-check.sh

print_status "Running initial security check..."
/usr/local/bin/ubuntu-security-check.sh

echo ""
echo -e "${GREEN}πŸŽ‰ Ubuntu Security Hardening Complete!${NC}"
echo ""
echo -e "${YELLOW}Next Steps:${NC}"
echo "1. Set password for new user: sudo passwd $NEW_USER"
echo "2. Copy SSH keys to server for the new user"
echo "3. Test SSH connection with new user"
echo "4. Run security check: /usr/local/bin/ubuntu-security-check.sh"
echo "5. Review logs: tail -f $LOG_FILE"
echo ""
echo -e "${BLUE}Log saved to: $LOG_FILE${NC}"
echo -e "${BLUE}Completed at: $(date)${NC}"

Individual Security Scripts

Quick SSH Key Setup Script

#!/bin/bash
# Quick SSH Key Setup for Ubuntu
# Run as the user who needs SSH access

set -e

# Colors
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'

echo -e "${GREEN}πŸ”‘ SSH Key Setup for Ubuntu${NC}"
echo "============================="

# Check if SSH key already exists
if [ -f ~/.ssh/id_ed25519 ]; then
    echo -e "${YELLOW}SSH key already exists. Backup and create new one? (y/n)${NC}"
    read -r response
    if [[ "$response" =~ ^([yY][eE][sS]|[yY])$ ]]; then
        mv ~/.ssh/id_ed25519 ~/.ssh/id_ed25519.backup.$(date +%Y%m%d_%H%M%S)
        mv ~/.ssh/id_ed25519.pub ~/.ssh/id_ed25519.pub.backup.$(date +%Y%m%d_%H%M%S)
    else
        echo "Keeping existing SSH key."
        exit 0
    fi
fi

# Generate new SSH key
echo "Generating Ed25519 SSH key..."
ssh-keygen -t ed25519 -C "$(whoami)@$(hostname)-$(date +%Y%m%d)" -f ~/.ssh/id_ed25519 -N ""

# Set correct permissions
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_ed25519
chmod 644 ~/.ssh/id_ed25519.pub

echo -e "${GREEN}βœ… SSH key generated!${NC}"
echo ""
echo "Public key (copy this to server's authorized_keys):"
echo "=================================================="
cat ~/.ssh/id_ed25519.pub
echo ""
echo "To copy to server, run:"
echo "ssh-copy-id user@server-ip"
echo ""
echo "Private key location: ~/.ssh/id_ed25519"
echo "Public key location: ~/.ssh/id_ed25519.pub"

Automated Firewall Setup Script

#!/bin/bash
# Automated Firewall Setup for Ubuntu
# Configures UFW with security best practices

set -e

# Colors
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
NC='\033[0m'

echo -e "${GREEN}πŸ”₯ Ubuntu Firewall Setup${NC}"
echo "========================"

# Check if running as root
if [[ $EUID -ne 0 ]]; then
   echo -e "${RED}❌ This script must be run as root or with sudo${NC}"
   exit 1
fi

# Backup current UFW configuration
echo "Backing up current UFW configuration..."
ufw status > /etc/ufw/ufw-status-backup-$(date +%Y%m%d_%H%M%S).txt

# Reset UFW to defaults
echo "Resetting UFW to defaults..."
ufw --force reset

# Set default policies
echo "Setting secure default policies..."
ufw default deny incoming
ufw default allow outgoing

# Allow essential services
echo "Allowing essential services..."

# SSH (with rate limiting to prevent brute force)
ufw limit ssh/tcp comment 'SSH with rate limiting'

# HTTP/HTTPS for web servers (uncomment if needed)
# ufw allow 80/tcp comment 'HTTP'
# ufw allow 443/tcp comment 'HTTPS'

# DNS
ufw allow out 53/udp comment 'DNS'
ufw allow out 53/tcp comment 'DNS TCP'

# NTP for time synchronization
ufw allow out 123/udp comment 'NTP'

# Enable UFW
echo "Enabling UFW firewall..."
ufw --force enable

# Show status
echo ""
echo -e "${GREEN}βœ… Firewall configured successfully!${NC}"
echo ""
echo "Current firewall status:"
ufw status verbose

echo ""
echo -e "${YELLOW}πŸ“‹ Firewall Rules Applied:${NC}"
echo "- Default: Deny all incoming, allow all outgoing"
echo "- SSH: Allowed with rate limiting (6 connections/minute)"
echo "- DNS: Allowed for name resolution"
echo "- NTP: Allowed for time synchronization"
echo ""
echo -e "${YELLOW}πŸ”§ To modify rules:${NC}"
echo "  ufw allow 80/tcp    # Allow HTTP"
echo "  ufw allow 443/tcp   # Allow HTTPS"
echo "  ufw allow from 192.168.1.0/24  # Allow from specific IP range"
echo "  ufw reload          # Apply changes"

Security Monitoring Dashboard Script

#!/bin/bash
# Ubuntu Security Monitoring Dashboard
# Run periodically to check system security status

# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
PURPLE='\033[0;35m'
CYAN='\033[0;36m'
NC='\033[0m'

echo -e "${BLUE}πŸ”’ Ubuntu Security Monitoring Dashboard${NC}"
echo -e "${BLUE}=========================================${NC}"
echo -e "${CYAN}Report generated: $(date)${NC}"
echo ""

# System Information
echo -e "${PURPLE}πŸ“Š System Information${NC}"
echo "======================"
echo "Hostname: $(hostname)"
echo "Ubuntu Version: $(lsb_release -d | cut -f2)"
echo "Kernel: $(uname -r)"
echo "Uptime: $(uptime -p)"
echo ""

# Security Status Checks
echo -e "${PURPLE}πŸ” Security Status${NC}"
echo "==================="

# SSH Security
echo -e "${CYAN}SSH Configuration:${NC}"
if grep -q "^PasswordAuthentication no" /etc/ssh/sshd_config; then
    echo -e "${GREEN}βœ… Password authentication disabled${NC}"
else
    echo -e "${RED}❌ Password authentication enabled${NC}"
fi

if grep -q "^PermitRootLogin no" /etc/ssh/sshd_config; then
    echo -e "${GREEN}βœ… Root login disabled${NC}"
else
    echo -e "${RED}❌ Root login enabled${NC}"
fi

# Firewall Status
echo -e "\n${CYAN}Firewall Status:${NC}"
if systemctl is-active --quiet ufw; then
    echo -e "${GREEN}βœ… UFW firewall is active${NC}"
    ufw status | grep -E "(Status:|22|80|443)" | sed 's/^/  /'
else
    echo -e "${RED}❌ UFW firewall is not active${NC}"
fi

# Fail2Ban Status
echo -e "\n${CYAN}Fail2Ban Status:${NC}"
if systemctl is-active --quiet fail2ban; then
    echo -e "${GREEN}βœ… Fail2Ban is running${NC}"
    echo "  Currently banned IPs:"
    fail2ban-client status sshd 2>/dev/null | grep "Currently banned" | sed 's/^/    /' || echo "    None"
else
    echo -e "${RED}❌ Fail2Ban is not running${NC}"
fi

# System Updates
echo -e "\n${CYAN}System Updates:${NC}"
UPGRADABLE=$(apt list --upgradable 2>/dev/null | grep -c "upgradable" || echo "0")
if [ "$UPGRADABLE" -eq 0 ]; then
    echo -e "${GREEN}βœ… System is up to date${NC}"
else
    echo -e "${YELLOW}⚠️  $UPGRADABLE packages can be upgraded${NC}"
fi

# Security Updates
SECURITY_UPDATES=$(apt list --upgradable 2>/dev/null | grep -c "security" || echo "0")
if [ "$SECURITY_UPDATES" -gt 0 ]; then
    echo -e "${RED}🚨 $SECURITY_UPDATES security updates available${NC}"
fi

echo ""

# Service Status
echo -e "${PURPLE}πŸ”§ Service Status${NC}"
echo "================="

services=("ssh" "ufw" "fail2ban" "auditd" "unattended-upgrades")
for service in "${services[@]}"; do
    if systemctl is-active --quiet "$service"; then
        echo -e "${GREEN}βœ… $service is running${NC}"
    else
        echo -e "${RED}❌ $service is not running${NC}"
    fi
done

echo ""

# Recent Security Events
echo -e "${PURPLE}πŸ“‹ Recent Security Events${NC}"
echo "==========================="

echo -e "${CYAN}Failed SSH login attempts (last 24h):${NC}"
failed_attempts=$(journalctl -u ssh --since "24 hours ago" -g "Failed password" | wc -l)
if [ "$failed_attempts" -gt 0 ]; then
    echo -e "${RED}🚨 $failed_attempts failed login attempts${NC}"
else
    echo -e "${GREEN}βœ… No failed login attempts${NC}"
fi

echo -e "\n${CYAN}Recent sudo usage:${NC}"
sudo_log=$(journalctl -u sudo --since "24 hours ago" | wc -l)
if [ "$sudo_log" -gt 0 ]; then
    echo -e "${BLUE}ℹ️  $sudo_log sudo commands executed${NC}"
else
    echo -e "${YELLOW}⚠️  No sudo usage in last 24 hours${NC}"
fi

echo ""

# Recommendations
echo -e "${PURPLE}πŸ’‘ Security Recommendations${NC}"
echo "============================"

recommendations=()

# Check SSH key authentication
if ! grep -q "^PasswordAuthentication no" /etc/ssh/sshd_config; then
    recommendations+=("Disable SSH password authentication")
fi

# Check firewall
if ! systemctl is-active --quiet ufw; then
    recommendations+=("Enable UFW firewall")
fi

# Check Fail2Ban
if ! systemctl is-active --quiet fail2ban; then
    recommendations+=("Install and enable Fail2Ban")
fi

# Check updates
if [ "$SECURITY_UPDATES" -gt 0 ]; then
    recommendations+=("Apply security updates immediately")
fi

if [ ${#recommendations[@]} -eq 0 ]; then
    echo -e "${GREEN}βœ… No immediate security actions required${NC}"
else
    echo -e "${YELLOW}⚠️  Recommended actions:${NC}"
    for rec in "${recommendations[@]}"; do
        echo -e "  β€’ $rec"
    done
fi

echo ""
echo -e "${BLUE}πŸ” Dashboard complete. Run this script daily for security monitoring.${NC}"
echo -e "${BLUE}πŸ’‘ For detailed logs, check: journalctl -u ssh, ufw, fail2ban${NC}"

1. System Updates and Package Management

Why Update Immediately?

Security Risk: New Ubuntu installations often ship with known vulnerabilities that attackers actively exploit. Cybercriminals scan the internet for unpatched systems.

Real-World Impact: The Equifax breach (2017) exposed 147 million records partly due to unpatched Apache Struts vulnerability.

# Update package lists
sudo apt update

# Upgrade all packages to latest versions
sudo apt upgrade -y

# Install security updates only
sudo apt install unattended-upgrades -y

Configure Automatic Security Updates

Why automate updates?

  • Human error prevention: Manual updates often get forgotten
  • Rapid vulnerability patching: Zero-day exploits get fixed quickly
  • Reduced attack window: Vulnerabilities patched before exploitation
# Enable automatic security updates
sudo dpkg-reconfigure --priority=low unattended-upgrades

# Edit the configuration file
sudo nano /etc/apt/apt.conf.d/50unattended-upgrades

Add these lines to enable automatic updates:

// Automatically upgrade packages from these sources
Unattended-Upgrade::Allowed-Origins {
    "${distro_id}:${distro_codename}";
    "${distro_id}:${distro_codename}-security";
    "${distro_id}:${distro_codename}-updates";
};

// Send email notifications (optional)
Unattended-Upgrade::Mail "your-email@example.com";

// Remove unused dependencies
Unattended-Upgrade::Remove-Unused-Dependencies "true";

// Automatic reboot if needed
Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-Time "02:00";

Install Essential Security Tools

# Install security and monitoring tools
sudo apt install -y curl wget htop ncdu ufw fail2ban rkhunter chkrootkit auditd

# Install development tools (if needed)
sudo apt install -y build-essential git vim

# Clean up
sudo apt autoremove -y
sudo apt autoclean

2. User Management and Access Control

Why Proper User Management Matters

Security Principle: Use separate user accounts for different purposes. Never use root for daily tasks.

Why not use root?

  • Accountability: Track who did what
  • Damage limitation: Compromised user β‰  full system compromise
  • Audit trails: Security monitoring can identify suspicious activity

Create a New Administrative User

# Create a new user (replace 'yourusername' with your preferred username)
sudo adduser yourusername

# Add user to sudo group for administrative access
sudo usermod -aG sudo yourusername

# Verify sudo access
su - yourusername
sudo whoami  # Should show 'root'

Secure SSH Access

Why harden SSH?

  • Brute force protection: Default SSH allows password authentication
  • Key-based auth: Cryptographically secure vs password guessing
  • Root login prevention: Eliminates direct root access attempts
# Switch to your new user
su - yourusername

# Generate SSH key pair (on your LOCAL machine)
ssh-keygen -t ed25519 -C "your-email@example.com"

# Copy public key to server
ssh-copy-id yourusername@your-server-ip

# Now disable password authentication
sudo nano /etc/ssh/sshd_config

Critical SSH security settings:

# Disable password authentication
PasswordAuthentication no

# Disable root login
PermitRootLogin no

# Use only secure protocols
Protocol 2

# Limit authentication attempts
MaxAuthTries 3

# Use secure ciphers
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com

# Disable X11 forwarding (unless needed)
X11Forwarding no

# Set idle timeout
ClientAliveInterval 300
ClientAliveCountMax 2
# Restart SSH service
sudo systemctl restart ssh

# Test SSH connection with key (from another terminal)
ssh yourusername@your-server-ip

Configure sudo Properly

Why sudo security matters:

  • Command logging: All sudo actions are logged
  • Time limits: Password not required repeatedly
  • Access control: Specific commands can be restricted
# Edit sudoers file safely
sudo visudo

# Add these security settings at the end:
# Security hardening
Defaults        env_reset
Defaults        mail_badpass
Defaults        secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
Defaults        logfile="/var/log/sudo.log"
Defaults        log_input,log_output
Defaults        requiretty

# Allow members of group sudo to execute any command
%sudo   ALL=(ALL:ALL) ALL

# Your user with passwordless sudo for specific commands (optional)
yourusername ALL=(ALL) NOPASSWD: /usr/sbin/service apache2 restart, /usr/sbin/service nginx restart

3. Firewall Configuration with UFW

Why Firewalls Are Essential

Network Security 101:

  • Default deny: Block everything except explicitly allowed traffic
  • Attack surface reduction: Fewer open ports = fewer attack vectors
  • Traffic monitoring: Log suspicious connection attempts

Real-World Example: The Mirai botnet (2016) exploited IoT devices with open Telnet ports, infecting 500,000+ devices.

# Enable UFW
sudo ufw enable

# Allow SSH (change port if you modified SSH config)
sudo ufw allow ssh
sudo ufw allow 22/tcp

# Allow HTTP/HTTPS for web servers
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

# Allow specific services as needed
# sudo ufw allow 3306/tcp  # MySQL
# sudo ufw allow 5432/tcp  # PostgreSQL

# Set default policies
sudo ufw default deny incoming
sudo ufw default allow outgoing

# Check status
sudo ufw status verbose

Advanced Firewall Rules

# Limit SSH connections (prevent brute force)
sudo ufw limit ssh

# Allow from specific IP ranges only
sudo ufw allow from 192.168.1.0/24 to any port 22

# Deny specific IPs
sudo ufw deny from 123.456.789.0/24

# Reload firewall
sudo ufw reload

4. Intrusion Prevention with Fail2Ban

Why Fail2Ban Matters

Automated Defense:

  • Brute force prevention: Bans IPs after failed login attempts
  • DDoS mitigation: Limits connection rates
  • Log analysis: Monitors system logs for attack patterns

Effectiveness: Reduces successful brute force attacks by 99%+.

# Enable and start Fail2Ban
sudo systemctl enable fail2ban
sudo systemctl start fail2ban

# Check status
sudo systemctl status fail2ban

# View banned IPs
sudo fail2ban-client status sshd

Configure Fail2Ban Jails

# Edit jail configuration
sudo nano /etc/fail2ban/jail.local

Add these security jails:

[sshd]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 3600

[nginx-http-auth]
enabled = true
port = http,https
filter = nginx-http-auth
logpath = /var/log/nginx/error.log
maxretry = 3
bantime = 3600

[nginx-noscript]
enabled = true
port = http,https
filter = nginx-noscript
logpath = /var/log/nginx/access.log
maxretry = 6
bantime = 3600

[nginx-badbots]
enabled = true
port = http,https
filter = nginx-badbots
logpath = /var/log/nginx/access.log
maxretry = 2
bantime = 3600

[nginx-noproxy]
enabled = true
port = http,https
filter = nginx-noproxy
logpath = /var/log/nginx/access.log
maxretry = 2
bantime = 3600
# Restart Fail2Ban
sudo systemctl restart fail2ban

# Monitor Fail2Ban logs
sudo tail -f /var/log/fail2ban.log

5. System Hardening and Security Monitoring

Kernel Security Parameters

Why harden the kernel?

  • Disable dangerous features: Prevent exploits using kernel vulnerabilities
  • Network protection: Block common network-based attacks
  • Resource limits: Prevent DoS attacks
# Edit sysctl configuration
sudo nano /etc/sysctl.conf

Add these security parameters:

# Network security
net.ipv4.tcp_syncookies = 1
net.ipv4.ip_forward = 0
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.all.log_martians = 1

# Disable IPv6 if not needed
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1

# File system security
fs.suid_dumpable = 0
kernel.dmesg_restrict = 1

# Memory protection
vm.mmap_min_addr = 65536

# ICMP security
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.icmp_ignore_bogus_error_responses = 1
# Apply changes
sudo sysctl -p

File Permissions Security

# Secure critical files
sudo chmod 600 /etc/shadow
sudo chmod 600 /etc/gshadow
sudo chmod 644 /etc/passwd
sudo chmod 644 /etc/group

# Secure SSH directory
sudo chmod 700 ~/.ssh
sudo chmod 600 ~/.ssh/authorized_keys

# Remove unnecessary files
sudo find /home -name ".rhost" -delete
sudo find /home -name ".shosts" -delete

Install and Configure Auditd

Why audit system activity?

  • Intrusion detection: Monitor file changes and system calls
  • Compliance: Meet security standards and regulations
  • Forensic analysis: Investigate security incidents
# Enable auditd
sudo systemctl enable auditd
sudo systemctl start auditd

# Check status
sudo systemctl status auditd

# View audit logs
sudo ausearch -m USER_LOGIN --start today

6. Security Monitoring and Alerting

Log Monitoring Setup

# Install logwatch for log analysis
sudo apt install -y logwatch

# Configure daily log reports
sudo nano /etc/cron.daily/logwatch

# Add this content:
#!/bin/bash
/usr/sbin/logwatch --output mail --mailto your-email@example.com --detail high

Automated Security Scanning

# Install security scanning tools
sudo apt install -y lynis rkhunter chkrootkit

# Run security audit
sudo lynis audit system

# Schedule weekly security scans
echo "0 2 * * 1 /usr/sbin/lynis audit system --cronjob" | sudo tee /etc/cron.weekly/lynis-scan

Rootkit Detection

# Run rootkit checks
sudo chkrootkit
sudo rkhunter --check

# Schedule regular scans
echo "0 3 * * * /usr/bin/chkrootkit" | sudo tee /etc/cron.daily/chkrootkit
echo "0 4 * * 1 /usr/bin/rkhunter --check --cronjob" | sudo tee /etc/cron.weekly/rkhunter

7. Backup and Recovery Strategy

Why Backups Matter

Data Protection:

  • Ransomware defense: Restore from clean backups
  • Accidental deletion: Recover lost files
  • System corruption: Restore from known good state

3-2-1 Rule: 3 copies, 2 media types, 1 offsite location.

Automated Backup Setup

# Install backup tools
sudo apt install -y rsync duplicity

# Create backup script
sudo nano /usr/local/bin/backup.sh
#!/bin/bash
# Automated backup script

BACKUP_DIR="/var/backups"
SOURCE_DIR="/home"
DESTINATION="user@backup-server:/backups"
DATE=$(date +%Y%m%d_%H%M%S)

# Create local backup
sudo rsync -avz --delete $SOURCE_DIR $BACKUP_DIR/daily_$DATE

# Encrypt and send to remote server
duplicity $SOURCE_DIR $DESTINATION/daily_$DATE \
  --encrypt-key YOUR_GPG_KEY_ID \
  --sign-key YOUR_GPG_KEY_ID

# Clean old backups (keep 7 daily, 4 weekly, 12 monthly)
duplicity remove-older-than 1M $DESTINATION
# Make executable and schedule
sudo chmod +x /usr/local/bin/backup.sh
echo "0 2 * * * /usr/local/bin/backup.sh" | sudo crontab -

8. Security Validation and Testing

Security Assessment Script

Create a comprehensive security check script:

sudo nano /usr/local/bin/security-check.sh
#!/bin/bash
# Comprehensive security validation script

echo "πŸ”’ Ubuntu Security Hardening Check"
echo "=================================="

# Check SSH configuration
echo "SSH Security:"
if grep -q "PasswordAuthentication no" /etc/ssh/sshd_config; then
    echo "βœ… Password authentication disabled"
else
    echo "❌ Password authentication still enabled"
fi

if grep -q "PermitRootLogin no" /etc/ssh/sshd_config; then
    echo "βœ… Root login disabled"
else
    echo "❌ Root login still enabled"
fi

# Check firewall
echo -e "\nFirewall Status:"
sudo ufw status | grep -E "(Status|22|80|443)"

# Check Fail2Ban
echo -e "\nFail2Ban Status:"
sudo fail2ban-client status sshd | grep "Currently banned"

# Check system updates
echo -e "\nSystem Updates:"
apt list --upgradable 2>/dev/null | wc -l | xargs echo "packages can be upgraded"

# Check running services
echo -e "\nCritical Services:"
systemctl is-active ssh && echo "βœ… SSH running" || echo "❌ SSH not running"
systemctl is-active ufw && echo "βœ… Firewall running" || echo "❌ Firewall not running"
systemctl is-active fail2ban && echo "βœ… Fail2Ban running" || echo "❌ Fail2Ban not running"

echo -e "\nSecurity check complete!"
# Make executable
sudo chmod +x /usr/local/bin/security-check.sh

# Run security check
sudo /usr/local/bin/security-check.sh

9. Ongoing Security Maintenance

Daily Security Tasks

# Quick daily security check
sudo /usr/local/bin/security-check.sh

# Check system logs for suspicious activity
sudo journalctl -p err --since today

# Monitor disk usage
df -h

# Check for failed login attempts
sudo lastb | head -10

Weekly Security Tasks

# Full system update
sudo apt update && sudo apt upgrade -y

# Run security scans
sudo lynis audit system
sudo rkhunter --check

# Review user accounts
sudo lastlog

# Check sudo usage
sudo cat /var/log/sudo.log | tail -20

Monthly Security Tasks

# Full backup verification
sudo /usr/local/bin/backup.sh

# Review firewall rules
sudo ufw status verbose

# Audit user permissions
sudo find /home -type f -perm 777

# Check for world-writable files
sudo find / -type f -perm 777 2>/dev/null | head -10

10. Troubleshooting Common Issues

SSH Connection Problems

# Check SSH service status
sudo systemctl status ssh

# Verify SSH configuration
sudo sshd -t

# Check listening ports
sudo netstat -tlnp | grep :22

# Restart SSH service
sudo systemctl restart ssh

Firewall Issues

# Check UFW status
sudo ufw status

# Reset firewall (CAUTION: will disconnect SSH)
sudo ufw reset
sudo ufw enable
sudo ufw allow ssh

# Check iptables rules
sudo iptables -L

Fail2Ban Problems

# Check Fail2Ban status
sudo fail2ban-client status

# View jail status
sudo fail2ban-client status sshd

# Unban an IP
sudo fail2ban-client set sshd unbanip 192.168.1.100

# Restart Fail2Ban
sudo systemctl restart fail2ban

Conclusion

Congratulations! You’ve successfully hardened your Ubuntu LTS server with enterprise-grade security practices. Your server now has:

  • βœ… Automatic security updates
  • βœ… Secure user management with proper sudo configuration
  • βœ… Hardened SSH with key-based authentication
  • βœ… Firewall protection with UFW
  • βœ… Intrusion prevention with Fail2Ban
  • βœ… System monitoring with auditd and logging
  • βœ… Automated backups with encryption
  • βœ… Security scanning and validation

Security Mindset

Remember that security is an ongoing process, not a one-time setup. Regularly:

  • Monitor logs for suspicious activity
  • Apply updates promptly
  • Review access controls periodically
  • Test backups regularly
  • Stay informed about new threats

Next Steps

  1. Set up monitoring alerts (email notifications for security events)
  2. Configure log shipping to a central logging server
  3. Implement SIEM (Security Information and Event Management) if managing multiple servers
  4. Consider intrusion detection systems like OSSEC or Snort for advanced protection

Your Ubuntu server is now significantly more secure and ready for production use! πŸ›‘οΈβœ¨

Security Resources

  • Ubuntu Security Notices: https://ubuntu.com/security/notices
  • NIST Cybersecurity Framework: https://www.nist.gov/cyberframework
  • OWASP Security Guidelines: https://owasp.org/
  • Linux Security Best Practices: https://www.cisecurity.org/

Stay secure, stay vigilant! πŸ”’

Written on October 9, 2025