Setting Up a Secure Linux Server
When you expose a server to the internet, it gets attacked within minutes. Literally. Bots constantly scan the entire internet looking for vulnerabilities. Here's how I secured my Ubuntu 24.04 server from the ground up.
1. SSH Key Authentication
The first thing I did was disable password authentication and use SSH keys instead.
Why? Passwords can be brute-forced. SSH keys with 4096-bit encryption would take billions of years to crack.
Implementation
# On my local machine, generate a key
ssh-keygen -t ed25519 -C "hamza@homelab"
# Copy it to the server
ssh-copy-id hamza@85.190.102.100
# Test it works
ssh hamza@85.190.102.100
Then I edited /etc/ssh/sshd_config:
PasswordAuthentication no
PermitRootLogin no
PubkeyAuthentication yes
Lesson learned: Always keep one SSH session open while testing! I could have locked myself out.
2. Firewall Configuration (UFW)
The principle is simple: deny everything by default, then explicitly allow only what you need.
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp # SSH
sudo ufw allow 80/tcp # HTTP
sudo ufw allow 443/tcp # HTTPS
sudo ufw enable
This creates three layers of protection:
- Network firewall blocks unwanted ports
- SSH keys prevent brute-force attacks
- Separate user accounts limit damage from compromises
3. Fail2ban - Automated Intrusion Prevention
Fail2ban monitors logs and automatically bans IPs after repeated failed login attempts.
sudo apt install fail2ban
sudo systemctl enable fail2ban
sudo systemctl start fail2ban
Configuration in /etc/fail2ban/jail.local:
[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 3
[sshd]
enabled = true
port = 22
Real-World Results
After setting this up, I checked what it caught:
sudo fail2ban-client status sshd
In just 2 weeks, fail2ban blocked over 50 IP addresses attempting brute-force attacks on my SSH port!
4. User Management
Never run as root. Create a personal user account with sudo privileges:
sudo adduser hamza
sudo usermod -aG sudo hamza
This follows the principle of least privilege - give minimum permissions needed, nothing more.
5. Automated Monitoring
I created bash scripts to monitor disk space and memory usage, scheduled with cron:
# Cron jobs
0 */6 * * * /home/hamza/scripts/disk_check.sh
0 * * * * /home/hamza/scripts/memory_check.sh
These scripts alert me if disk usage exceeds 80% or memory usage exceeds 85%. Automation means I don't have to remember to check manually.
What I Learned
- Defense in depth: No single security measure is enough. Layer multiple protections.
- Automation is critical: You can't manually monitor logs 24/7.
- Testing is essential: Always verify configurations before applying them.
- Logs tell the story: Everything important should be logged.
Common Mistakes to Avoid
- Not keeping a backup SSH session open while changing SSH config
- Enabling UFW before allowing SSH port (locked out!)
- Wrong permissions on .ssh directory (SSH keys won't work)
- Forgetting to restart services after config changes
Next Steps
With a secure foundation in place, the next step is building a production web stack. In my next post, I'll cover:
- Nginx configuration as web server and reverse proxy
- SSL/TLS certificate setup
- Security headers
- Rate limiting
Questions or feedback? Feel free to reach out!