Security Groups Design Patterns in AWS
The Tiered Security Group Pattern: Building Intent-Based Network Boundaries in AWS
Security groups are among the most important—and most frequently misconfigured—security controls in an AWS environment.
A security group acts as a stateful virtual firewall for resources such as EC2 instances, load balancers, and databases. It controls which traffic may enter or leave a resource based on protocol, port, and source or destination. Because security groups are stateful, return traffic for an allowed connection is automatically permitted. (docs.aws.amazon.com)
The simplest implementation might attach one broadly permissive security group to every resource in an application. Although convenient, this approach creates an unnecessarily large attack surface. If one system is compromised, the attacker may be able to communicate directly with databases, management interfaces, monitoring systems, and other sensitive components.
A stronger approach is the Tiered Security Group Pattern.
Instead of treating the VPC as one trusted network, this pattern creates a distinct security boundary around each architectural tier, environment, and application. Communication is permitted only where there is a documented business or technical requirement.
What Is the Tiered Security Group Pattern?
The Tiered Security Group Pattern assigns different security groups to resources with different functions.
A typical three-tier application might use:
- An internet-facing load balancer security group
- A web-tier security group
- An application-tier security group
- A database-tier security group
- An administrative-access security group
- A monitoring or observability security group
Each security group describes the network role of the resources associated with it.
For example:
| Destination tier | Allowed source | Port | Purpose |
|---|---|---|---|
| Public load balancer | Internet | 443 | Public HTTPS access |
| Web tier | Load balancer SG | 443 or application port | Requests forwarded by the load balancer |
| Application tier | Web-tier SG | 443 or 8443 | Application API calls |
| Database tier | Application-tier SG | 5432 | PostgreSQL connections |
| Monitoring agent | Monitoring SG | Required telemetry ports | Metrics and health monitoring |
| Administrative tier | Approved management path | Required admin port | Controlled administration |
The resulting communication path is deliberate:
Internet → Load Balancer → Web Tier → Application Tier → Database Tier
The database is not reachable from the internet, the load balancer, or the web tier. It accepts connections only from resources associated with the application-tier security group.
This is network least privilege expressed in architectural terms.
Use Security Group References Instead of Hard-Coded IP Addresses
One of the most valuable characteristics of AWS security groups is the ability to reference another security group as the source or destination of a rule.
Suppose the application tier must accept HTTPS connections from the web tier. The application security group can contain the following inbound rule:
Protocol: TCP
Port: 443
Source: sg-web-tier
The rule effectively says:
Permit TCP port 443 from resources associated with the approved web-tier security group.
This is superior to a rule such as:
Protocol: TCP
Port: 443
Source: 10.20.10.0/24
The CIDR-based rule trusts every resource that can obtain an address within that subnet. The security-group-based rule trusts only resources assigned the expected security identity.
AWS evaluates incoming traffic from a referenced security group using the private IP addresses of the resources associated with that group. (Amazon Elastic Compute Cloud)
Why SG-to-SG references are more resilient
Cloud resources are dynamic. Instances are replaced, containers move, Auto Scaling groups expand and contract, and load balancer nodes change over time.
If access depends on individual IP addresses, every infrastructure change can require a corresponding firewall change. That creates several problems:
- Rules become stale.
- Old addresses remain trusted.
- New instances may not work until rules are updated.
- Operations teams add broad CIDR ranges as a workaround.
- Security reviews become difficult because the purpose of each IP address is unclear.
A security group reference avoids this dependency. Access follows the resource’s assigned role rather than its current IP address.
The rule remains valid when an instance is replaced or an application scales horizontally—as long as the appropriate security group is attached to the new resource.
1. Tier-Based Security Groups
Tier-based security groups separate resources according to their position and function within an architecture.
Load balancer security group
The public load balancer may accept HTTPS traffic from the internet:
Inbound:
TCP 443 from 0.0.0.0/0
TCP 443 from ::/0
Public access should terminate at a deliberately exposed service such as an Application Load Balancer rather than directly on application servers.
If CloudFront is the intended entry point, the load balancer can be restricted further by using the appropriate AWS-managed prefix list instead of accepting traffic from the entire internet.
Web-tier security group
The web tier should accept traffic only from the load balancer security group:
Inbound:
TCP 443 from sg-public-alb
This prevents clients from bypassing the load balancer and connecting directly to a web server—even if routing or another configuration mistake makes the server reachable.
AWS specifically supports configuring target security groups to accept traffic only from the security group associated with a load balancer. (Elastic Load Balancing)
Application-tier security group
The application tier should accept only the protocols required by the web tier:
Inbound:
TCP 8443 from sg-web
A web server compromise would therefore give an attacker access only to the application endpoint explicitly authorized by this rule. It would not automatically provide access to SSH, management interfaces, databases, or unrelated services.
Database-tier security group
The database security group should allow the database port only from the application security group:
Inbound:
TCP 5432 from sg-application
It should not allow database access from:
- The internet
- The complete VPC CIDR
- Every private subnet
- The load balancer
- The web tier
- Developer workstations
- A general-purpose “internal” security group
AWS recommends creating security group rules based on the role of the associated resource—for example, web-server rules for web servers and database-specific rules for database systems. (docs.aws.amazon.com)
Administrative security group
Administrative access deserves its own boundary.
Avoid opening SSH or RDP to the internet. AWS Security Hub treats unrestricted access to SSH, RDP, and other high-risk ports as a significant security issue. (docs.aws.amazon.com)
Where possible, use AWS Systems Manager Session Manager so instances do not require publicly exposed administrative ports. If a bastion host or management appliance is necessary, create a dedicated security group and restrict access to an approved VPN, corporate network, or managed prefix list.
2. Environment-Isolated Security Groups
Production, staging, development, and test environments should not share the same application security groups.
Use separate security group sets such as:
prod-alb-sg
prod-web-sg
prod-app-sg
prod-db-sg
stage-alb-sg
stage-web-sg
stage-app-sg
stage-db-sg
dev-alb-sg
dev-web-sg
dev-app-sg
dev-db-sg
This prevents a development resource from becoming implicitly trusted by production simply because both systems perform a similar function.
For stronger isolation, place environments in separate AWS accounts and VPCs. Security groups then reinforce the account and network boundaries rather than attempting to replace them.
Production security groups should generally have:
- Fewer permitted flows
- Tighter egress controls
- Separate change approval
- Infrastructure-as-code ownership
- Automated compliance monitoring
- Alerts for unauthorized modifications
Development environments can remain flexible without forcing production to inherit the same permissive rules.
Avoid cross-environment references
Rules such as the following should be treated with suspicion:
prod-db-sg:
TCP 5432 from dev-app-sg
If developers need production data access, provide a controlled access mechanism with explicit authentication, authorization, auditing, and time-bounded approval. Do not create a permanent network trust relationship merely because it is operationally convenient.
3. Application-Scoped Security Groups
Tier separation alone may not be sufficient in a shared VPC.
Imagine that ten applications use the same application-tier security group. If the database security group trusts that shared group, every application associated with it may be able to reach the database—even when only one application requires access.
A stronger design gives each application or service its own security groups:
payments-prod-api-sg
payments-prod-db-sg
orders-prod-api-sg
orders-prod-db-sg
customer-prod-api-sg
customer-prod-db-sg
The Payments database accepts traffic only from the Payments application security group. The Orders application cannot connect to it simply because both workloads reside in the same subnet or application tier.
This creates a more meaningful security boundary:
Access is granted to a specific application role, not to a broad category of machines.
Application teams can own the ingress rules for their services, subject to centrally enforced guardrails.
Ingress Should Be Application-Owned
Each service should define who may initiate connections to it.
For example, the team responsible for an internal pricing API might own an ingress security group containing:
TCP 443 from sg-checkout-service
TCP 443 from sg-order-service
These rules clearly identify the approved consumers of the API.
This model improves:
- Ownership
- Change accountability
- Impact analysis
- Incident response
- Architecture reviews
- Automated policy validation
When a service is retired or a dependency is removed, its SG-to-SG rule can be removed without reviewing unrelated subnet ranges.
Treat Shared Egress Security Groups Carefully
Some organizations use a small, centrally managed egress security group for common destinations such as:
- DNS services
- Approved HTTPS endpoints
- Central proxies
- Monitoring collectors
- Patch repositories
- VPC endpoints
This can be useful, but there is an important AWS behavior to understand: when multiple security groups are associated with a resource, their rules are combined. Security groups do not contain explicit deny rules. A permissive rule in any attached security group can therefore undermine tighter rules in the others. (Amazon Virtual Private Cloud)
A shared egress security group should consequently contain only narrowly defined, genuinely common access. Do not attach an “allow all outbound” group and assume an application-specific security group can override it.
For precise egress control, consider a combination of:
- Destination security-group references
- AWS-managed or customer-managed prefix lists
- VPC endpoints
- Central egress proxies
- Route 53 Resolver DNS Firewall
- AWS Network Firewall
- Explicit application-level allowlists
Security Groups Describe Permission, Not Connectivity
An SG-to-SG rule permits traffic; it does not create a network path.
The architecture still requires:
- Valid routes
- Compatible subnet routing
- Correct load balancer configuration
- Working DNS resolution
- Network ACL compatibility
- Listening application services
- VPC peering, Transit Gateway, or another supported connection where applicable
Security group references also require special care when traffic passes through a middlebox or inspection appliance. AWS documents that SG references between the original source and final destination might not work when the traffic is routed through a middlebox. In those cases, private IP addresses or appropriate subnet CIDRs may be required. (Amazon Virtual Private Cloud)
Security-group references should therefore be the default for direct workload relationships, but they are not a universal substitute for understanding the traffic path.
Naming and Tagging Standards
A security group should communicate its purpose without requiring an engineer to inspect every rule.
A practical naming format is:
<application>-<environment>-<tier>-sg
Examples:
payments-prod-alb-sg
payments-prod-api-sg
payments-prod-db-sg
payments-prod-monitoring-sg
Recommended tags include:
| Tag | Example |
|---|---|
| Application | Payments |
| Environment | Production |
| Tier | Database |
| Owner | PaymentsPlatform |
| ManagedBy | Terraform |
| DataClassification | Confidential |
| Criticality | High |
| CostCenter | CC-1042 |
Rule descriptions should also be mandatory:
Allow PostgreSQL from Payments API
Allow HTTPS health checks from production ALB
Allow telemetry to central monitoring collector
Descriptions such as “temporary,” “internal,” or “required for testing” provide little governance value and have a habit of becoming permanent.
Infrastructure as Code
Tiered security groups should be created through Terraform, AWS CloudFormation, AWS CDK, or another controlled infrastructure-as-code process.
A simplified Terraform example looks like this:
resource "aws_security_group" "app" {
name = "payments-prod-app-sg"
description = "Security group for the Payments production application tier"
vpc_id = aws_vpc.production.id
}
resource "aws_vpc_security_group_ingress_rule" "app_from_web" {
security_group_id = aws_security_group.app.id
referenced_security_group_id = aws_security_group.web.id
ip_protocol = "tcp"
from_port = 8443
to_port = 8443
description = "Allow application traffic from Payments web tier"
}
resource "aws_security_group" "database" {
name = "payments-prod-db-sg"
description = "Security group for the Payments production database"
vpc_id = aws_vpc.production.id
}
resource "aws_vpc_security_group_ingress_rule" "database_from_app" {
security_group_id = aws_security_group.database.id
referenced_security_group_id = aws_security_group.app.id
ip_protocol = "tcp"
from_port = 5432
to_port = 5432
description = "Allow PostgreSQL from Payments application tier"
}
Infrastructure as code provides version history, peer review, repeatability, and automated testing. It also reduces configuration drift between environments.
Governance and Continuous Validation
A good pattern can deteriorate if nobody monitors it.
Organizations should continuously check for:
0.0.0.0/0and::/0on unauthorized ports- Databases reachable from public or broad internal sources
- SSH and RDP exposed to the internet
- Rules covering entire VPC CIDRs without justification
- Cross-environment access
- Cross-application trust
- Unused security groups
- Duplicate security groups
- Stale referenced security groups
- Security groups changed outside the approved deployment process
- Default security groups containing active rules
- Unrestricted outbound access from sensitive workloads
AWS Config can evaluate security group configurations, while Security Hub provides controls for unrestricted high-risk ports and other unsafe network configurations. AWS Firewall Manager can centrally apply, audit, and remediate security group policies across an AWS organization. It can also identify unused or redundant security groups. (AWS Config)
VPC Flow Logs and Network Access Analyzer can provide additional visibility into actual traffic and unintended network paths. (docs.aws.amazon.com)
Common Anti-Patterns
One security group for the entire application
This eliminates meaningful separation between web, application, database, and administration tiers.
Trusting the entire VPC
A rule allowing a database connection from the complete VPC CIDR treats every workload in the VPC as trusted.
Reusing production SGs in non-production
This creates unintended trust relationships and weakens environment isolation.
Hard-coding individual private IP addresses
This makes security rules fragile and difficult to maintain in an elastic environment.
Leaving the default outbound rule unchanged everywhere
New security groups commonly begin with an outbound rule permitting all traffic. Whether that is acceptable should be an explicit risk decision, not an unnoticed default. AWS allows this rule to be removed and replaced with specific outbound permissions. (Amazon Virtual Private Cloud)
Opening management ports temporarily
Temporary 0.0.0.0/0 SSH and RDP rules are frequently forgotten. Use Session Manager, VPN-based administration, or an automatically expiring access workflow.
Attaching too many security groups
Because rules are aggregated, attaching multiple SGs can create permissions that are difficult to understand. More security groups do not automatically mean more security.
A Practical Design Principle
Every security group rule should answer four questions:
- Who is initiating the connection?
- Which service is receiving it?
- Which protocol and port are required?
- Why is the connection necessary?
If a rule cannot answer those questions clearly, it is probably too broad.
The Tiered Security Group Pattern transforms security groups from collections of ports and IP addresses into an expression of application architecture:
- The load balancer may communicate with the web tier.
- The web tier may communicate with the application tier.
- The application tier may communicate with its database.
- Monitoring systems may reach defined telemetry endpoints.
- Administrators may enter only through an approved management path.
- Development systems may not communicate with production.
- One application may not inherit access intended for another.
That is the central idea: grant access according to workload identity, architectural tier, application ownership, and environment—not merely according to network location.
When properly implemented, tiered security groups reduce lateral movement, contain compromised workloads, simplify security reviews, and make the intended architecture visible directly in the network policy.
“`
Also read VPC Security Best Practices
The Tiered Security Group Pattern: Building Intent-Based Network Boundaries in AWS
Security groups are among the most important—and most frequently misconfigured—security controls in an AWS environment.
A security group acts as a stateful virtual firewall for resources such as EC2 instances, load balancers, and databases. It controls which traffic may enter or leave a resource based on protocol, port, and source or destination. Because security groups are stateful, return traffic for an allowed connection is automatically permitted.
The simplest implementation might attach one broadly permissive security group to every resource in an application. Although convenient, this approach creates an unnecessarily large attack surface. If one system is compromised, the attacker may be able to communicate directly with databases, management interfaces, monitoring systems, and other sensitive components.
A stronger approach is the Tiered Security Group Pattern.
Instead of treating the VPC as one trusted network, this pattern creates a distinct security boundary around each architectural tier, environment, and application. Communication is permitted only where there is a documented business or technical requirement.
What Is the Tiered Security Group Pattern?
The Tiered Security Group Pattern assigns different security groups to resources with different functions.
A typical three-tier application might use:
- An internet-facing load balancer security group
- A web-tier security group
- An application-tier security group
- A database-tier security group
- An administrative-access security group
- A monitoring or observability security group
Each security group describes the network role of the resources associated with it.
For example:
| Destination tier | Allowed source | Port | Purpose |
|---|---|---|---|
| Public load balancer | Internet | 443 | Public HTTPS access |
| Web tier | Load balancer SG | 443 or application port | Requests forwarded by the load balancer |
| Application tier | Web-tier SG | 443 or 8443 | Application API calls |
| Database tier | Application-tier SG | 5432 | PostgreSQL connections |
| Monitoring agent | Monitoring SG | Required telemetry ports | Metrics and health monitoring |
| Administrative tier | Approved management path | Required admin port | Controlled administration |
The resulting communication path is deliberate:
Internet → Load Balancer → Web Tier → Application Tier → Database Tier
The database is not reachable from the internet, the load balancer, or the web tier. It accepts connections only from resources associated with the application-tier security group.
This is network least privilege expressed in architectural terms.
Use Security Group References Instead of Hard-Coded IP Addresses
One of the most valuable characteristics of AWS security groups is the ability to reference another security group as the source or destination of a rule.
Suppose the application tier must accept HTTPS connections from the web tier. The application security group can contain the following inbound rule:
Protocol: TCP
Port: 443
Source: sg-web-tier
The rule effectively says:
Permit TCP port 443 from resources associated with the approved web-tier security group.
This is superior to a rule such as:
Protocol: TCP
Port: 443
Source: 10.20.10.0/24
The CIDR-based rule trusts every resource that can obtain an address within that subnet. The security-group-based rule trusts only resources assigned the expected security identity.
AWS evaluates incoming traffic from a referenced security group using the private IP addresses of the resources associated with that group.
Why SG-to-SG references are more resilient
Cloud resources are dynamic. Instances are replaced, containers move, Auto Scaling groups expand and contract, and load balancer nodes change over time.
If access depends on individual IP addresses, every infrastructure change can require a corresponding firewall change. That creates several problems:
- Rules become stale.
- Old addresses remain trusted.
- New instances may not work until rules are updated.
- Operations teams add broad CIDR ranges as a workaround.
- Security reviews become difficult because the purpose of each IP address is unclear.
A security group reference avoids this dependency. Access follows the resource’s assigned role rather than its current IP address.
The rule remains valid when an instance is replaced or an application scales horizontally—as long as the appropriate security group is attached to the new resource.
1. Tier-Based Security Groups
Tier-based security groups separate resources according to their position and function within an architecture.
Load balancer security group
The public load balancer may accept HTTPS traffic from the internet:
Inbound:
TCP 443 from 0.0.0.0/0
TCP 443 from ::/0
Public access should terminate at a deliberately exposed service such as an Application Load Balancer rather than directly on application servers.
If CloudFront is the intended entry point, the load balancer can be restricted further by using the appropriate AWS-managed prefix list instead of accepting traffic from the entire internet.
Web-tier security group
The web tier should accept traffic only from the load balancer security group:
Inbound:
TCP 443 from sg-public-alb
This prevents clients from bypassing the load balancer and connecting directly to a web server—even if routing or another configuration mistake makes the server reachable.
AWS specifically supports configuring target security groups to accept traffic only from the security group associated with a load balancer.
Application-tier security group
The application tier should accept only the protocols required by the web tier:
Inbound:
TCP 8443 from sg-web
A web server compromise would therefore give an attacker access only to the application endpoint explicitly authorized by this rule. It would not automatically provide access to SSH, management interfaces, databases, or unrelated services.
Database-tier security group
The database security group should allow the database port only from the application security group:
Inbound:
TCP 5432 from sg-application
It should not allow database access from:
- The internet
- The complete VPC CIDR
- Every private subnet
- The load balancer
- The web tier
- Developer workstations
- A general-purpose “internal” security group
AWS recommends creating security group rules based on the role of the associated resource—for example, web-server rules for web servers and database-specific rules for database systems.
Administrative security group
Administrative access deserves its own boundary.
Avoid opening SSH or RDP to the internet. AWS Security Hub treats unrestricted access to SSH, RDP, and other high-risk ports as a significant security issue.
Where possible, use AWS Systems Manager Session Manager so instances do not require publicly exposed administrative ports. If a bastion host or management appliance is necessary, create a dedicated security group and restrict access to an approved VPN, corporate network, or managed prefix list.
2. Environment-Isolated Security Groups
Production, staging, development, and test environments should not share the same application security groups.
Use separate security group sets such as:
prod-alb-sg
prod-web-sg
prod-app-sg
prod-db-sg
stage-alb-sg
stage-web-sg
stage-app-sg
stage-db-sg
dev-alb-sg
dev-web-sg
dev-app-sg
dev-db-sg
This prevents a development resource from becoming implicitly trusted by production simply because both systems perform a similar function.
For stronger isolation, place environments in separate AWS accounts and VPCs. Security groups then reinforce the account and network boundaries rather than attempting to replace them.
Production security groups should generally have:
- Fewer permitted flows
- Tighter egress controls
- Separate change approval
- Infrastructure-as-code ownership
- Automated compliance monitoring
- Alerts for unauthorized modifications
Development environments can remain flexible without forcing production to inherit the same permissive rules.
Avoid cross-environment references
Rules such as the following should be treated with suspicion:
prod-db-sg:
TCP 5432 from dev-app-sg
If developers need production data access, provide a controlled access mechanism with explicit authentication, authorization, auditing, and time-bounded approval. Do not create a permanent network trust relationship merely because it is operationally convenient.
3. Application-Scoped Security Groups
Tier separation alone may not be sufficient in a shared VPC.
Imagine that ten applications use the same application-tier security group. If the database security group trusts that shared group, every application associated with it may be able to reach the database—even when only one application requires access.
A stronger design gives each application or service its own security groups:
payments-prod-api-sg
payments-prod-db-sg
orders-prod-api-sg
orders-prod-db-sg
customer-prod-api-sg
customer-prod-db-sg
The Payments database accepts traffic only from the Payments application security group. The Orders application cannot connect to it simply because both workloads reside in the same subnet or application tier.
This creates a more meaningful security boundary:
Access is granted to a specific application role, not to a broad category of machines.
Application teams can own the ingress rules for their services, subject to centrally enforced guardrails.
Ingress Should Be Application-Owned
Each service should define who may initiate connections to it.
For example, the team responsible for an internal pricing API might own an ingress security group containing:
TCP 443 from sg-checkout-service
TCP 443 from sg-order-service
These rules clearly identify the approved consumers of the API.
This model improves:
- Ownership
- Change accountability
- Impact analysis
- Incident response
- Architecture reviews
- Automated policy validation
When a service is retired or a dependency is removed, its SG-to-SG rule can be removed without reviewing unrelated subnet ranges.
Treat Shared Egress Security Groups Carefully
Some organizations use a small, centrally managed egress security group for common destinations such as:
- DNS services
- Approved HTTPS endpoints
- Central proxies
- Monitoring collectors
- Patch repositories
- VPC endpoints
This can be useful, but there is an important AWS behavior to understand: when multiple security groups are associated with a resource, their rules are combined. Security groups do not contain explicit deny rules. A permissive rule in any attached security group can therefore undermine tighter rules in the others.
A shared egress security group should consequently contain only narrowly defined, genuinely common access. Do not attach an “allow all outbound” group and assume an application-specific security group can override it.
For precise egress control, consider a combination of:
- Destination security-group references
- AWS-managed or customer-managed prefix lists
- VPC endpoints
- Central egress proxies
- Route 53 Resolver DNS Firewall
- AWS Network Firewall
- Explicit application-level allowlists
Security Groups Describe Permission, Not Connectivity
An SG-to-SG rule permits traffic; it does not create a network path.
The architecture still requires:
- Valid routes
- Compatible subnet routing
- Correct load balancer configuration
- Working DNS resolution
- Network ACL compatibility
- Listening application services
- VPC peering, Transit Gateway, or another supported connection where applicable
Security group references also require special care when traffic passes through a middlebox or inspection appliance. AWS documents that SG references between the original source and final destination might not work when the traffic is routed through a middlebox. In those cases, private IP addresses or appropriate subnet CIDRs may be required.
Security-group references should therefore be the default for direct workload relationships, but they are not a universal substitute for understanding the traffic path.
Naming and Tagging Standards
A security group should communicate its purpose without requiring an engineer to inspect every rule.
A practical naming format is:
<application>-<environment>-<tier>-sg
Examples:
payments-prod-alb-sg
payments-prod-api-sg
payments-prod-db-sg
payments-prod-monitoring-sg
Recommended tags include:
| Tag | Example |
|---|---|
| Application | Payments |
| Environment | Production |
| Tier | Database |
| Owner | PaymentsPlatform |
| ManagedBy | Terraform |
| DataClassification | Confidential |
| Criticality | High |
| CostCenter | CC-1042 |
Rule descriptions should also be mandatory:
Allow PostgreSQL from Payments API
Allow HTTPS health checks from production ALB
Allow telemetry to central monitoring collector
Descriptions such as “temporary,” “internal,” or “required for testing” provide little governance value and have a habit of becoming permanent.
Infrastructure as Code
Tiered security groups should be created through Terraform, AWS CloudFormation, AWS CDK, or another controlled infrastructure-as-code process.
A simplified Terraform example looks like this:
resource "aws_security_group" "app" {
name = "payments-prod-app-sg"
description = "Security group for the Payments production application tier"
vpc_id = aws_vpc.production.id
}
resource "aws_vpc_security_group_ingress_rule" "app_from_web" {
security_group_id = aws_security_group.app.id
referenced_security_group_id = aws_security_group.web.id
ip_protocol = "tcp"
from_port = 8443
to_port = 8443
description = "Allow application traffic from Payments web tier"
}
resource "aws_security_group" "database" {
name = "payments-prod-db-sg"
description = "Security group for the Payments production database"
vpc_id = aws_vpc.production.id
}
resource "aws_vpc_security_group_ingress_rule" "database_from_app" {
security_group_id = aws_security_group.database.id
referenced_security_group_id = aws_security_group.app.id
ip_protocol = "tcp"
from_port = 5432
to_port = 5432
description = "Allow PostgreSQL from Payments application tier"
}
Infrastructure as code provides version history, peer review, repeatability, and automated testing. It also reduces configuration drift between environments.
Governance and Continuous Validation
A good pattern can deteriorate if nobody monitors it.
Organizations should continuously check for:
0.0.0.0/0and::/0on unauthorized ports- Databases reachable from public or broad internal sources
- SSH and RDP exposed to the internet
- Rules covering entire VPC CIDRs without justification
- Cross-environment access
- Cross-application trust
- Unused security groups
- Duplicate security groups
- Stale referenced security groups
- Security groups changed outside the approved deployment process
- Default security groups containing active rules
- Unrestricted outbound access from sensitive workloads
AWS Config can evaluate security group configurations, while Security Hub provides controls for unrestricted high-risk ports and other unsafe network configurations. AWS Firewall Manager can centrally apply, audit, and remediate security group policies across an AWS organization. It can also identify unused or redundant security groups.
VPC Flow Logs and Network Access Analyzer can provide additional visibility into actual traffic and unintended network paths.
Common Anti-Patterns
One security group for the entire application
This eliminates meaningful separation between web, application, database, and administration tiers.
Trusting the entire VPC
A rule allowing a database connection from the complete VPC CIDR treats every workload in the VPC as trusted.
Reusing production SGs in non-production
This creates unintended trust relationships and weakens environment isolation.
Hard-coding individual private IP addresses
This makes security rules fragile and difficult to maintain in an elastic environment.
Leaving the default outbound rule unchanged everywhere
New security groups commonly begin with an outbound rule permitting all traffic. Whether that is acceptable should be an explicit risk decision, not an unnoticed default. AWS allows this rule to be removed and replaced with specific outbound permissions.
Opening management ports temporarily
Temporary 0.0.0.0/0 SSH and RDP rules are frequently forgotten. Use Session Manager, VPN-based administration, or an automatically expiring access workflow.
Attaching too many security groups
Because rules are aggregated, attaching multiple SGs can create permissions that are difficult to understand. More security groups do not automatically mean more security.
A Practical Design Principle
Every security group rule should answer four questions:
- Who is initiating the connection?
- Which service is receiving it?
- Which protocol and port are required?
- Why is the connection necessary?
If a rule cannot answer those questions clearly, it is probably too broad.
The Tiered Security Group Pattern transforms security groups from collections of ports and IP addresses into an expression of application architecture:
- The load balancer may communicate with the web tier.
- The web tier may communicate with the application tier.
- The application tier may communicate with its database.
- Monitoring systems may reach defined telemetry endpoints.
- Administrators may enter only through an approved management path.
- Development systems may not communicate with production.
- One application may not inherit access intended for another.
That is the central idea: grant access according to workload identity, architectural tier, application ownership, and environment—not merely according to network location.
When properly implemented, tiered security groups reduce lateral movement, contain compromised workloads, simplify security reviews, and make the intended architecture visible directly in the network policy.
Leave a Reply