IP allowlist playbook

IP allowlist for AWS

Security groups and the aws:SourceIp condition for your team’s IPs, also called an AWS IP whitelist.

AWS gives you two allowlists, and a small team usually wants both: a security group that drops everyone else before they reach a bastion or admin panel, and an IAM condition that refuses your people’s credentials when they are used from anywhere but your team’s IPs.

Vendor facts checked on .

What this control covers

  • Network access to EC2 instances, load balancers and anything else with a security group.
  • API and console calls made with the IAM roles you attach the policy to, including stolen access keys used from elsewhere.

What it doesn’t cover

  • Requests through VPC endpoints, which carry no aws:SourceIp at all.
  • Roles that AWS services assume: never attach the IP deny to them.
  • Databases: keep them reachable only from the bastion or the app subnet, never from team IPs directly.

The plan you need

Every AWS account. A security group takes 60 inbound rules by default, counted separately for IPv4 and IPv6; a customer-managed prefix list holds up to 1,000 entries. AWS: Amazon VPC quotas.

Why an allowlist needs fixed IPs

An IP allowlist is only as good as the addresses on it. Home and mobile IPs change without notice, so a list of your team’s own addresses locks people out every few weeks, and someone ends up adding a broad range or switching the control off.

A shared VPN looks like a fix, but its exit IP belongs to every customer of that VPN server. Allowlisting it lets them in too.

What works is a small set of addresses that belong to your team alone and don’t change: your team’s traffic to the service leaves from them, and nothing else does.

Set it up

  1. Keep the team IPs in one prefix list

    A customer-managed prefix list lets every security group point at the same list, so a change is one edit. Set its maximum entries small: AWS counts the maximum, not the entries in use, against each group’s rule quota. A list holds IPv4 or IPv6, never both, and lives in one Region.

    prefix-list.tf
    resource "aws_ec2_managed_prefix_list" "team" {
      name           = "team-egress"
      address_family = "IPv4"
      max_entries    = 4
    
      entry {
        cidr        = "203.0.113.10/32"
        description = "gate-1"
      }
      entry {
        cidr        = "203.0.113.11/32"
        description = "gate-2"
      }
    }
  2. Allow it on the bastion’s security group

    One rule per port, pointing at the list instead of copied addresses. Remove any old rule that allows 0.0.0.0/0 on the same port.

    bastion.tf
    resource "aws_vpc_security_group_ingress_rule" "bastion_ssh" {
      security_group_id = aws_security_group.bastion.id
      prefix_list_id    = aws_ec2_managed_prefix_list.team.id
      ip_protocol       = "tcp"
      from_port         = 22
      to_port           = 22
    }
  3. Deny your human roles from anywhere else

    This statement allows nothing by itself; it refuses every action from an address that isn’t yours. aws:ViaAWSService set to false keeps it from blocking calls AWS services make on your behalf. Attach it to the roles people sign in to, never to roles that services or CI assume.

    deny-outside-team-ips.json
    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Sid": "DenyOutsideTeamIps",
          "Effect": "Deny",
          "Action": "*",
          "Resource": "*",
          "Condition": {
            "NotIpAddress": { "aws:SourceIp": ["203.0.113.10/32", "203.0.113.11/32"] },
            "Bool": { "aws:ViaAWSService": "false" }
          }
        }
      ]
    }
  4. Mind the VPC-endpoint trap

    AWS says aws:SourceIp is in the request “except when the requester uses a VPC endpoint”. With the key missing, NotIpAddress matches and the deny fires, so a role used from inside a VPC through an endpoint is refused. Keep the policy off those roles, or add a statement keyed on aws:SourceVpce for them. The same applies to S3 bucket policies: behind a gateway endpoint S3 sees private addresses, and AWS says to use aws:VpcSourceIp there instead.

  5. Shield service principals in resource policies

    If you put the same deny in a bucket or key policy, AWS warns that service principals can be blocked by mistake; add "aws:PrincipalIsAWSService": "false" to the condition.

Check that it works

  1. SSH from your fixed IPs, and from elsewhere

    Through your team’s IPs, ssh to the bastion connects. From any other network the connection times out: the security group drops it silently.

  2. Call AWS with the restricted role

    With credentials for a role that has the deny, aws sts get-caller-identity works from your fixed IPs. From another network, a call such as aws s3 ls fails with AccessDenied and mentions an explicit deny.

  3. Read CloudTrail

    CloudTrail records the sourceIPAddress of each call. Look at a day of your team’s calls and confirm they all come from your listed addresses before you widen the policy to more roles.

Where the fixed IPs come from

Teicho sends your team’s traffic to the service through a pair of Gates dedicated to your team, each with a fixed IP. Allowlist both, and the service sees only your people and machines, each signed in as themselves, while everyday browsing still goes straight to the internet. Teicho is in early access.

FAQ

Questions

Is this the same as an AWS IP whitelist?

Yes. Security group inbound rules and the `aws:SourceIp` condition are the two ways AWS whitelists IP addresses: one for the network, one for credentials.

Do these work with IPv6?

`aws:SourceIp` accepts IPv6 ranges, and security groups count IPv6 rules separately from IPv4. A prefix list holds one family, so use a second list for IPv6.

Will the IAM deny lock people out of the console?

Anyone using a restricted role from another address is refused, in the console as on the CLI. That is the point, which is why you keep a break-glass role without the policy.

What about CI and Lambda?

Don’t attach the deny to roles that CI runners or AWS services assume; hosted runners change addresses, and services call from inside AWS. Protect those roles with OIDC trust conditions and least privilege instead.

Why not list each person’s home IP?

They change, and every change is a policy edit that can lock someone out. A short list of fixed team IPs doesn’t move when people do.

Sources

Plans, limits and steps on this page were checked against the vendor’s documentation on 2 November 2026. Vendors change them; if something here is out of date, tell us at hello@teicho.io.

Put a wall around what matters.

Teicho is in early access. Tell us what you want to protect and we’ll be in touch.

Request early access