Project 02 / 03Open source
CloudGuard
Autonomous AWS security remediation: finds and fixes public S3 buckets, world-open SSH, and orphaned EC2 on a schedule.
CloudGuard architecture
Problem
The same three misconfigurations keep showing up in AWS accounts: a bucket made public, a security group open to the world on SSH, and instances nobody owns any more. Finding them is easy; fixing them safely and automatically, without handing an automated tool the power to break things, is the hard part.
CloudGuard runs on a schedule, remediates the unambiguous cases itself, and hands everything else to a human.
Architecture
- Python 3.12 Lambda functions on arm64, triggered by an EventBridge schedule.
- Findings and remediation state in a DynamoDB single-table design with TTL, so records expire on their own.
- SNS for notifications; an API Gateway HTTP API serves the dashboard.
- React/TypeScript dashboard hosted on S3 behind CloudFront with Origin Access Control.
- Detects and remediates public S3 buckets, world-open SSH ingress, and orphaned EC2 instances; ingress rules wider than TCP/22 move to a manual-review state instead of being revoked.
Key decisions & trade-offs
- Remediate automatically only where the fix is unambiguous. Rules wider than TCP/22 wait in a manual-review state, so some findings need a person, but the service never revokes a rule it cannot reason about.
- Least privilege by construction. The scanner role has exactly 12 actions and no delete, terminate, or authorize permissions: some remediations are impossible by design, which is the point.
- Defence in depth on EC2. The opt-in tag is checked in the API filter, in the code, and in an aws:ResourceTag IAM condition, so a bug in one layer cannot widen the blast radius.
- DynamoDB single-table with TTL. No cleanup job and no relational database to run; the trade-off is designing access patterns up front.
- arm64 Lambda. Cheaper per millisecond than x86; dependencies need arm builds.
- Reviewer-gated apply. Terraform plans are posted on pull requests and nothing is applied until a reviewer approves, at the cost of slower infrastructure changes.
Security, cost & testing
- Security: 12-action least-privilege scanner IAM role with no delete/terminate/authorize; EC2 opt-in tag enforced in the API filter, the code, and an aws:ResourceTag IAM condition.
- Infrastructure: Terraform with 31 resources and S3 remote state; GitHub Actions with OIDC, PR plan comments, reviewer-gated apply.
- Cost: runs inside the AWS free tier.
- Tests: 59 pytest cases, 98% line coverage, zero live AWS calls in the suite.
What I'd do differently
- Close the loop on manual review. Rules wider than TCP/22 are parked for a human and SNS says so, but nothing lets that human act from the notification. An approval action with an expiring token would turn a message into a decision.
- Scope by tag everywhere, not only for EC2. The opt-in tag enforced in the API, the code, and an aws:ResourceTag condition is the right pattern; extending it to buckets and security groups would bound the blast radius to resources someone chose to enrol.
- Test the IAM policy, not just the code. 59 pytest cases cover the logic with zero live AWS calls, which is right for CI, but a permission regression would still pass. IAM policy simulation in the merge gate would make sure the 12-action role can only ever shrink.