Security groups are stateful firewall rules applied to elastic network interfaces. Every EC2 instance, RDS database, Lambda function in a VPC, and Elastic Load Balancer has one or more security groups controlling what traffic can reach it. The firewall rules you write in security groups — or don't write — directly determine your network attack surface. And yet security groups are often treated as configuration afterthoughts: rules get added in a hurry during debugging, never reviewed, and accumulate over years into a structure nobody fully understands.
Good security group hygiene is not especially complex, but it requires following consistent principles and reviewing the rules you've already created against those principles periodically. This guide covers both the principles and the review process.
The Reference-by-Group-ID Principle
The most important practice in security group design: reference other security groups rather than CIDR ranges when restricting access between resources within the same VPC or peered VPCs. Instead of allowing port 5432 (PostgreSQL) from 10.1.0.0/24 (your application subnet), allow port 5432 from the security group ID of your application servers.
This approach is more accurate (CIDR ranges don't precisely identify application servers — any resource in the subnet, including unrelated things, matches the CIDR), more scalable (adding a new application server auto-inherits access without CIDR changes), and more auditable (the security group name "app-servers" is self-documenting in a way that "10.1.0.0/24" isn't).
Use CIDR ranges for external sources (office IP addresses for VPN/SSH access) and for resources where security group ID reference isn't available (resources in different AWS accounts where VPC peering is in use require CIDR references unless you use account-cross security group references).
Security Group Per Function
Create one security group per functional role, not one per resource. "web-servers" is a better security group name than "web-server-1" — it applies to all web servers and its rules describe what the web server tier needs, not what one specific instance needs. This approach scales: when you add a new web server, you attach the existing group without creating new rules.
Common functional security groups for a web application:
alb-public: allows inbound 80 and 443 from 0.0.0.0/0 (internet-facing ALB)app-servers: allows inbound 8080 from alb-public security group onlydatabase: allows inbound 5432 from app-servers security group onlycache: allows inbound 6379 from app-servers security group onlybastion: allows inbound 22 from specific office IP CIDR only
This structure makes the access model immediately readable. No database rule allows internet-facing access even through indirect paths — the database group's only inbound rule references the app-servers group, which only receives traffic from the load balancer group.
Outbound Rules and the "Allow All" Default
Security groups allow all outbound traffic by default. Most guides advise leaving this default — managing outbound rules on every security group is operationally expensive and doesn't meaningfully reduce risk for most workloads. This advice is correct for development and most production environments.
Restrict outbound rules for high-sensitivity workloads where egress control matters for compliance or for limiting the blast radius of a compromise. A database server with outbound rules restricted to specific destination ports (application servers on port 8080, DNS resolution on port 53, patch repositories on port 443) has a smaller footprint than one allowed to connect anywhere. The tradeoff is operational overhead — new outbound requirements need explicit rule additions. Implement outbound restriction only where the security benefit justifies the overhead.
Naming Conventions
Security group names should communicate their purpose to someone who didn't create them. A naming convention that works in practice: {environment}-{service}-{role} or {service}-{role} for environment-agnostic groups. Examples: prod-api-webserver, prod-api-database, shared-nat-gateway.
Add descriptions to every security group rule. The description field is not prominent in the console but is invaluable during security reviews. "Allows GitHub Actions runners to push container images" is infinitely more useful than an undescribed rule allowing inbound port 443 from a specific IP range.
Review Process and Tools
Quarterly security group reviews catch rules that accumulated outside your design principles. A structured review covers:
- Rules allowing 0.0.0.0/0 on any port — are these intentional? Can they be restricted?
- Security groups with no rules attached to any resource — candidates for deletion
- Rules using CIDR ranges that could be converted to security group references
- Rules added in the past 90 days without description — verify intent and add documentation
The security groups audit guide covers the CLI commands and AWS Config rules that automate much of this review. AWS Config's restricted-ssh rule continuously monitors for the most dangerous pattern (SSH open to the world) and alerts within minutes of violation.
Related Reading
- EC2 security groups audit — auditing existing rules for dangerous configurations
- Network Access Control Lists — subnet-level controls that work alongside security groups
- VPC security monitoring — monitoring network access alongside security group controls
- Essential AWS Config rules — automated security group compliance checking
FAQ
How many security groups can I attach to an EC2 instance?
By default, up to 5 security groups per network interface. If you need more, you can request an increase through Service Quotas. In practice, most well-designed instances need 2-4 groups (their own functional group, a management group for bastion access, and perhaps a compliance logging group). If you're approaching the 5-group limit, evaluate whether rules can be consolidated.
Can security groups span VPCs?
Security groups are VPC-scoped. You can reference security groups from peered VPCs in rules — specifying a security group ID from a peered account or VPC as the source in an inbound rule. This requires the VPCs to be peered. For Transit Gateway connections, security group references work within the same VPC; cross-VPC references require VPC peering.
What happens when an instance has multiple security groups with conflicting rules?
Security groups use "allow" rules only (no explicit deny rules). When multiple security groups are attached to an instance, the rules are combined — the effective rule set is the union of all attached groups' rules. If any attached group allows an inbound connection, it's permitted, regardless of whether other groups have no rule for it. This means adding a security group to an instance can only expand access, never restrict it. Restricting access requires removing groups or modifying existing rules.
Protect your AWS accounts before it's too late
Vigilare monitors your AWS accounts for suspension risks — billing anomalies, IAM issues, GuardDuty findings, and more — and alerts you before AWS takes action.
Written by Vigilare Engineering
Platform Team