Continuous Security Group Rule Change Detection & Response at Scale
Get notified of Security Group changes across all AWS Accounts & Regions in an AWS Organization, with the ability to revert those changes with a single button click from Slack.
Built with AWS CDK (Python) — deploy the entire stack in one language, no Terraform or CSPM tools required. Designed for small-to-medium teams (0-200) running AWS at scale who need real-time network security controls without vendor lock-in.
MITRE ATT&CK: T1562.007 — Impair Defenses: Disable or Modify Cloud Firewall
- EventBridge triggers
sg-detectLambda every 10 minutes sg-detectassumescloudtrail-lake-read-rolein the Organization account and queries CloudTrail Lake forAuthorizeSecurityGroupIngressevents in the last 20 minutes (overlapping window to avoid missing events)- New security group changes are stored in DynamoDB and sent to Slack as interactive messages with
IgnoreandDenybuttons - Security group changes are allowed by default — security doesn't add friction to operations
- If a user clicks Deny, Slack sends the callback to API Gateway →
sg-respondLambda sg-respondreads the event from DynamoDB, assumesspoke-001role in the target account, and callsrevoke_security_group_ingressto revert the change- The Slack message is updated with who approved/denied the change
| Component | Purpose |
|---|---|
sg-detect (Lambda) |
Queries CloudTrail Lake, stores events, sends Slack alerts |
sg-respond (Lambda) |
Handles Slack callbacks, reverts SG changes |
| API Gateway | Receives Slack interactive message webhooks |
DynamoDB secgrouprequests |
Stores SG change event details by CloudTrail requestId |
| EventBridge Rule | Triggers sg-detect every 10 minutes |
| VPC (Private Subnet + NAT) | Lambda runs in private subnet for outbound-only access |
-
IAM Hub-Spoke Roles — deployed via FleetAccess:
hub-001in Security Account (assumed by both Lambdas)spoke-001in all member accounts (assumed bysg-respondto revert changes)cloudtrail-lake-read-rolein Organization account (assumed bysg-detectto query)
-
CloudTrail Lake — enabled in the Organization account with an Event Data Store aggregating across all accounts
-
Slack App — with:
- Incoming Webhooks enabled
- Interactivity enabled (URL will be the API Gateway endpoint from CDK output)
- Note the Signing Secret for request verification
-
AWS CDK —
npm install -g aws-cdk
git clone https://github.com/raajheshkannaa/green-stone
cd green-stone
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txtCopy config.py.example to each of these 4 locations:
config.py
src/revertsg-1/config.py
src/revertsg-2/config.py
stacks/config.py
Fill in your values:
AUTOMATION_ACCOUNT = '123456789012' # Where Lambdas run
ORG_ACCOUNT = '987654321098' # Where CloudTrail Lake lives
CLOUDTRAIL_LAKE_READ_ROLE = 'cloudtrail-lake-read-role'
HOOK_URL = 'https://hooks.slack.com/services/YOUR/WEBHOOK/URL'
SIGNINGSECRET = 'your-slack-signing-secret'Or set them as environment variables (recommended for production — the CDK stack passes them to Lambda automatically).
# Verify stacks
cdk ls
# Synthesize CloudFormation
cdk synth
# Deploy with Security Account credentials
cdk deploy --profile security-accountCDK deploys a CodePipeline that creates a CodeCommit repo. Push your code to that repo and the pipeline handles the rest.
The CDK output will print the API Gateway URL — update your Slack App's Interactivity URL with this.
- Allow by default, deny explicitly — SG changes go through unless a security engineer clicks Deny. This prevents security from blocking operations while still maintaining oversight.
- 20-minute lookback window — CloudTrail events can be delayed 2-3 minutes. The 10-minute trigger with 20-minute lookback ensures overlap so no events are missed.
- DynamoDB dedup —
ConditionExpression='attribute_not_exists(requestid)'ensures the same event isn't processed twice across overlapping windows. - VPC-bound Lambdas — run in private subnets with NAT for outbound-only access.
- PAY_PER_REQUEST DynamoDB — no need to manage capacity for low-volume security events.
For a typical organization (~50 accounts, ~10 SG changes/day):
- Lambda: ~$0.50/month (2 functions, 10-min intervals)
- DynamoDB: ~$0.25/month (on-demand, low volume)
- API Gateway: ~$0.01/month (Slack callbacks only)
- NAT Gateway: ~$35/month (this is the main cost)
- CloudTrail Lake: ~$2.50/GB ingested (shared across all detections)
Total: ~$38/month (dominated by NAT Gateway)
- aws-cloudtrail-lake-detections — Additional CloudTrail Lake detections (compromised keys, public resources, CloudTrail tampering)
- fleet-access — Hub-Spoke IAM role deployment (prerequisite for this project)
MIT
