Skip to main content

Restrict Default Security Group Traffic

Overview

This check verifies that the default security group in each of your VPCs has no inbound or outbound rules. Every VPC comes with a default security group that cannot be deleted. AWS recommends keeping it empty and using custom security groups instead.

Risk

Permissive rules in default security groups create significant security risks:

  • Lateral movement: Attackers who compromise one instance can more easily reach others using the same default security group
  • Port scanning: Open inbound rules allow reconnaissance of internal resources
  • Data exfiltration: Unrestricted outbound rules enable data theft and command-and-control communication
  • Accidental exposure: Resources may unintentionally use the default security group if no custom group is specified

Keeping the default security group empty reduces your attack surface and enforces explicit network access controls.

Remediation Steps

Prerequisites

You need:

  • AWS Console access with permissions to modify VPC security groups
  • Understanding of which resources (if any) currently use the default security group
Required IAM permissions (for administrators)

Your IAM user or role needs these permissions:

  • ec2:DescribeSecurityGroups
  • ec2:DescribeSecurityGroupRules
  • ec2:RevokeSecurityGroupIngress
  • ec2:RevokeSecurityGroupEgress

AWS Console Method

Important: Before removing rules, verify that no resources depend on the default security group. If resources use it, migrate them to a custom security group first.

  1. Open the VPC Console

  2. Navigate to Security Groups

    • In the left sidebar, click Security groups
    • Or go directly to Security Groups
  3. Find the default security group

    • Look for the security group named default
    • You can filter by typing "default" in the search box
    • Note: Each VPC has its own default security group
  4. Remove all inbound rules

    • Select the default security group
    • Click the Inbound rules tab
    • Click Edit inbound rules
    • Delete all rules by clicking the Delete button next to each rule
    • Click Save rules
  5. Remove all outbound rules

    • Click the Outbound rules tab
    • Click Edit outbound rules
    • Delete all rules (including the default "allow all" rule)
    • Click Save rules
  6. Repeat for other VPCs

    • If you have multiple VPCs, repeat steps 3-5 for each one
    • Also check other AWS regions where you have VPCs
AWS CLI (optional)

Find the default security group

aws ec2 describe-security-groups \
--filters "Name=group-name,Values=default" \
--region us-east-1 \
--query 'SecurityGroups[*].[GroupId,VpcId,GroupName]' \
--output table

View current rules

aws ec2 describe-security-group-rules \
--filters "Name=group-id,Values=sg-xxxxxxxxxxxxxxxxx" \
--region us-east-1 \
--query 'SecurityGroupRules[*].[SecurityGroupRuleId,IsEgress,IpProtocol,FromPort,ToPort,CidrIpv4]' \
--output table

Replace sg-xxxxxxxxxxxxxxxxx with your default security group ID.

Remove all inbound rules

First, get the rule IDs:

INGRESS_RULES=$(aws ec2 describe-security-group-rules \
--filters "Name=group-id,Values=sg-xxxxxxxxxxxxxxxxx" \
--query 'SecurityGroupRules[?!IsEgress].SecurityGroupRuleId' \
--output text \
--region us-east-1)

if [ -n "$INGRESS_RULES" ]; then
aws ec2 revoke-security-group-ingress \
--group-id sg-xxxxxxxxxxxxxxxxx \
--security-group-rule-ids $INGRESS_RULES \
--region us-east-1
fi

Remove all outbound rules

EGRESS_RULES=$(aws ec2 describe-security-group-rules \
--filters "Name=group-id,Values=sg-xxxxxxxxxxxxxxxxx" \
--query 'SecurityGroupRules[?IsEgress].SecurityGroupRuleId' \
--output text \
--region us-east-1)

if [ -n "$EGRESS_RULES" ]; then
aws ec2 revoke-security-group-egress \
--group-id sg-xxxxxxxxxxxxxxxxx \
--security-group-rule-ids $EGRESS_RULES \
--region us-east-1
fi

Script to clean all default security groups in a region

#!/bin/bash
REGION="us-east-1"

# Get all default security groups
DEFAULT_SGS=$(aws ec2 describe-security-groups \
--filters "Name=group-name,Values=default" \
--query 'SecurityGroups[].GroupId' \
--output text \
--region $REGION)

