A default EC2 instance is not a secure instance. Every production server I've deployed — whether for a clinic SaaS platform or an internal reporting system — goes through the same hardening pass before it ever sees real traffic. Here is that checklist, in the order I actually apply it.
1. IAM: Never Use Root, Never Use Long-Lived Keys
The single biggest AWS mistake I still see is applications using root account access keys. Instead:
- Create an IAM role and attach it to the EC2 instance — no access keys stored on disk at all.
- Scope the role's policy to the exact actions the app needs (e.g.
s3:GetObjecton one bucket, nots3:*). - Enable MFA on every human IAM user, and never share the root account credentials.
2. Security Groups: Default Deny
Treat security groups as a firewall with a default-deny posture. Open only what is needed:
Never leave port 22 open to the world
SSH open to 0.0.0.0/0 is the #1 way EC2 instances get compromised by automated bots within minutes of launch. Restrict it to your office/VPN IP, or better, use AWS Systems Manager Session Manager and remove inbound SSH entirely.
3. SSH Hardening
- Disable password authentication — key-based auth only.
- Disable root login (
PermitRootLogin no). - Change the default port only as a minor speed bump, not a real control.
- Install
fail2banto auto-block repeated failed login attempts.
4. Encryption Everywhere
Encrypt EBS volumes at rest by default on every new instance. Enforce HTTPS-only with a redirect from port 80, and terminate TLS at an Application Load Balancer with a free ACM certificate rather than managing certs manually on each box.
5. Patch Management & Monitoring
Enable automatic security updates for the OS, and put CloudWatch alarms on CPU, disk, and failed login attempts. For anything internet-facing, put AWS WAF in front of it to filter common attack patterns (SQLi, XSS, bad bots) before they reach your application code.
6. Backups & the "Assume Breach" Mindset
Automated daily snapshots aren't optional — they're the difference between a bad afternoon and a resume-generating event. Assume that, eventually, something will get compromised, and make sure a compromised instance can be terminated and rebuilt from a clean AMI in minutes, not days.