Most data breaches happen because of missing basics, not zero-day exploits. In support tickets I've handled, the pattern is always the same: weak passwords, unpatched software, no monitoring, and services exposed to the internet that shouldn't be. The good news? Six controls, properly configured, stop the majority of attacks before they start.
This is a practical walkthrough. You'll implement access controls, encrypt sensitive data, set up monitoring, automate patching, configure backups, and segment your network. Each section includes the commands and config you need.
1. Lock down access with least privilege
Start here. Every breach investigation traces back to compromised credentials or excessive permissions. Your goal is simple: every user and service gets exactly the access it needs, nothing more.
Audit current access
Before you restrict anything, see what you're working with.
# List all users with shell access
grep -vE '(nologin|false)$' /etc/passwd
# Check sudo permissions
sudo cat /etc/sudoers
sudo ls -la /etc/sudoers.d/
# List SSH authorized keys for all users
for user in $(cut -d: -f1 /etc/passwd); do
key_file="/home/$user/.ssh/authorized_keys"
[ -f "$key_file" ] && echo "$user: $(wc -l < $key_file) keys"
done
Remove any accounts you don't recognize. Disable shell access for service accounts that don't need it by changing their shell to /sbin/nologin.
Implement SSH key authentication
Password authentication is a liability. Disable it.
On your local machine, generate a key if you don't have one:
ssh-keygen -t ed25519 -C "[email protected]"
Copy it to the server:
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@your-server
Test the key-based login, then disable password auth. Edit /etc/ssh/sshd_config:
PasswordAuthentication no
PermitRootLogin prohibit-password
PubkeyAuthentication yes
Restart SSH:
sudo systemctl restart sshd
Set up sudo with limited scope
Don't give users full sudo. Grant specific commands instead. Create a file /etc/sudoers.d/limited-access:
# Allow www-data to restart web services
www-data ALL=(ALL) NOPASSWD: /bin/systemctl restart nginx, /bin/systemctl restart php*-fpm
# Allow backup user to run backup script only
backup ALL=(ALL) NOPASSWD: /usr/local/bin/backup.sh
Validate syntax before saving:
sudo visudo -c -f /etc/sudoers.d/limited-access
Enforce multi-factor authentication
For SSH, use Google Authenticator PAM module.
sudo apt install libpam-google-authenticator # Debian/Ubuntu
sudo yum install google-authenticator # RHEL/CentOS
Run the setup as each user:
google-authenticator
Save the emergency codes. Edit /etc/pam.d/sshd and add:
auth required pam_google_authenticator.so
In /etc/ssh/sshd_config, set:
ChallengeResponseAuthentication yes
AuthenticationMethods publickey,keyboard-interactive
Restart SSH. Now every login requires both the key and a TOTP code.
2. Encrypt data at rest and in transit
Encryption is non-negotiable for any system holding user data, credentials, or payment information.
Enable full-disk encryption
If you're provisioning a new server, use LUKS during installation. For existing systems, encrypting the root partition requires a rebuild, but you can encrypt data partitions.
Create an encrypted volume:
sudo cryptsetup luksFormat /dev/sdb1
sudo cryptsetup luksOpen /dev/sdb1 encrypted_data
sudo mkfs.ext4 /dev/mapper/encrypted_data
Mount it:
sudo mkdir /mnt/secure
sudo mount /dev/mapper/encrypted_data /mnt/secure
To auto-mount at boot, add a key file and update /etc/crypttab and /etc/fstab. Store the key file with restricted permissions.
Encrypt database connections
For MySQL/MariaDB, generate SSL certificates:
sudo mysql_ssl_rsa_setup --uid=mysql
Edit /etc/mysql/my.cnf:
[mysqld]
require_secure_transport=ON
ssl-ca=/var/lib/mysql/ca.pem
ssl-cert=/var/lib/mysql/server-cert.pem
ssl-key=/var/lib/mysql/server-key.pem
Restart MySQL. Verify:
mysql -u root -p -e "SHOW VARIABLES LIKE '%ssl%';"
Update application connection strings to use SSL. Most drivers support a ?ssl=true parameter.
Force HTTPS everywhere
Get a free Let's Encrypt certificate:
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com
Certbot configures your Nginx or Apache vhost automatically. Add a redirect for HTTP to HTTPS in your vhost config:
server {
listen 80;
server_name yourdomain.com www.yourdomain.com;
return 301 https://$server_name$request_uri;
}
Set up auto-renewal:
sudo systemctl enable certbot.timer
sudo systemctl start certbot.timer
3. Monitor everything and alert on anomalies
You can't respond to what you don't see. Real-time monitoring catches intrusions before they escalate.
Set up centralized logging
Install and configure rsyslog to forward logs to a central server or external service. On each server, edit /etc/rsyslog.conf:
*.* @@log-server.example.com:514
Restart rsyslog:
sudo systemctl restart rsyslog
If you don't have a log server, use a managed service or set up a simple ELK stack (Elasticsearch, Logstash, Kibana) or Graylog instance.
Install and configure fail2ban
Fail2ban watches logs for repeated failed login attempts and bans the offending IPs.
sudo apt install fail2ban
Create /etc/fail2ban/jail.local:
[DEFAULT]
bantime = 3600
findtime = 600
maxretry = 5
[sshd]
enabled = true
port = ssh
logpath = /var/log/auth.log
[nginx-limit-req]
enabled = true
filter = nginx-limit-req
logpath = /var/log/nginx/error.log
Restart fail2ban:
sudo systemctl restart fail2ban
Check banned IPs:
sudo fail2ban-client status sshd
Enable auditd for file integrity monitoring
Auditd tracks file access and changes to critical system files.
sudo apt install auditd
Add rules in /etc/audit/rules.d/audit.rules:
-w /etc/passwd -p wa -k passwd_changes
-w /etc/shadow -p wa -k shadow_changes
-w /etc/sudoers -p wa -k sudoers_changes
-w /var/www/ -p wa -k webroot_changes
Reload rules:
sudo augenrules --load
Search audit logs:
sudo ausearch -k passwd_changes
Set up email alerts for critical events
Configure your server to send mail through an SMTP relay. Install msmtp:
sudo apt install msmtp msmtp-mta
Create /etc/msmtprc:
defaults
auth on
tls on
tls_trust_file /etc/ssl/certs/ca-certificates.crt
account default
host smtp.example.com
port 587
from [email protected]
user [email protected]
password your-password
Set permissions:
sudo chmod 600 /etc/msmtprc
Test:
echo "Test alert" | mail -s "Test" [email protected]
Configure fail2ban and auditd to email on specific events by adding action lines to their configs.
4. Automate patching and vulnerability scanning
Unpatched software is the easiest entry point. Automate updates so you're never running vulnerable versions.
Enable automatic security updates
On Debian/Ubuntu:
sudo apt install unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgrades
Edit /etc/apt/apt.conf.d/50unattended-upgrades to enable automatic reboots if needed:
Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-Time "03:00";
On RHEL/CentOS, use yum-cron or dnf-automatic:
sudo yum install yum-cron
sudo systemctl enable yum-cron
sudo systemctl start yum-cron
Edit /etc/yum/yum-cron.conf:
apply_updates = yes
Scan for vulnerabilities with Lynis
Lynis is an open-source security auditing tool.
sudo apt install lynis
Run a full audit:
sudo lynis audit system
Review the report in /var/log/lynis.log and address high-priority findings. Run this monthly and track improvements.
Keep web applications updated
For WordPress, enable automatic updates in wp-config.php:
define( 'WP_AUTO_UPDATE_CORE', true );
add_filter( 'auto_update_plugin', '__return_true' );
add_filter( 'auto_update_theme', '__return_true' );
For custom apps, set up a CI/CD pipeline that runs dependency checks (e.g., npm audit, composer audit) and deploys updates automatically after passing tests.
5. Back up everything and test restores
Backups are your last line of defense. If everything else fails, a clean backup gets you back online.
Implement the 3-2-1 rule
Three copies of your data, on two different media types, with one copy off-site. For servers, that means:
- Live data on the server (copy 1)
- Local backup on a separate disk or NAS (copy 2)
- Remote backup to cloud storage or another datacenter (copy 3)
Automate backups with restic
Restic is a fast, encrypted backup tool with support for many backends.
sudo apt install restic
Initialize a repository:
restic -r /mnt/backup init
For remote backups, use S3-compatible storage:
export AWS_ACCESS_KEY_ID="your-key"
export AWS_SECRET_ACCESS_KEY="your-secret"
restic -r s3:s3.amazonaws.com/your-bucket init
Create a backup script /usr/local/bin/backup.sh:
#!/bin/bash
set -e
export RESTIC_REPOSITORY="s3:s3.amazonaws.com/your-bucket"
export RESTIC_PASSWORD="your-restic-password"
restic backup /var/www /etc /home --exclude="*.tmp" --exclude="cache"
restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune
Make it executable:
sudo chmod +x /usr/local/bin/backup.sh
Schedule it with cron:
sudo crontab -e
Add:
0 2 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1
Test restores monthly
A backup you've never restored is just wishful thinking. Pick a random file and restore it:
restic -r s3:s3.amazonaws.com/your-bucket snapshots
restic -r s3:s3.amazonaws.com/your-bucket restore latest --target /tmp/restore-test --include /var/www/index.html
Verify the file matches the live version. Document the restore process so anyone on your team can do it under pressure.
6. Segment your network and limit exposure
Network segmentation contains breaches. If an attacker compromises one system, they shouldn't be able to pivot to everything else.
Use a firewall to restrict traffic
On each server, enable ufw (Uncomplicated Firewall):
sudo apt install ufw
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow ssh
sudo ufw allow http
sudo ufw allow https
sudo ufw enable
Check status:
sudo ufw status verbose
For database servers, only allow connections from your application servers:
sudo ufw allow from 192.168.1.10 to any port 3306
sudo ufw allow from 192.168.1.11 to any port 3306
Isolate services with VLANs or VPCs
If you're on a VPS or cloud platform, create separate networks for different tiers:
- Public subnet: web servers, load balancers
- Private subnet: application servers, databases
- Management subnet: admin access, monitoring tools
In AWS, use Security Groups to enforce this. In a cPanel environment, you can achieve basic segmentation with firewall rules and private IPs.
Disable unused services and ports
List listening services:
sudo ss -tulnp
For each service you don't recognize, identify the process and decide whether it's needed. Disable and mask unused services:
sudo systemctl stop cups
sudo systemctl disable cups
sudo systemctl mask cups
Run a port scan from outside
Use nmap from an external machine to see what's visible to the internet:
nmap -sV -p- your-server-ip
Anything open that you didn't expect is a problem. Close it.
What if a breach still happens?
These six controls dramatically reduce your attack surface, but no system is invulnerable. Have an incident response plan ready: a checklist of who to notify, how to isolate compromised systems, where your backups live, and how to restore service. Run a tabletop exercise once a year so your team knows the drill.
The controls you just implemented give you visibility and containment. If an attacker gets in, your monitoring alerts you immediately, your segmentation limits their movement, and your backups let you rebuild clean. That's the difference between a contained incident and a catastrophic breach.
Frequently asked questions
How often should I rotate SSH keys?
Rotate them when someone leaves your team or if you suspect compromise. Otherwise, annual rotation is sufficient if keys are properly protected.
Do I need a dedicated SIEM for monitoring?
Not at small scale. Fail2ban, auditd, and centralized logging cover the basics. Add a SIEM when you're managing dozens of servers or have compliance requirements.
Can I automate all six controls with a single tool?
No single tool does everything well. Use specialized tools for each layer—SSH hardening, restic for backups, ufw for firewalls, unattended-upgrades for patching.
What's the biggest mistake you see in breach post-mortems?
No monitoring. Organizations run blind for months while attackers exfiltrate data, because nobody was watching the logs.
Should I segment applications on the same server?
Yes, use containers or separate user accounts with strict file permissions. Even on a single server, isolation limits blast radius.
Where to start today
Don't try to implement all six controls at once. Start with access management—lock down SSH, enforce MFA, audit permissions. That alone stops most opportunistic attacks. Then add monitoring so you can see what's happening. The rest follows naturally once you have visibility and control over who gets in.
These controls work because they're layered. Each one covers weaknesses in the others, and together they force an attacker to get past multiple defenses. Most give up and move to easier targets. The ones who don't? Your monitoring catches them, your segmentation slows them down, and your backups let you recover.
Pick the control that's weakest on your systems right now and spend an hour fixing it. Then move to the next. In a few weeks, you'll have a foundation that holds up under real attacks.
