AWS EC2 Security Groups

By Vishvesh Patel · DevOps EngineerUpdated September 12, 2026

How security groups work as stateful firewalls. Rule structure, referencing other groups, the default outbound rule, limits, and debugging blocked traffic.

A security group is a stateful firewall attached to a network interface. It is the primary network control in AWS, and it behaves differently enough from a traditional firewall that the differences are worth being precise about.

Stateful means you write half the rules

Allow inbound TCP 443 and the response traffic is automatically permitted, on whatever ephemeral port the client used. You never write a return rule.

This is the key difference from a network ACL, which is stateless and requires explicit rules in both directions. Most "my traffic is mysteriously blocked" problems in AWS are NACL statelessness, not security groups.

Allow-only, and implicitly deny

A security group contains only allow rules. There is no deny.

Anything not explicitly allowed is denied, and rules are additive. Attaching three groups to an instance gives it the union of all their allow rules. You cannot use one group to subtract permission granted by another. That is what NACLs are for.

Default behaviour

DirectionDefault in a new group
InboundNothing allowed
OutboundAll traffic allowed

The permissive outbound default is deliberate and usually sensible. Tightening it is a real security improvement. An instance that can only reach what it needs limits what a compromise can do, but it breaks package installs, agent check-ins and AWS API calls in ways that are tedious to diagnose. Restrict outbound only where the value justifies the effort, and expect to add VPC endpoints when you do.

Rule structure

Each rule is a protocol, a port range, and a source (inbound) or destination (outbound):

aws ec2 authorize-security-group-ingress \
  --group-id sg-web \
  --ip-permissions '[{
    "IpProtocol":"tcp","FromPort":443,"ToPort":443,
    "IpRanges":[{"CidrIp":"0.0.0.0/0","Description":"Public HTTPS"}]
  }]'

Always set a Description. Six months later, an undocumented rule allowing an unfamiliar CIDR is one nobody dares remove, so it stays forever.

Reference other security groups, not CIDRs

This is the single most useful thing security groups do, and it is underused.

# The database accepts Postgres only from the application tier
aws ec2 authorize-security-group-ingress \
  --group-id sg-database \
  --protocol tcp --port 5432 \
  --source-group sg-application

The rule now means "any instance carrying sg-application", which keeps working as instances are created, replaced and scaled. A CIDR-based equivalent has to be maintained by hand and breaks the moment the fleet changes.

That gives you a clean tiered design:

Internet ──► sg-alb          (443 from 0.0.0.0/0)
                │
                ▼
            sg-application   (8080 from sg-alb)
                │
                ▼
            sg-database      (5432 from sg-application)

Nothing in that diagram contains a hardcoded IP address, and no rule needs updating when the application scales.

Security group references work within a VPC, and across peered VPCs in the same region.

Things not to do

0.0.0.0/0 on port 22 or 3389. Automated scanners find an open SSH port within minutes of it opening. Restrict to a known CIDR, or better, remove SSH entirely and use SSM Session Manager, which needs no inbound rule at all.

0.0.0.0/0 on a database port. 5432, 3306 and 27017 open to the internet is how databases get ransomed. Databases belong in private subnets with a security group reference.

Opening a wide port range because the exact port is unclear. Find the port.

# Audit for the obvious problems
aws ec2 describe-security-groups \
  --filters Name=ip-permission.cidr,Values=0.0.0.0/0 \
  --query 'SecurityGroups[].{ID:GroupId,Name:GroupName,Ports:IpPermissions[?contains(IpRanges[].CidrIp, `0.0.0.0/0`)].[FromPort,ToPort]}'

AWS Config and Security Hub can flag these continuously, which is better than remembering to audit.

Limits worth knowing

LimitDefault
Rules per security group60 inbound, 60 outbound
Security groups per network interface5
Referenced groups counted per ruleEach reference counts as a rule

Most are adjustable, but the 60-rule limit is reached surprisingly quickly by teams that list individual office IPs. That is a signal to use a prefix list. A managed, named collection of CIDR blocks referenced as a single rule, and updated in one place.

Debugging blocked traffic

Security groups produce timeouts, not refusals. A blocked packet is dropped silently, so the client hangs until it gives up. A fast "connection refused" means the packet arrived and nothing was listening, which is an application problem, not a security group one.

That distinction alone saves a lot of time.

Check in this order:

  1. Destination inbound rule. Does it allow the source on that port?
  2. Source outbound rule. Default is allow-all, but someone may have tightened it.
  3. Route table. Is there a path at all?
  4. NACL. Both directions, remembering statelessness.
  5. The application. Is it actually listening, and on the right interface?
# What rules does it actually have?
aws ec2 describe-security-groups --group-ids sg-database \
  --query 'SecurityGroups[0].IpPermissions'

