Security Group Has Too Many Rules
Overview
This check verifies that your AWS security groups have a manageable number of rules. By default, it flags security groups with more than 50 inbound (ingress) or 50 outbound (egress) rules. Large rule sets become difficult to audit and may hide overly permissive access.
Risk
Security groups with too many rules create several problems:
- Hidden vulnerabilities: Overly permissive rules can get lost in large rule sets, inadvertently exposing services to the internet or unintended networks
- Audit difficulty: Large rule lists make it hard to verify that only necessary access is allowed
- Compliance issues: Security frameworks often require demonstrable least-privilege access, which is difficult to prove with sprawling rule sets
- Lateral movement risk: Excessive rules may allow attackers to move between resources more easily once inside your network
Remediation Steps
Prerequisites
You need:
- AWS Console access with permissions to view and modify EC2 security groups
- Knowledge of which rules are actively needed (check with your team before removing rules)
Required IAM permissions
Your IAM user or role needs these permissions:
ec2:DescribeSecurityGroupsec2:DescribeSecurityGroupRulesec2:RevokeSecurityGroupIngressec2:RevokeSecurityGroupEgressec2:AuthorizeSecurityGroupIngress(if consolidating rules)ec2:AuthorizeSecurityGroupEgress(if consolidating rules)
AWS Console Method
-
Open the EC2 Console
- Go to EC2 Console in us-east-1
-
Navigate to Security Groups
- In the left sidebar, click Security Groups (under Network & Security)
- Or go directly to Security Groups
-
Find the flagged security group
- Use the search box to find the security group by ID or name
- Click on the security group to view its details
-
Review inbound rules
- Click the Inbound rules tab
- Review each rule and identify:
- Duplicate or redundant rules
- Rules no longer needed (old projects, decommissioned services)
- Overly broad CIDR ranges that can be narrowed
-
Remove unnecessary inbound rules
- Click Edit inbound rules
- Click the Delete button (X icon) next to rules you want to remove
- Click Save rules
-
Review and clean outbound rules
- Click the Outbound rules tab
- Repeat the review and removal process
- Click Edit outbound rules to make changes
-
Consider splitting the security group
- If the group still has many rules after cleanup, consider creating dedicated security groups for different purposes (e.g., web traffic, database access, monitoring)
AWS CLI (optional)
List security groups with rule counts
aws ec2 describe-security-groups \
--region us-east-1 \
--query 'SecurityGroups[*].{ID:GroupId,Name:GroupName,InboundCount:length(IpPermissions),OutboundCount:length(IpPermissionsEgress)}' \
--output table
View rules for a specific security group
aws ec2 describe-security-group-rules \
--region us-east-1 \
--filters Name=group-id,Values=sg-0123456789abcdef0 \
--output table
Replace sg-0123456789abcdef0 with your security group ID.
Remove an inbound rule by rule ID
aws ec2 revoke-security-group-ingress \
--region us-east-1 \
--group-id sg-0123456789abcdef0 \
--security-group-rule-ids sgr-0123456789abcdef0
Remove an inbound rule by specifying the rule details
aws ec2 revoke-security-group-ingress \
--region us-east-1 \
--group-id sg-0123456789abcdef0 \
--protocol tcp \
--port 22 \
--cidr 203.0.113.0/24
Remove an outbound rule
aws ec2 revoke-security-group-egress \
--region us-east-1 \
--group-id sg-0123456789abcdef0 \
--security-group-rule-ids sgr-0123456789abcdef0
Find security groups with more than 50 rules
aws ec2 describe-security-groups \
--region us-east-1 \
--query 'SecurityGroups[?length(IpPermissions) > `50` || length(IpPermissionsEgress) > `50`].{ID:GroupId,Name:GroupName,Inbound:length(IpPermissions),Outbound:length(IpPermissionsEgress)}' \
--output table
CloudFormation (optional)
When defining security groups in CloudFormation, keep rule sets minimal and use security group references instead of broad CIDR ranges.
Example: Minimal security group with role-specific rules
AWSTemplateFormatVersion: '2010-09-09'
Description: Security groups with minimal, role-specific rules
Parameters:
VpcId:
Type: AWS::EC2::VPC::Id
Description: VPC ID for the security groups
Resources:
# Web tier security group - only HTTP/HTTPS
WebSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Web server access
VpcId: !Ref VpcId
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 443
ToPort: 443
CidrIp: 0.0.0.0/0
Description: HTTPS from internet
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
Description: HTTP from internet (redirects to HTTPS)
Tags:
- Key: Name
Value: web-tier-sg
# Application tier - only from web tier
AppSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Application server access
VpcId: !Ref VpcId
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 8080
ToPort: 8080
SourceSecurityGroupId: !Ref WebSecurityGroup
Description: App traffic from web tier
Tags:
- Key: Name
Value: app-tier-sg
# Database tier - only from app tier
DatabaseSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Database access
VpcId: !Ref VpcId
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 5432
ToPort: 5432
SourceSecurityGroupId: !Ref AppSecurityGroup
Description: PostgreSQL from app tier
Tags:
- Key: Name
Value: database-tier-sg
Outputs:
WebSecurityGroupId:
Value: !Ref WebSecurityGroup
AppSecurityGroupId:
Value: !Ref AppSecurityGroup
DatabaseSecurityGroupId:
Value: !Ref DatabaseSecurityGroup
Best practices for CloudFormation security groups
- Use security group references: Use
SourceSecurityGroupIdinstead of CIDR blocks when traffic comes from known AWS resources - One purpose per group: Create separate security groups for different tiers or services
- Add descriptions: Always include the
Descriptionfield on rules to document their purpose - Avoid wildcards: Never use
-1protocol or0.0.0.0/0without a specific, documented reason
Terraform (optional)
Example: Minimal security groups with role separation
variable "vpc_id" {
description = "VPC ID for the security groups"
type = string
}
# Web tier security group
resource "aws_security_group" "web" {
name = "web-tier-sg"
description = "Web server access"
vpc_id = var.vpc_id
tags = {
Name = "web-tier-sg"
}
}
resource "aws_vpc_security_group_ingress_rule" "web_https" {
security_group_id = aws_security_group.web.id
description = "HTTPS from internet"
ip_protocol = "tcp"
from_port = 443
to_port = 443
cidr_ipv4 = "0.0.0.0/0"
}
resource "aws_vpc_security_group_ingress_rule" "web_http" {
security_group_id = aws_security_group.web.id
description = "HTTP from internet (redirects to HTTPS)"
ip_protocol = "tcp"
from_port = 80
to_port = 80
cidr_ipv4 = "0.0.0.0/0"
}
# Application tier security group
resource "aws_security_group" "app" {
name = "app-tier-sg"
description = "Application server access"
vpc_id = var.vpc_id
tags = {
Name = "app-tier-sg"
}
}
resource "aws_vpc_security_group_ingress_rule" "app_from_web" {
security_group_id = aws_security_group.app.id
description = "App traffic from web tier"
ip_protocol = "tcp"
from_port = 8080
to_port = 8080
referenced_security_group_id = aws_security_group.web.id
}
# Database tier security group
resource "aws_security_group" "database" {
name = "database-tier-sg"
description = "Database access"
vpc_id = var.vpc_id
tags = {
Name = "database-tier-sg"
}
}
resource "aws_vpc_security_group_ingress_rule" "db_from_app" {
security_group_id = aws_security_group.database.id
description = "PostgreSQL from app tier"
ip_protocol = "tcp"
from_port = 5432
to_port = 5432
referenced_security_group_id = aws_security_group.app.id
}
output "web_security_group_id" {
value = aws_security_group.web.id
}
output "app_security_group_id" {
value = aws_security_group.app.id
}
output "database_security_group_id" {
value = aws_security_group.database.id
}
Best practices for Terraform security groups
- Use individual rule resources: Prefer
aws_vpc_security_group_ingress_ruleandaws_vpc_security_group_egress_ruleover inline rules for better change management - Reference security groups: Use
referenced_security_group_idinstead of CIDR blocks when possible - Always add descriptions: Include the
descriptionfield on every rule - Separate by function: Create dedicated security groups for each tier or service
Verification
After removing rules, verify your security group is within limits:
-
In the AWS Console:
- Go to EC2 > Security Groups
- Click on your security group
- Count the rules in the Inbound rules and Outbound rules tabs
- Ensure each has 50 or fewer rules
-
Test application connectivity:
- Verify that your applications still work correctly after rule removal
- Check logs for any connection failures
CLI verification commands
Check rule counts for a specific security group:
aws ec2 describe-security-groups \
--region us-east-1 \
--group-ids sg-0123456789abcdef0 \
--query 'SecurityGroups[0].{ID:GroupId,Name:GroupName,InboundRules:length(IpPermissions),OutboundRules:length(IpPermissionsEgress)}'
List all security groups exceeding the threshold:
aws ec2 describe-security-groups \
--region us-east-1 \
--query 'SecurityGroups[?length(IpPermissions) > `50` || length(IpPermissionsEgress) > `50`].GroupId' \
--output text
If this returns nothing, all security groups are within limits.
Additional Resources
- AWS Documentation: Amazon VPC Security Groups
- AWS Documentation: Security Group Rules
- AWS Documentation: Security Group Quotas
- AWS Well-Architected Framework: Security Pillar
Notes
- Do not blindly delete rules: Before removing any rule, verify it is not needed by active applications. Coordinate with application owners to avoid outages.
- Document before removing: Keep a record of removed rules in case they need to be restored.
- Consider using AWS Firewall Manager: For organizations managing many security groups, AWS Firewall Manager can help enforce consistent policies.
- Use security group references: Instead of adding many CIDR rules, reference other security groups when traffic comes from known AWS resources. This reduces rule count and is more maintainable.
- Review regularly: Schedule periodic reviews of security group rules to remove stale entries before they accumulate.
- Split large groups: If a security group legitimately needs many rules, consider splitting it into multiple purpose-specific groups (e.g., one for web traffic, one for monitoring, one for database access).
- Check associated resources: Before deleting rules, identify which EC2 instances, RDS databases, or other resources use the security group to understand the impact.