Lesson 18 · 27 min read
Security Hardening and Backups
Locking down what you built: patching, secrets, least privilege, automated backups and tested restores.
What you'll learn in this lesson
- Concept: layers, not a single lock
- Why this matters
- Shared responsibility: what AWS secures, what you secure
- The five ways small teams actually get hurt
- Layer 1 — identity: keys, MFA, roles
- Layer 2 — network: shrink the attack surface
- Layer 3 — no SSH: Session Manager
- Layer 4 — the server: patching and IMDSv2
- Layer 5 — secrets management
- Layer 6 — data: encryption and access
- Layer 7 — the application and its dependencies
- Layer 8 — detection: CloudTrail, GuardDuty, Config
- Backups: the part that saves the company
- Test the restore — the drill
- When a key leaks: the first hour
- The hardening checklist
- What this costs
- Interview corner
- Practice task before Lesson 19
- Conclusion
Concept
Security is not a product that you switch on. It is the habit of making every part of the system hard to misuse and cheap to recover. This lesson has two halves, and each half is a question:
- Hardening — “how do I make it hard for the wrong person to do harm?” Hardening means closing weak points so the system is harder to attack.
- Backups — “when something bad happens anyway, how do I get back?”
A castle is not safe because of one thick wall. It has a moat, walls, a gatehouse, guards who check who comes in, locked inner rooms and a lookout. Sieges sometimes succeed, so it also has a hidden store of food and a practised escape route. This is called defence in depth. Assume that any single layer will fail one day. Make sure the next layer holds.
Do not ask “could someone get in?” Ask “when one credential or one server is compromised, how far can the attacker go, and how fast would we notice?” Everything below answers that question. Keep the blast radius small. The blast radius is how much damage one break-in can cause. Detect problems fast. Make recovery easy.
- IAM (Identity and Access Management) is the AWS service that decides who may do what in your account. An IAM role is a set of permissions that a machine or person can borrow for a short time, with no password or long-term key.
- Least privilege is the rule that every user or machine gets only the permissions it needs, and nothing more.
- A Security Group is a virtual firewall for an AWS resource. It lists which network traffic may come in. A subnet is a smaller part of your private network. A private subnet has no direct route from the internet.
- ALB (Application Load Balancer) is the service that receives web traffic and spreads it over your servers. EC2 is the AWS service that rents virtual servers.
- TLS is the protocol that encrypts data as it travels over a network. It is the “S” in HTTPS. Encryption turns data into code that only a key can read.
- A region is a part of the world where AWS has data centres, for example Mumbai (ap-south-1).
- CI (continuous integration) is a system that builds and tests your code automatically on every change.
Why this matters
You now hold your customers' data, their logins and their payments. The results of a mistake are real. Laws such as DPDP in India and GDPR in Europe require you to report a data breach. You can lose your customers' trust, and a small company often does not survive. Attackers do not pick you personally. Their programs scan the whole internet automatically. A server with a weak point is found in minutes, not months.
There is good news. Most real incidents come from a short list of ordinary mistakes. If you fix that list, you are safer than most companies.
Shared responsibility: what AWS secures, what you secure
| AWS is responsible for (“security of the cloud”) | You are responsible for (“security in the cloud”) | |
|---|---|---|
| Physical | Buildings, power, hardware, network cabling | — |
| Virtualisation | Hypervisor, isolation between customers | — |
| EC2 servers | Nothing inside the operating system | OS patches (security updates), SSH, the firewall, your app and the data |
| RDS / ElastiCache | Patching of the OS and the database engine, and the hardware | Who can connect, users and passwords, the encryption choice, how long backups are kept, and your queries |
| S3 | Durability (not losing files) and the service itself | Bucket policies, public access, encryption, and what you put in the bucket |
| IAM | The service works correctly | Every policy, key, user and role. All of it |
Most cloud breaches come from the customer's own settings and stolen credentials, not from failures inside AWS. The right-hand column is your job.
The five ways small teams actually get hurt
| Threat | What it looks like | The defence |
|---|---|---|
| Leaked credentials | Access keys in a public repo, a .env file inside a Docker image, or a stolen laptop that is still logged in to the CLI | Use roles instead of keys (Lessons 5, 14), use MFA, scan for secrets, and rotate (replace) a key if you have any doubt |
| Exposed services | A database, Redis or SSH open to 0.0.0.0/0 (the whole internet), or an S3 bucket set to public | Private subnets, Security Group references, S3 Block Public Access, and no port 22 |
| Unpatched software | A server or an npm package with a known flaw that attackers already use | Automatic OS updates, dependency scanning, and frequent image rebuilds |
| Weak or shared access | One admin login shared by five people, former staff who still have access, or no MFA | A separate login for each person, least privilege, an access review every quarter, and removal of leavers on the same day |
| No backups (or untested ones) | Ransomware, a bad migration or a mistaken delete happens, and there is nothing to restore from | Automatic backups, versioning, copies in another account, and a practised restore |
Notice how much you have already done. You use roles instead of keys (Lesson 5). Your databases are private (Lessons 6 and 8). Your buckets are private (Lesson 11). Your deploy role has narrow permissions (Lesson 14). This lesson closes the remaining gaps. It also adds detection and recovery.
Layer 1 — identity: keys, MFA, roles
Identity means who or what is allowed to log in. It is the front door, and attackers use it most often. First check it with AWS's own tools. Then apply the rules:
- In IAM, open the Credential report. It is a CSV file (a simple table) with one row for every user. It shows password age, whether MFA is on, and the age and last use of each access key. MFA (multi-factor authentication) means a second proof of identity, such as a code from a phone app. Look for users with no MFA, keys older than 90 days, and keys that were never used.
- Check the user list itself. Does each person still work here? Does each identity still need to exist?
- IAM Access Analyzer (the external access check is free) finds buckets, roles and keys that are shared outside your account. It can also write least-privilege policies (policies that give only the access that is really used) from real usage.
- Root user: the root user is the all-powerful first account. Turn MFA on (Lesson 0), create no access keys, and never use it for daily work. Check this in the credential report. The
<root_account>row must showaccess_key_1_active = false. - MFA for every person, preferably with a hardware key or a passkey. A hardware key is a small USB device that proves you are you. A passkey is a login method that replaces the password. An AWS login with only a password is one stolen password away from total loss.
- People use IAM Identity Center (single sign-on, SSO). Machines use roles. SSO lets people sign in once with a central account. If any long-lived access key still exists, rotate it, give it the minimum permissions, and name an owner for it.
- Remove people on the day they leave. This is why groups matter (Lesson 5). One action removes one person, and no access is left over.
- Review permissions using real data. IAM shows when each service was last used (“last accessed”). If a policy allows 40 services but only 3 are used, make the policy smaller.
Asking people to turn on MFA is only a wish. A policy that denies everything except managing their own MFA, until they sign in with MFA, is a real control. AWS publishes a ready-made example called “Allow users to manage their own MFA”. It uses a deny rule with BoolIfExists aws:MultiFactorAuthPresent = false. Attach it to the Developers group.
Stop secrets reaching Git at all
- Turn on GitHub secret scanning and push protection. Push protection blocks a push that contains an AWS key. It is free for public repositories. For private repositories it is a paid GitHub feature (GitHub Secret Protection).
- Also add a pre-commit scanner such as
gitleakson your own computer. It checks your code for secrets before each commit. - AWS also scans public GitHub. When it finds your key, it restricts the key and emails you. But attack bots are faster than email. If a key ever reaches a public repository, treat it as stolen, however fast you delete the commit. Rotate it.
Layer 2 — network: shrink the attack surface
The safest port is a closed port. (A port is a numbered door on a server that a service listens on.) Check what is open to the world. The list should be very short:
In EC2, go to Security Groups. Look at every inbound rule whose source is 0.0.0.0/0. In our design the only such rule should be alb-sg on ports 80 and 443. If web-sg, db-sg or cache-sg appears, or any group has port 22, 5432 or 6379 open to the world, fix it now.
| Should be open to the internet | Must never be |
|---|---|
| The ALB: ports 80 and 443 only | SSH (22), RDP (3389, remote desktop for Windows), databases (5432, 3306), Redis (6379), the app port (3000), Elasticsearch (9200), and admin dashboards |
- Keep databases and caches in private subnets with no public IP. Only
web-sgmay reach them (Lessons 6, 8, 16). - App servers accept traffic only from the ALB (Lesson 12). If they have public IPs, those IPs are for outbound traffic, not inbound.
- Add AWS WAF (Web Application Firewall) on the ALB or CloudFront. It blocks common web attacks, such as SQL injection (an attack that sneaks database commands into a form field), bad bots and too many requests from one IP. It costs about $5 a month plus $1 per rule and $0.60 per million requests. Start with the AWS-managed core rule set.
- Turn on VPC Flow Logs. They record the network connections in your VPC, and storing them in S3 is cheap. Later you can answer “who talked to this server?”
Layer 3 — no SSH: Session Manager
In Lesson 7 you opened port 22 to your IP address. That is fine for learning. It is a poor choice for production. Keys are files, and files get copied. Your IP address changes. And every scanner on earth probes an open port 22. AWS Systems Manager Session Manager gives you a shell on a server with no open port, no key pair and no bastion host. (A bastion host is a server that you log in to first, in order to reach other servers.) The SSM agent on the server connects out to AWS, and you connect using your IAM identity.
| SSH on port 22 | Session Manager | |
|---|---|---|
| Inbound port | Port 22 must be open | None |
| Credentials | A .pem key file that you must store and share | Your IAM/SSO login, with MFA |
| Who can access | Anyone holding the key | Anyone your IAM policy names |
| Audit trail | Only what you log yourself | Every session logged (CloudTrail, optionally full keystrokes to S3/CloudWatch) |
| Revoking access | Change the key on every server | Remove the IAM permission |
Give the instance role permission to talk to SSM
Attach the AWS-managed policy
AmazonSSMManagedInstanceCoretomyapp-ec2-role. The migrations in Lesson 14 already need it. The agent is preinstalled on Ubuntu 24.04 images. It needs only this permission and outbound HTTPS. After a few minutes the instance shows as Online in Systems Manager, under Fleet Manager.Install the Session Manager plugin and connect
Install the Session Manager plugin for the AWS CLI on your laptop. Then run:
On your laptopaws ssm start-session --target i-0abc123def456Opens a shell on the server with no port 22 and no key pair. The Connect button, then Session Manager, in the console does the same in the browser
The same tool can also forward a port. This replaces Lesson 8's SSH tunnel to the database, and needs no SSH at all.
Close port 22 and drop the key pair
Once
start-sessionworks, delete the SSH rule fromweb-sg. The new launch templates already have no key pair (Lesson 12).
Layer 4 — the server: patching and IMDSv2
In our design the server is disposable, and that helps security. Think of servers as cattle, not pets. You do not nurse each one. The best way to patch them is to replace, not repair. On a schedule, rebuild from the latest AMI (the saved server disk image) and the latest app image. Then servers never collect drift (small unplanned changes) or old security holes.
- Automatic OS security updates as a safety net for servers that live a long time.
Install the unattended-upgrades package in the user-data script (the script that runs when the server first boots) and enable it. Then Ubuntu applies security updates by itself every day.
- Replace servers regularly. Run an instance refresh (Lesson 12) at least once a month, even when the code has not changed. Then servers get the latest AMI and OS patches. A scheduled GitHub Actions workflow can do this for you.
- IMDSv2 required (set since Lesson 7). IMDS is the instance metadata service. A server can ask it for its own details, including temporary credentials. Version 2 needs a session token, so a bug in your app cannot easily be turned into credential theft. Check this on every instance.
In the EC2 instance list, add the IMDSv2 column. Every instance should say Required. Any instance that says Optional still accepts the old, weaker version. Fix it in place: choose Actions, then Instance settings, then Modify instance metadata options.
- Encrypted disks by default. In the EC2 settings, turn on “EBS encryption by default” for the region (EBS is the disk service for EC2). Then no disk is ever created without encryption. The command is
aws ec2 enable-ebs-encryption-by-default. It applies to one region at a time. - An instance role with least privilege. Review
myapp-ec2-role. It should read its own parameters, pull its own image and write its own logs. It should do nothing else.
Layer 5 — secrets management
A secret is anything that gives access. Examples are database passwords, API keys, signing keys and tokens. Here is a ladder of practices, from worst to best:
| Where the secret lives | Verdict |
|---|---|
| Hard-coded in source, committed to Git | It leaks the day the repository is shared. Never do this |
| Baked into a Docker image or a build argument | Anyone who can pull the image can read it |
| A .env file copied to the server by hand | It works, but there is no audit trail and no rotation. It is easy to lose, and it differs from server to server |
| SSM Parameter Store SecureString (an encrypted parameter), read at boot with the instance role | Good: encrypted, controlled by IAM, audited and free |
| Secrets Manager with automatic rotation | Best for database passwords. It changes them on a schedule |
| SSM Parameter Store | Secrets Manager | |
|---|---|---|
| Price | Free (standard parameters) | About $0.40 per secret per month, plus API calls |
| Automatic rotation | No (you must build it yourself) | Yes, built in for RDS and other services |
| Best for | App settings and most secrets | Database credentials and anything that must be rotated |
Our setup already uses Parameter Store (Lesson 12). The upgrade worth making is for the database password. Let RDS manage it in Secrets Manager with rotation. Then it never appears in the Terraform state, in chat messages or on anyone's clipboard.
In RDS, choose Modify and tick Manage master credentials in AWS Secrets Manager. RDS creates the secret and rotates it. The app, or its boot script, reads the secret with permission from its role. The password never has to be copied anywhere.
A secret that never changes is quietly copied to more places every year. Rotation means replacing a secret with a new one on a schedule. It limits how long a leaked secret stays useful. It also forces you to know where each secret is used. If rotating a credential feels risky, that risk is itself a problem you have found.
Layer 6 — data: encryption and access
| Store | At rest | In transit | Also |
|---|---|---|---|
| RDS | Storage encryption on (Lesson 8; you cannot add it later to an existing instance) | TLS enforced (the default in Postgres 15 and later) | Private subnet, deletion protection, and database users with least privilege |
| S3 | Default encryption (on by default now) | Deny requests that do not use TLS, in the bucket policy | Block Public Access and versioning |
| EBS disks | Encrypted (the default-on setting above) | — | Encrypted snapshots |
| ElastiCache | At-rest encryption on (Lesson 16) | TLS + AUTH | Private subnet |
| Between the browser and you | — | HTTPS only, redirect HTTP, and HSTS (a header that tells browsers to always use HTTPS; Lesson 10) | Secure and HttpOnly cookies |
Encryption at rest means data is encrypted while stored on disk. It protects you from a stolen disk or snapshot. It does not protect you from a compromised application, because the app can read the decrypted data. That layer is access control, which decides who and what may read which rows, files and keys. Here are two points for a multi-tenant product (one app that serves many customers):
- Tenant isolation is a security control, not an extra feature. Every query filters by tenant. Consider PostgreSQL row-level security as a second layer. It is a database rule that limits which rows a user can see. It still protects you if a developer forgets a
WHEREclause. S3 keys and cache keys also include the tenant (Lessons 11, 16). - Force TLS on the bucket. Add a deny statement for requests where
aws:SecureTransportis false.
Layer 7 — the application and its dependencies
Infrastructure controls cannot fix a vulnerable app. These habits give the most protection for the least effort:
- Keep dependencies up to date. A dependency is a package that your code uses. Turn on Dependabot (
.github/dependabot.yml). It opens update requests for npm packages, GitHub Actions and Docker base images. Most breaches through code use flaws that were fixed months earlier. - Scan in the pipeline. Add
pnpm audit --prodand an image scan as CI steps. For the image scan usetrivy imageor ECR scan-on-push from Lesson 9. Make the build fail on serious findings. - Follow the basics of the OWASP Top 10. OWASP publishes a list of the ten most common web security risks. Validate input. Use parameterised queries, where the values are sent separately from the SQL text, and never build SQL from strings. Escape output. Check authorisation on every request. Authorisation means checking that the user may access this record, not only that the user is logged in. Set secure cookies. Add security headers (Lesson 10).
- Do not trust the client. The browser can send anything. Checks on the server are the only real checks. A hidden button is not access control.
- Pin and review code from other people. Pin means to fix an exact version. This includes GitHub Actions (Lesson 14) and the
curl | bashinstallers that you copy from the internet.
Layer 8 — detection: CloudTrail, GuardDuty, Config
You cannot respond to what you cannot see. Here are three AWS services, in order of importance:
| Service | What it does | Cost |
|---|---|---|
| CloudTrail | The audit log. It records every API call in your account: who, what, when and from which IP. You cannot investigate an incident without it | The last 90 days of management events are free automatically. A trail that saves them to S3 for longer is almost free for one copy |
| GuardDuty | Watches CloudTrail, DNS and network logs and compares them with lists of known threats. Examples: “this key is being used from an IP linked to crypto-mining” and “this server is talking to a known malware host” | 30-day free trial, then pay for what you use: roughly $5 to $20 a month for a small account |
| AWS Config | Records the settings of your resources over time and flags rule violations. Examples: “this bucket became public” and “this Security Group opened port 22 to the world” | You pay for each rule and each recorded change. It can add up, so start with a few rules |
- CloudTrail: create a trail for all regions. Deliver it to a new S3 bucket with tight access rules. Turn on log file validation, so you can tell if anyone changes the log files.
- GuardDuty: choose Get started, then Enable. Then send its findings to the same SNS topic as your alarms (Lesson 17).
Send GuardDuty findings to the alerts topic with an EventBridge rule (EventBridge is the AWS service that reacts to events). Use source: aws.guardduty and a severity of 7 or higher. Then a serious finding reaches a person in the same way as a 5xx alarm. Also set an alarm on root user login. Use a CloudTrail metric filter on userIdentity.type = Root. The root user should almost never sign in.
Backups: the part that saves the company
Hardening lowers the chance of a disaster. Backups lower its cost. Think of ransomware (malware that locks your data and demands money), a bad migration, a deleted table, a hacked admin account or a region outage. The answer is the same for all of them: a recent, separate, tested copy.
Keep 3 copies of your data, on 2 different kinds of storage, with 1 copy in another place. In AWS terms, you have the live data, a backup in the same region, and a copy in another region or another account. A hacked production login must not be able to delete that last copy.
| What | How it is protected | Notes |
|---|---|---|
| RDS database | Automatic daily backups and point-in-time restore (set to 7 to 35 days, Lesson 8). A manual snapshot before risky changes. A copy of the snapshot in another region | This is the most valuable data you have. Automatic backups are deleted with the instance unless you choose to keep them. Manual snapshots are not deleted |
| S3 uploads | Versioning (lets you undo overwrites and deletes). A lifecycle rule that removes old versions. Cross-region replication (automatic copying) to a second bucket | Consider Object Lock for data that nobody may change or delete |
| Servers | Nothing to back up. You rebuild them from the image, the launch template and Terraform | This is the reward for the stateless design (Lessons 8, 11, 12) |
| Configuration and infrastructure | Terraform code in Git, and a state bucket with versioning (Lesson 15) | Also keep a copy of your secrets offline, in a password manager |
| Redis | Usually none, because it is a cache. If sessions matter, use a replica with failover | If losing it would be a disaster, the data is stored in the wrong place |
Put it on autopilot with AWS Backup
AWS Backup is one service where you manage backup schedules, retention times and copies for RDS, EBS, S3, DynamoDB and more. It stores backups in a vault. You can lock a vault so that even an administrator cannot delete the recovery points. This is a real defence against ransomware and against bad insiders. Here is a small but useful plan for this architecture:
- Daily backups of the RDS instance, kept for 35 days.
- Weekly backups kept for 12 months. Add a monthly backup kept for 7 years if the law or your rules require it.
- A copy of each backup in a vault in a second region (for example
ap-southeast-1). - Backup Vault Lock in compliance mode. Turn it on once you are sure about your retention times.
- Copy an important RDS snapshot to a second region (choose Actions, then Copy snapshot).
- Keep versioning on for every bucket that holds user data (Lesson 11).
If an attacker steals an admin login, they can delete the database and its backups at the same time. The strongest protection is a copy in a separate AWS account that production credentials cannot touch. You can set this up with AWS Organizations, using a “backup” account. A locked vault also helps. A small company should at least keep a copy in another region and protect the accounts that can delete it with MFA.
Test the restore — the drill
A backup that you have never restored is a hope, not a backup. Backups fail quietly, in boring ways. The wrong database was included. The encryption key was deleted. The restore takes nine hours instead of one. The instructions are in the head of someone who has left. The only way to find out is to practise on a calm day.
| Step | Do |
|---|---|
| 1. Write the target | RPO (recovery point objective) is how much data you can afford to lose, for example 15 minutes. RTO (recovery time objective) is how long you can afford to be down, for example 1 hour. |
| 2. Restore to a NEW instance | Never overwrite production. Restore a snapshot, or a chosen point in time, into a fresh database. |
| 3. Prove the data is right | Connect to it. Count the rows in your key tables. Open a recent record that you know. |
| 4. Time it | How long did the whole job take, from start to finish, including finding the instructions? |
| 5. Fix the runbook | Write down every surprise. The next person (or you, when you are tired) will follow it. |
| 6. Clean up | Delete the drill instance so that it stops costing money. |
- Restore the production database to a new instance called
myapp-db-drill, at the latest restorable time (Lesson 8). Production is not touched. Note how long it takes. That is your real recovery time. - Connect to the drill copy from an app server. Check that the data is really there. Count the rows and look at the newest
created_atvalue. - Delete the drill instance so it stops billing.
Do this every quarter and after any major change. The time it took is your real RTO. It is usually longer than the one in your head. For the object store (S3), also practise “restore a deleted file”. Delete a test object, then bring it back from the previous version in the console.
When a key leaks: the first hour
Sooner or later this will happen to someone on your team. If the steps are written down, a stressful moment becomes a checklist. Follow these steps in order:
Contain — deactivate, do not delete yet
Kill the credentialaws iam update-access-key --user-name <USER> --access-key-id <AKIA…> --status InactiveDeactivating stops the key at once and keeps the evidence. You can delete it later
Then attach an explicit Deny everything policy to that identity. An explicit deny always wins (Lesson 5). Also check that the attacker did not create a second key for themselves.
Find out what the key did
In CloudTrail, open Event history and filter by the access key ID (or the user name). Look for actions that you did not do. Examples are new users or keys, new instances (often used for crypto-mining), changed Security Groups, S3 downloads, snapshot copies to unknown accounts, and changes in other regions. Attackers like regions that you never look at.
Remove what they left behind
Terminate instances that you do not know. Delete users, roles and keys that you do not know. Review Security Groups and bucket policies. Check for new IAM roles that trust an outside account. If you are in doubt, treat the whole account as unsafe. Rotate everything that the leaked identity could reach, including the database password and the application secrets.
Tell people, then write it up
If customer data may have been accessed, the law may require you to report it by a deadline. Open a case with AWS Support. They can help, and they sometimes waive the charges from fraudulent use if you act quickly. Then hold a blameless post-mortem, a review that looks for causes and not for someone to blame. Ask how the key leaked, and which control would have stopped it.
The hardening checklist
Copy this into your repository. Every line is something that you have already met in the series:
## Identity
[ ] Root user: MFA on, no access keys, not used (credential report clean)
[ ] MFA required (by policy) for every human; humans use SSO / IAM Identity Center
[ ] No long-lived access keys on servers or in CI (roles + OIDC)
[ ] Any remaining keys are less than 90 days old, each with a named owner
[ ] Leavers removed the same day; access reviewed every quarter
[ ] GitHub secret scanning and push protection on
## Network
[ ] Only the ALB accepts traffic from 0.0.0.0/0 (ports 80 and 443)
[ ] Port 22 closed; access through SSM Session Manager
[ ] DB and cache in private subnets, no public IP, reachable only from web-sg
[ ] WAF (managed core rule set) on the ALB / CloudFront
## Servers
[ ] IMDSv2 required on every instance
[ ] EBS encryption by default; unattended security upgrades on
[ ] Servers rebuilt from a fresh AMI and image at least every month
[ ] Instance role is least-privilege (reviewed with Access Analyzer)
## Secrets and data
[ ] No secrets in Git, images or build args; all in SSM / Secrets Manager
[ ] DB password managed and rotated by Secrets Manager
[ ] RDS, S3, EBS, Redis encrypted at rest; TLS everywhere in transit
[ ] S3 Block Public Access on at the account level
[ ] Every query, cache key and S3 key is tenant-scoped
## App
[ ] Dependabot, pnpm audit and image scan in CI (fail on high severity)
[ ] Authorisation checked on every request, not only the login
## Detection
[ ] CloudTrail multi-region trail with log validation
[ ] GuardDuty on, findings routed to the alerts topic
[ ] Alarm on root login and on unusual spend
## Backups
[ ] RDS backups kept 7 to 35 days, plus a manual snapshot before risky changes
[ ] Cross-region copy of RDS snapshots; S3 versioning and replication
[ ] Backups protected from deletion (locked vault / separate account)
[ ] Restore drill done this quarter; RTO measured and written down
[ ] Key-leak runbook written and its steps rehearsedWhat this costs
| Item | Approximate price |
|---|---|
| IAM, MFA, Access Analyzer, Session Manager, Parameter Store | Free |
| CloudTrail management events, one trail | Free (plus a few cents of S3) |
| GuardDuty | 30 days free, then about $5 to $20 a month for a small account |
| Secrets Manager | About $0.40 per secret per month |
| AWS WAF | About $5 per web ACL, plus $1 per rule, plus $0.60 per million requests |
| RDS automated backups (up to DB size) | Free |
| RDS snapshots / cross-region copies | About $0.095 per GB per month, plus the cost of transfer between regions |
| S3 versioning + replication | Storage for each copy, plus the cost of replication transfer |
| Dependabot, secret scanning (public repos) | Free |
The most valuable items are free: MFA, no keys, no port 22, private data stores, Access Analyzer and CloudTrail. Everything that costs money here costs less per month than an hour of an engineer's time. That is far cheaper than one incident.
Interview corner
What is the shared responsibility model?
AWS secures the cloud itself. That means the buildings, the hardware, the hypervisor and the software of managed services. The customer secures what they put in the cloud. That means identities and permissions, network rules, OS and application patching on EC2, decisions about data and encryption, and configuration. Managed services move the line up, but they never remove the customer's side.
How do you give engineers shell access to production servers securely?
I use Session Manager instead of SSH. It needs no inbound ports and no shared key pairs. IAM controls access, with MFA. Sessions are logged in CloudTrail and can also be recorded. To remove someone's access, I remove their IAM permission.
Where do you store application secrets on AWS?
In SSM Parameter Store (SecureString) or in Secrets Manager. The app reads them at run time through the IAM role of the instance or task. I never put them in code, images or build arguments. I use Secrets Manager for credentials that need automatic rotation, such as database passwords.
An access key was pushed to a public repo. What do you do?
I deactivate the key at once and add a deny policy. I read CloudTrail for everything the key did, in all regions. I remove any resources or credentials that the attacker created. I rotate everything that the key could reach. I report the incident if the law requires it. Then I add controls such as secret scanning and a move to roles. Deleting the commit does not undo the exposure.
Explain RPO and RTO and how they drive your backup design.
RPO is the most data loss I can accept. It decides how often I back up and whether I need point-in-time restore. RTO is the longest downtime I can accept. It decides the restore method: Multi-AZ failover, snapshot restore or a rebuild in another region. I measure the real RTO by practising restores.
How would you protect backups from ransomware or a compromised admin?
I use backups that cannot be changed (immutable) in locked vaults, with AWS Backup Vault Lock or S3 Object Lock. I keep copies in a separate account and region that production credentials cannot delete. I require MFA for deletes. And I test restores regularly.
Security Groups are open only to the ALB, and the app still leaked data. What might have happened?
Flaws in the application go straight through network controls. Examples are broken authorisation, injection, a vulnerable dependency, or an instance role with too many permissions. Broken authorisation is often called IDOR (insecure direct object reference). It lets a user read another tenant's record by changing an ID in the request. The fixes are an authorisation check on every request, tenant scoping, dependency scanning and roles with least privilege.
Practice task before Lesson 19
- Run the credential report. Write down every finding, such as missing MFA, old keys and unused users. Fix each one.
- Check your Security Groups as described above. List everything that is open to the internet. Explain why each item is needed, or close it.
- Attach the SSM policy to the instance role and connect with
start-session. Then remove the port 22 rule and delete the key pair from the launch template. - Turn on CloudTrail, GuardDuty and IAM Access Analyzer. Read the first findings.
- Move the database password to RDS-managed Secrets Manager. Update the boot script so that it reads the password from there.
- Run the restore drill. Restore to a point in time into a new instance, check the data, record the time it took and delete the copy. Then delete a test S3 object and restore it from its earlier version.
- Write the key-leak runbook for your team in ten lines. Then ask a teammate to read it and say what is unclear.
- Copy the checklist into your repository. Tick every box that you can honestly tick. The boxes you cannot tick are next quarter's work.
Hold a game-day, which is a planned practice of an incident. Ask a teammate to “leak” a test key with few permissions into a private repository. See how long secret scanning, GuardDuty or CloudTrail takes to show it. The gap between what you expected and what happened is your plan for improvement.
Conclusion
A secure system is not one that cannot be attacked. It is one where each layer limits the damage that gets past the layer before it. Problems are visible quickly, and recovery is a practised routine.
- Identity first. Use MFA for people and roles for machines. Have no long-lived keys. Give least privilege. Remove leavers on the same day.
- Shrink the attack surface. Only the ALB is public. Data stores are private. Close port 22 and use Session Manager.
- Treat servers as cattle. Rebuild them regularly. Use IMDSv2, encrypted disks and automatic security updates.
- Keep secrets in a secret store and rotate them. Use encryption at rest and in transit. Scope everything by tenant.
- Detect problems with CloudTrail, GuardDuty and alerts. Recover with layered, protected and tested backups and a written incident routine.
The system is secure, observable and recoverable. One question remains. What does all of it cost, and how do you keep the bill under control? That is the final lesson.
End of Lesson 18. Next: Lesson 19 — Cost Review and Final Production Architecture.
Published by Jainik · Last updated · How these lessons are made
Keep learning
Part of the study guide
Related series