# Which interfaces use this group?
aws ec2 describe-network-interfaces \
  --filters Name=group-id,Values=sg-database \
  --query 'NetworkInterfaces[].{ID:NetworkInterfaceId,Desc:Description}'

VPC Reachability Analyzer does the whole check automatically and names the blocking component, which beats working through the list by hand:

aws ec2 create-network-insights-path \
  --source i-0app --destination i-0db --protocol tcp --destination-port 5432

And VPC flow logs record REJECT entries, so you can confirm whether traffic arrived and was dropped rather than never arriving.

Quick reference

TaskCommand
List groupsaws ec2 describe-security-groups --query 'SecurityGroups[].[GroupId,GroupName]'
Inspect rulesaws ec2 describe-security-groups --group-ids <sg>
Allow from a CIDRaws ec2 authorize-security-group-ingress --group-id <sg> --protocol tcp --port 443 --cidr <cidr>
Allow from another groupaws ec2 authorize-security-group-ingress --group-id <sg> --protocol tcp --port 5432 --source-group <sg>
Remove a ruleaws ec2 revoke-security-group-ingress --group-id <sg> --protocol tcp --port 22 --cidr 0.0.0.0/0
Find world-open rulesaws ec2 describe-security-groups --filters Name=ip-permission.cidr,Values=0.0.0.0/0
Who uses this groupaws ec2 describe-network-interfaces --filters Name=group-id,Values=<sg>
Diagnose a pathaws ec2 create-network-insights-path --source <a> --destination <b> --protocol tcp

Key takeaways

  • Stateful: allow inbound and the response is automatic. No return rule needed.
  • Allow-only and additive. Multiple groups union together; you cannot deny with a security group.
  • Inbound denies everything, outbound allows everything by default.
  • Reference other security groups instead of CIDRs so rules survive instance churn.
  • Never open 22, 3389 or a database port to 0.0.0.0/0. Use Session Manager and private subnets.
  • 60 rules per group. Hitting it means you want a prefix list.
  • Blocked traffic times out; a refusal means the application is the problem.

Check your understanding

Exercise

Timeout or refused

A client connecting to an EC2 instance gets an immediate "connection refused" rather than hanging. What does that tell you about the security group?

Exercise

Rules that survive scaling

An Auto Scaling group replaces application instances regularly. What is the best way to let them reach the database?


Frequently Asked Questions

What is the difference between a security group and a network ACL?

A security group attaches to an instance or network interface, is stateful so return traffic is automatically allowed, and contains only allow rules. A network ACL attaches to a subnet, is stateless so each direction requires its own rule, and supports both allow and deny evaluated in numbered order. Use security groups as the primary control and NACLs only for coarse subnet-level blocking. The stateless behaviour of NACLs is the most common cause of confusing half-working connectivity in AWS.

Why can't I add a deny rule to a security group?

Because security groups are allow-only by design. Everything not explicitly permitted is denied implicitly, and rules from multiple attached groups combine additively, so there is no mechanism to subtract permission that another group granted. If you genuinely need to block specific traffic, such as a known-bad CIDR range, that belongs in a network ACL at the subnet level, which does support explicit deny rules.

Should I restrict outbound rules?

It is a real security improvement, since an instance that can only reach what it needs limits what an attacker can do after a compromise, including preventing data exfiltration to arbitrary destinations. It is also genuinely fiddly: package repositories, agent check-ins, and AWS API calls all break in ways that take time to diagnose, and you will usually need VPC endpoints to compensate. Restrict outbound on high-value workloads where the effort is justified, and leave the permissive default elsewhere.

How do I let one security group talk to another?

Use the other group as the source rather than a CIDR: aws ec2 authorize-security-group-ingress --group-id sg-database --protocol tcp --port 5432 --source-group sg-application. The rule then applies to any network interface carrying that group, so it keeps working as instances are replaced and scaled without any rule maintenance. This works within a VPC and across peered VPCs in the same region, and it is the foundation of a clean tiered network design.

My connection times out. Is it the security group?

Probably, because security groups drop disallowed packets silently and a client hangs until it times out. Check the destination's inbound rules first, then the source's outbound rules, then route tables, then NACLs in both directions. A fast "connection refused" instead means the packet arrived and nothing was listening, which points at the application rather than networking. VPC Reachability Analyzer tests the whole path and names the blocking component, which is quicker than checking each layer manually.

How many rules can a security group have?

Sixty inbound and sixty outbound by default, with up to five security groups per network interface, and each referenced group counts as a rule. Most of these limits can be raised on request. Reaching the rule limit usually means you are listing individual IP addresses, for example, every office and VPN endpoint, which is better handled with a managed prefix list, a named collection of CIDR blocks referenced as a single rule and maintained in one place.

Continue Learning

Explore Related Topics

When it breaks

Related Resources