for SG_ID in $DEFAULT_SGS; do
echo "Processing $SG_ID..."

# Remove ingress rules
INGRESS=$(aws ec2 describe-security-group-rules \
--filters "Name=group-id,Values=$SG_ID" \
--query 'SecurityGroupRules[?!IsEgress].SecurityGroupRuleId' \
--output text \
--region $REGION)

if [ -n "$INGRESS" ]; then
echo " Removing inbound rules: $INGRESS"
aws ec2 revoke-security-group-ingress \
--group-id $SG_ID \
--security-group-rule-ids $INGRESS \
--region $REGION
fi

# Remove egress rules
EGRESS=$(aws ec2 describe-security-group-rules \
--filters "Name=group-id,Values=$SG_ID" \
--query 'SecurityGroupRules[?IsEgress].SecurityGroupRuleId' \
--output text \
--region $REGION)

if [ -n "$EGRESS" ]; then
echo " Removing outbound rules: $EGRESS"
aws ec2 revoke-security-group-egress \
--group-id $SG_ID \
--security-group-rule-ids $EGRESS \
--region $REGION
fi

echo " Done"
done
CloudFormation (optional)

Use the AWS::EC2::SecurityGroup resource type with the special aws_default_security_group behavior. Note that CloudFormation can manage the default security group by importing it:

AWSTemplateFormatVersion: '2010-09-09'
Description: Restrict default security group to have no rules

Parameters:
VpcId:
Type: AWS::EC2::VPC::Id
Description: The VPC ID whose default security group should be restricted

Resources:
# Note: You cannot create a default security group via CloudFormation.
# The default SG already exists in each VPC.
# Use a Custom Resource to clear its rules:

ClearDefaultSGFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Sub 'ClearDefaultSG-${AWS::StackName}'
Runtime: python3.11
Handler: index.handler
Timeout: 60
Role: !GetAtt ClearDefaultSGRole.Arn
Code:
ZipFile: |
import boto3
import cfnresponse

def handler(event, context):
try:
if event['RequestType'] in ['Create', 'Update']:
vpc_id = event['ResourceProperties']['VpcId']
ec2 = boto3.client('ec2')

# Find default security group
response = ec2.describe_security_groups(
Filters=[
{'Name': 'vpc-id', 'Values': [vpc_id]},
{'Name': 'group-name', 'Values': ['default']}
]
)

if response['SecurityGroups']:
sg_id = response['SecurityGroups'][0]['GroupId']

# Get and revoke ingress rules
rules = ec2.describe_security_group_rules(
Filters=[{'Name': 'group-id', 'Values': [sg_id]}]
)

ingress_ids = [r['SecurityGroupRuleId'] for r in rules['SecurityGroupRules'] if not r['IsEgress']]
egress_ids = [r['SecurityGroupRuleId'] for r in rules['SecurityGroupRules'] if r['IsEgress']]

if ingress_ids:
ec2.revoke_security_group_ingress(GroupId=sg_id, SecurityGroupRuleIds=ingress_ids)
if egress_ids:
ec2.revoke_security_group_egress(GroupId=sg_id, SecurityGroupRuleIds=egress_ids)

cfnresponse.send(event, context, cfnresponse.SUCCESS, {})
except Exception as e:
print(f"Error: {e}")
cfnresponse.send(event, context, cfnresponse.FAILED, {'Error': str(e)})

ClearDefaultSGRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: SecurityGroupAccess
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- ec2:DescribeSecurityGroups
- ec2:DescribeSecurityGroupRules
- ec2:RevokeSecurityGroupIngress
- ec2:RevokeSecurityGroupEgress
Resource: '*'

ClearDefaultSG:
Type: Custom::ClearDefaultSecurityGroup
Properties:
ServiceToken: !GetAtt ClearDefaultSGFunction.Arn
VpcId: !Ref VpcId

Outputs:
Status:
Description: Default security group has been restricted
Value: !Sub 'Default security group rules cleared for VPC ${VpcId}'

Deploy with:

