Audit AWS security groups for overly permissive rules and security vulnerabilities. Use when reviewing AWS security, auditing security groups, or improving network security posture.
Audit AWS security groups and identify security vulnerabilities.
List security groups, check for 0.0.0.0/0 access, restrict to minimum needed ports and IPs.
# List all security groups
aws ec2 describe-security-groups \
--query 'SecurityGroups[].[GroupId,GroupName,Description]' \
--output table
# Get specific security group
aws ec2 describe-security-groups \
--group-ids sg-1234567890abcdef0
1. Open to the world (0.0.0.0/0)
Find security groups with unrestricted access:
aws ec2 describe-security-groups \
--filters "Name=ip-permission.cidr,Values=0.0.0.0/0" \
--query 'SecurityGroups[].[GroupId,GroupName,IpPermissions[?IpRanges[?CidrIp==`0.0.0.0/0`]]]'
High-risk ports open to 0.0.0.0/0:
2. Unrestricted outbound rules
Default security groups allow all outbound traffic. Restrict if possible:
# Check outbound rules
aws ec2 describe-security-groups \
--group-ids sg-1234567890abcdef0 \
--query 'SecurityGroups[].IpPermissionsEgress'
3. Unused security groups
Find security groups not attached to any resources:
# List all security groups
aws ec2 describe-security-groups --query 'SecurityGroups[].GroupId' > all-sgs.txt
# List security groups in use
aws ec2 describe-instances --query 'Reservations[].Instances[].SecurityGroups[].GroupId' > used-sgs.txt
aws rds describe-db-instances --query 'DBInstances[].VpcSecurityGroups[].VpcSecurityGroupId' >> used-sgs.txt
# Compare to find unused
Principle of least privilege:
SSH/RDP access:
# Bad: Open to world
0.0.0.0/0 on port 22
# Good: Restrict to office IP
203.0.113.0/24 on port 22
# Better: Use bastion host or AWS Systems Manager Session Manager
Database access:
# Bad: Open to world
0.0.0.0/0 on port 3306
# Good: Only from application security group
sg-app-12345678 on port 3306
Web servers:
# Acceptable: HTTP/HTTPS from anywhere
0.0.0.0/0 on port 80, 443
# But use CloudFront or ALB for additional protection
Remove overly permissive rule:
# Revoke SSH from anywhere
aws ec2 revoke-security-group-ingress \
--group-id sg-1234567890abcdef0 \
--protocol tcp \
--port 22 \
--cidr 0.0.0.0/0
Add restricted rule:
# Allow SSH only from office
aws ec2 authorize-security-group-ingress \
--group-id sg-1234567890abcdef0 \
--protocol tcp \
--port 22 \
--cidr 203.0.113.0/24
Use security group references:
# Allow traffic from another security group
aws ec2 authorize-security-group-ingress \
--group-id sg-database-12345678 \
--protocol tcp \
--port 3306 \
--source-group sg-app-12345678
Inbound rules structure:
Example secure configuration:
Web tier (ALB):
Inbound:
- HTTP (80) from 0.0.0.0/0
- HTTPS (443) from 0.0.0.0/0
Outbound:
- All traffic to app-tier-sg
App tier:
Inbound:
- Port 8080 from web-tier-sg
Outbound:
- Port 3306 to db-tier-sg
- HTTPS (443) to 0.0.0.0/0 (for external APIs)
Database tier:
Inbound:
- Port 3306 from app-tier-sg
Outbound:
- None (or minimal)
Critical issues:
High priority:
Best practices:
Bastion host:
Bastion SG:
Inbound:
- SSH (22) from office IP only
Private instance SG:
Inbound:
- SSH (22) from bastion-sg only
Load balancer pattern:
ALB SG:
Inbound:
- HTTP/HTTPS from 0.0.0.0/0
App SG:
Inbound:
- App port from alb-sg only
Database pattern:
DB SG:
Inbound:
- DB port from app-sg only
- DB port from bastion-sg (for admin)
CloudWatch Events for security group changes:
{
"source": ["aws.ec2"],
"detail-type": ["AWS API Call via CloudTrail"],
"detail": {
"eventName": [
"AuthorizeSecurityGroupIngress",
"RevokeSecurityGroupIngress"
]
}
}
AWS Config rules:
For SSH/RDP open to world:
For database ports open:
For unused security groups:
AWS Security Hub:
AWS Config:
Third-party tools:
Security Group Audit Report
===========================
Critical Issues:
- sg-12345678: SSH (22) open to 0.0.0.0/0
- sg-23456789: MySQL (3306) open to 0.0.0.0/0
High Priority:
- sg-34567890: Unused security group
- sg-45678901: RDP from broad CIDR (10.0.0.0/8)
Recommendations:
1. Restrict SSH to office IP: 203.0.113.0/24
2. Restrict MySQL to app security group: sg-app-12345
3. Delete unused security group: sg-34567890
4. Narrow RDP access to specific subnet
Estimated risk reduction: High
Regular audits:
Documentation:
Automation:
Defense in depth: