Security groups have no deny rules — and that is the point
You cannot block an IP with an AWS security group. That looks like a missing feature until you notice what it buys: no rule ordering, no shadowing, and the ability to authorise a role instead of an address.
Every engineer coming to AWS from iptables asks the same question within their first week:
How do I block this IP in a security group?
You cannot. There is no deny rule. And the usual next thought — then what use is it? — is worth taking seriously, because the answer explains a genuinely different model of access control.
The premise is right, the conclusion is backwards
A security group is default-deny. There is no policy to set, no final DROP to remember. An empty security group blocks everything inbound. Anything not explicitly allowed is refused.
So invert the question. You are asking “how do I block this address?” — but the only reason that address could reach you at all is that you allowed it, almost always via a 0.0.0.0/0 rule you wrote yourself. In a default-deny allowlist, blocking is not an operation you perform. It is the state you departed from.
The analogy that holds: a security group is a guest list, not a banned-persons list. You do not strike a name off the banned list — you simply do not write it on the guest list. And there is no need for a banned list, because nobody gets in without appearing on the guest list.
The legitimate version of the question
There is a real gap here, and it is narrower than it first appears:
How do I block one address inside a range I have already allowed?
You run a public website, so port 443 is open to 0.0.0.0/0, and now one address is abusing it. A security group genuinely cannot express that. You need a different layer:
| Need | Layer |
|---|---|
| Block one IP inside an allowed range | NACL deny rule, host firewall, or WAF |
| Block thousands of addresses, changing constantly | ipset — one rule, O(1) hash lookup |
| Block after N failed attempts | fail2ban or CrowdSec |
| Block on URL, header or payload | WAF — security groups are L3/L4 only |
This is why the stack has layers. Not redundancy for its own sake — each layer expresses something the others cannot. NACLs earn their place in an AWS architecture almost entirely because they have deny rules and ordered evaluation, and security groups do not.
What you get for giving up deny
Because a security group is an unordered set of allow rules, it acquires properties that an ordered allow/deny list cannot have. If you have written iptables at any scale, you will recognise every one of these as a problem you have hit.
Order-independence. There is no precedence and no shadowing. In iptables, inserting at the wrong index silently changes behaviour, and appending after a broad REJECT produces dead code that still appears in the listing. You cannot break a security group by reordering it, because it has no order.
No accidental allow-all. In any deny/allow list you can place a permissive rule above a restrictive one and quietly open everything while the config still reads as locked down. Structurally impossible in a set where every entry only widens access and no entry interacts with another.
Composability. Rules are additive, so several security groups on one interface produce their union. Different owners can hold different groups — a baseline maintained centrally, plus one owned by your team — and they cannot conflict. Merging two ordered allow/deny lists is ambiguous; merging two allowlists is not.
Auditability. The output of describe-security-groups is the complete answer to “what can reach this?” There is nothing else to consult and no evaluation to simulate. With an ordered list you must mentally execute it to know the effective policy, which is exactly why firewall audits are painful and security group audits are a script.
Statefulness with no return rules. Allow inbound 443 and the response leaves automatically. Compare the classic NACL mistake: allow inbound 443, forget outbound 1024-65535, and every connection hangs — because the stateless layer does not know those outbound packets are replies.
The capability that settles the argument
Here is what a security group can do that iptables, ipsets and NACLs cannot express at all:
# Database security group, inbound
Port 3306 ← source: sg-0a1b2c3d (the application tier's security group)
There is no IP address in that rule. It reads: any instance carrying the app-tier security group may reach MySQL. Follow the consequences:
- It authorises a role, not a location. This is the same conceptual shift as moving from IP allowlists to workload identity — available natively in AWS, today, with no extra machinery.
- It survives address churn completely. Auto Scaling launches three nodes with private IPs you have never seen; they can reach the database immediately, with zero rule changes. An instance is replaced and gets a new address; nothing to update.
- It gives you microsegmentation for free. The app tier reaches the database, but two instances within the app tier still cannot reach each other unless you deliberately self-reference the group. Lateral movement between peers is denied by default.
- The rule documents the architecture.
sg-app → sg-db:3306states intent.10.0.2.0/24 → 3306states a coincidence of subnet layout, and rots the moment something else lands in that subnet.
I spent a long time building automation whose entire purpose was keeping IP-based firewall rules correct as addresses changed across a fleet. Security group references make that class of problem structurally disappear. That is not a small thing.
Two limits worth knowing: references work within a VPC and across same-Region VPC peering, but not across cross-Region peering — there you fall back to CIDR blocks or an automated prefix list.
Where security groups genuinely fall short
An honest list, because this is what people are sensing when they call them limited:
| You want to | SG? | Use instead |
|---|---|---|
| Deny one IP inside an allowed range | No | NACL, ipset, WAF |
| Block a country | No | CDN or WAF geo-match |
| Rate limit | No | WAF, nginx |
| Filter on URL, header or payload | No | WAF |
| Auto-ban after repeated failures | No | fail2ban, CrowdSec |
| Hold a 50,000-entry blocklist | No — quota | ipset |
| See what was rejected | No | VPC Flow Logs |
That last row is the most operationally important and the least discussed. A security group drops traffic silently. No log, no counter, no event — nothing comparable to iptables -L -n -v packet counters or a LOG target. If you need to know that something was refused, whether for troubleshooting or detection, you must enable VPC Flow Logs and look for REJECT records.
Which is worth sitting with: an unmonitored control that has silently stopped working reports all-clear forever, and security groups are unmonitored by default.
The quotas explain the division of labour
Defaults are 60 inbound and 60 outbound rules per group, 5 groups per network interface (raisable, commonly to 16), and 1,000 rules per interface overall.
So you could not load a large blocklist into a security group even if deny rules existed. That is not an oversight — it reflects what each tool is for:
| Security group | ipset | |
|---|---|---|
| Size | Tens of rules | Millions of entries |
| Churn | Rare, deliberate, reviewed | Constant, automated |
| Semantics | Allowlist — who is authorised | Blocklist — who has misbehaved |
| Basis | Identity, via group references | Address |
| Managed by | Terraform, in review | fail2ban and scripts, in real time |
They are not competing implementations of one idea. Trying to express “block this abusive IP” in a security group is like trying to express “the app tier may reach the database” in an ipset — imaginable, and structurally wrong.
In one line
A security group is not a firewall rule list. It is a default-deny, order-free, stateful allowlist attached to an identity.
Deny rules would destroy the order-independence and composability that make it safe, and would duplicate what three other layers already do well. What it offers instead — authorising by group rather than by address — is something none of those layers can express.
Far from useless. It is your primary access control, and the others are backstops.
References
- AWS: security groups
- AWS: compare security groups and network ACLs
- Security group references across peered VPCs
- VPC quotas
- VPC Flow Logs — the only place rejections are visible