aws cloudformation deploy \
--template-file restrict-default-sg.yaml \
--stack-name restrict-default-sg \
--parameter-overrides VpcId=vpc-xxxxxxxxxxxxxxxxx \
--capabilities CAPABILITY_IAM \
--region us-east-1
Terraform (optional)

Terraform has a special resource for managing the default security group:

# Manage the default security group to have no rules
resource "aws_default_security_group" "default" {
vpc_id = aws_vpc.main.id

# Empty ingress and egress blocks remove all rules
# Do not add any ingress or egress blocks here
}

# If managing an existing VPC's default security group:
resource "aws_default_security_group" "existing_vpc_default" {
vpc_id = "vpc-xxxxxxxxxxxxxxxxx" # Replace with your VPC ID

# Leaving ingress and egress undefined removes all rules
}

# Example: Create a VPC with a properly restricted default security group
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
enable_dns_support = true

tags = {
Name = "main-vpc"
}
}

# The default security group is automatically created with the VPC
# This resource takes ownership and removes all rules
resource "aws_default_security_group" "main_default" {
vpc_id = aws_vpc.main.id

tags = {
Name = "default-restricted"
Description = "Default security group - DO NOT USE"
}
}

# Create a custom security group for actual use
resource "aws_security_group" "web" {
name = "web-servers"
description = "Security group for web servers"
vpc_id = aws_vpc.main.id

ingress {
description = "HTTPS from anywhere"
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}

egress {
description = "Allow all outbound"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}

tags = {
Name = "web-servers"
}
}

Deploy with:

terraform init
terraform plan
terraform apply

Important notes about aws_default_security_group:

  • This resource adopts the existing default security group rather than creating a new one
  • Defining no ingress or egress blocks removes all rules
  • The default security group cannot be deleted, only its rules can be managed
  • Any rules added outside of Terraform will be removed on the next terraform apply

Verification

After removing all rules, verify the default security group is empty:

  1. In the AWS Console:

    • Go to VPC > Security groups
    • Select the default security group
    • Check the Inbound rules tab - it should show no rules
    • Check the Outbound rules tab - it should show no rules
  2. Run Prowler again:

    • Re-run the Prowler check to confirm it now passes
CLI verification commands

Check that the default security group has no rules:

aws ec2 describe-security-group-rules \
--filters "Name=group-id,Values=sg-xxxxxxxxxxxxxxxxx" \
--region us-east-1 \
--query 'SecurityGroupRules[*].[SecurityGroupRuleId,IsEgress,IpProtocol]' \
--output table

The output should be empty or show "None".

Check all default security groups in a region:

for SG_ID in $(aws ec2 describe-security-groups \
--filters "Name=group-name,Values=default" \
--query 'SecurityGroups[].GroupId' \
--output text \
--region us-east-1); do

RULE_COUNT=$(aws ec2 describe-security-group-rules \
--filters "Name=group-id,Values=$SG_ID" \
--query 'length(SecurityGroupRules)' \
--region us-east-1)

echo "$SG_ID: $RULE_COUNT rules"
done

All default security groups should show "0 rules".

Additional Resources

Notes

  • Cannot delete the default security group: AWS does not allow deletion of the default security group. You can only remove its rules to make it empty.

  • Check for dependent resources: Before removing rules, ensure no EC2 instances, RDS databases, Lambda functions, or other resources are using the default security group. Use the console or CLI to check:

    aws ec2 describe-network-interfaces \
    --filters "Name=group-id,Values=sg-xxxxxxxxxxxxxxxxx" \
    --query 'NetworkInterfaces[*].[NetworkInterfaceId,Description]' \
    --region us-east-1
  • Self-referencing rules: The default security group often has a self-referencing rule that allows instances in the group to communicate with each other. Removing this rule prevents intra-group communication, which is the desired behavior for an empty default group.

  • Per-VPC setting: Each VPC has its own default security group. You must clear rules in each VPC separately.

  • New VPCs: When you create a new VPC, AWS automatically creates a default security group with permissive rules. Use Terraform's aws_default_security_group resource or a CloudFormation Custom Resource to automatically restrict it.

  • Use custom security groups: Instead of the default security group, create purpose-specific security groups with explicit, least-privilege rules for each workload.