• aws
  • security
  • vpc
  • networking

The security group mistake almost every AWS diagram gets wrong

Your diagram draws one clean arrow from the app to the database. The security group behind it usually allows the entire VPC. Here's the gap, and how to close it.

Your architecture diagram draws one arrow: app tier to database. Clean, specific, one line between two boxes. Then you look at the security group actually protecting that database, and the inbound rule says allow 5432 from 10.0.0.0/16 — the whole VPC.

That’s the mistake, and it’s almost universal: the diagram implies a precise, scoped connection, but the rule behind it allows far more than the arrow. One line on the canvas, a hundred instances allowed in. Here’s why it happens and how to make the rule match the picture.

The arrow says one thing, the rule says another

A security-group rule can name its source two ways: a CIDR range, or another security group. Those aren’t just two syntaxes for the same thing — they permit completely different sets of traffic.

allow 5432 from 10.0.0.0/16 allows anything in that address range to reach the database on 5432 — the app tier, sure, but also every bastion host, batch worker, build agent, and forgotten test instance that happens to live in the VPC. The diagram drew app-to-database. The rule drew everything-to-database.

A CIDR-sourced rule for the whole VPC lets every instance in it reach the database — not just the app tier the diagram shows.

Why the CIDR rule feels fine — and isn’t

It feels fine because it works. The app connects, the feature ships, nothing errors. The cost is invisible until something goes wrong, and then it’s two problems at once.

The first is blast radius. If any instance in that CIDR is compromised — a bastion with a weak key, a worker running a vulnerable dependency — it can now talk straight to your database, because the rule never distinguished it from the app tier. Least privilege is the whole game in a breach, and a VPC-wide CIDR rule throws it away.

The second is that CIDR rules are brittle. Tie a rule to IP ranges and you’re back to maintaining it by hand every time infrastructure moves — new subnet, re-addressed tier, migrated instance. Miss an update and you either break a real connection or leave a stale hole open.

The fix: reference the security group, not the range

Name the source security group instead of a CIDR. A rule like allow 5432 from sg-app means exactly one thing:

allow traffic from any network interface that has this security group attached

It doesn’t matter what IP that interface has, or whether it existed when you wrote the rule. Only instances actually in the app tier’s security group can reach the database — which is what your diagram said in the first place. Referencing security groups is how AWS recommends you apply least privilege, and it’s the foundation of real tiered architecture: each tier’s group references only the tier that’s supposed to talk to it.

It also fixes the brittleness for free. Auto-scaling launches a new app instance, it comes up in the app security group, and it’s allowed — no rule change. Terminate one and its access goes with it. The rule tracks membership, not addresses.

Referencing the source security group scopes access to the app tier itself — new instances are covered automatically, everything else is denied.

When a CIDR is the right answer

To be fair, CIDR sources aren’t always wrong — they’re wrong inside the VPC, where a security group reference is almost always the more precise tool. A CIDR is the correct choice when the source genuinely isn’t an AWS resource you can reference by group:

  • An on-premises network reaching in over a VPN or Direct Connect — that’s a real IP range, so a rule scoped to it is right.
  • A public-facing load balancer that has to accept 0.0.0.0/0 on 443, because the whole internet is legitimately the source.

The rule of thumb: if the source is something running in your AWS account, reference its security group. If it’s the outside world, a CIDR is honest.

Make the rule match the diagram

The deeper point is that a diagram should tell the truth about what talks to what. An arrow from the app to the database is a claim, and a VPC-wide CIDR rule quietly makes that claim false — the real access is far wider than the picture. Design Beaver is built on the idea that the diagram should mean something: it validates as you draw, checking each connection the moment you make it. The discipline that pairs with it is on you — keep your security-group rules as specific as the arrows, so the architecture you deployed is the one you designed. If you want a canvas that takes your connections seriously, open Design Beaver — it’s live in beta, free, no account required.

Milin Paul

@milinpaul

Lead Software Engineer at EverestEngineering. I write about practical AI systems, engineering architecture, and what actually works in production.

Try Design Beaver on your own architecture

It’s live in beta — free, in your browser, no account required.

Open the app →

Prefer email? Get new features in your inbox:

← Back to all posts