Back to blog
AWSBest PracticesCloud SecurityIdentity & AccessNetworking

VPC Peering to External Account: Auditing and Securing Cross-Account Connections

Learn why cross-account VPC peering is risky, how to audit external peering connections in AWS, and how to remediate and prevent them with SCPs and CI/CD gates.

TL;DR

This check flags VPC peering connections where the accepter or requester VPC belongs to a different AWS account. Cross-account peering is sometimes legitimate, but it opens a private network path into your environment, so every external peering relationship needs to be reviewed and locked down with tight route tables and security groups. Audit each connection, confirm the partner account is one you trust, and remove anything unexpected.

VPC peering is one of those features that quietly does its job until someone wires it to the wrong account. A peering connection creates a private, routable link between two VPCs so resources can talk to each other using internal IP addresses. That is great for splitting workloads across accounts in an organization. It is a problem when the VPC on the other end belongs to a third party, a former vendor, or an account nobody on your team recognizes.

The VPC Peering to External Account check looks at every active peering connection in your account and surfaces the ones where the peer VPC is owned by a different AWS account ID. It does not assume those connections are malicious. It assumes they deserve a second look.


What this check detects

Each VPC peering connection records two sides: the requester VPC and the accepter VPC. Both have an owner account ID attached. When those two account IDs differ, you have a cross-account peering connection.

Lensix raises this finding when a peering connection in your account references a VPC owned by an external account. You can inspect the same data yourself with the AWS CLI:

aws ec2 describe-vpc-peering-connections \
  --query 'VpcPeeringConnections[].{
    Id:VpcPeeringConnectionId,
    Status:Status.Code,
    RequesterAccount:RequesterVpcInfo.OwnerId,
    RequesterVpc:RequesterVpcInfo.VpcId,
    AccepterAccount:AccepterVpcInfo.OwnerId,
    AccepterVpc:AccepterVpcInfo.VpcId
  }' \
  --output table

If the RequesterAccount and AccepterAccount values are not identical, that row is cross-account. Compare those account IDs against the list of accounts in your AWS Organization to spot anything that lives outside your boundary.

Note: Peering does not grant access on its own. For traffic to actually flow, both sides need matching route table entries and permissive security group or NACL rules. A peering connection without routes is dormant, but it is still a standing approval you have to track.


Why it matters

A cross-account peering connection is a private door between two networks. When you control both accounts, that door is fine. When the other side belongs to someone outside your organization, you are trusting their security posture as much as your own.

Here is what makes external peering risky in practice:

  • The blast radius extends past your accounts. If the peer account is compromised, an attacker with a foothold there can reach any of your resources that have a route and an open security group on the peered CIDR range.
  • Forgotten vendor connections linger. Peering set up for a one-off integration or a vendor proof of concept often outlives the project. The vendor relationship ends, the connection stays, and nobody removes it.
  • Internal traffic bypasses internet-facing controls. Because peering uses private IPs, traffic skips your internet gateway, NAT layer, and any inspection you run at the edge. Flow that you would scrutinize coming from the internet arrives quietly over a trusted internal path.
  • It can be a data exfiltration route. A misconfigured route table can let sensitive data flow out to an external account without ever touching a public endpoint, which makes it harder to catch with internet-egress monitoring.

The realistic attack scenario is not exotic. An attacker phishes credentials for a partner account that has a peering connection to your production VPC. From there they pivot across the private link to internal databases that were only ever firewalled against the public internet, not against a trusted peer.

Warning: Cross-account peering is not transitive, which is good, but it is easy to misjudge reach. If your peer VPC is itself peered to a dozen other VPCs, none of those gain access to you. Only the directly peered VPC does. Still, validate that the directly peered CIDR range is as small as it needs to be.


How to fix it

Start by classifying each cross-account connection as expected, suspicious, or unknown. The remediation depends on which bucket it lands in.

1. Identify the owner of the peer account

Pull the full detail on the flagged connection so you have both account IDs and the CIDR ranges involved:

aws ec2 describe-vpc-peering-connections \
  --vpc-peering-connection-ids pcx-0a1b2c3d4e5f67890 \
  --query 'VpcPeeringConnections[0].{
    Status:Status.Code,
    RequesterAccount:RequesterVpcInfo.OwnerId,
    RequesterCidrs:RequesterVpcInfo.CidrBlockSet[].CidrBlock,
    AccepterAccount:AccepterVpcInfo.OwnerId,
    AccepterCidrs:AccepterVpcInfo.CidrBlockSet[].CidrBlock
  }'

Match the external account ID against your Organizations account list and your record of approved vendor integrations. If you cannot tie it to a known, documented relationship, treat it as suspicious.

2. Remove unexpected or stale connections

Danger: Deleting a peering connection immediately severs all traffic flowing across it. If a live application depends on the link, you will cause an outage. Confirm there is no production traffic on the peering route before deleting, by checking VPC Flow Logs for activity on the peered CIDR.

# Check Flow Logs for recent traffic to the peer CIDR before deleting
# Then delete once you have confirmed the connection is unused:
aws ec2 delete-vpc-peering-connection \
  --vpc-peering-connection-id pcx-0a1b2c3d4e5f67890

Deleting the connection is only half the cleanup. The routes that pointed at it remain as blackholed entries and should be removed too.

3. Clean up the dangling routes

Find route table entries that still reference the deleted peering connection and delete them:

# List routes that point at the peering connection
aws ec2 describe-route-tables \
  --query "RouteTables[?Routes[?VpcPeeringConnectionId=='pcx-0a1b2c3d4e5f67890']].{RouteTableId:RouteTableId}" \
  --output text

# Remove the specific route from the affected route table
aws ec2 delete-route \
  --route-table-id rtb-0123456789abcdef0 \
  --destination-cidr-block 10.20.0.0/16

4. If the connection is legitimate, tighten it

For peering you want to keep, do not leave it wide open. Scope it down so the external account can reach only what it genuinely needs.

  • Narrow the routes. Instead of routing the peer's entire VPC CIDR, route only the specific subnets that need to communicate.
  • Lock down security groups. Reference specific peer security groups or the smallest possible CIDR, not 0.0.0.0/0 and not the full peer VPC range when a single subnet will do.
  • Add NACL boundaries. Use network ACLs as a coarse second layer to deny everything except the ports and ranges the integration requires.

A tightened security group rule that only permits the partner's application subnet on a single port:

aws ec2 authorize-security-group-ingress \
  --group-id sg-0abc123def456789 \
  --protocol tcp \
  --port 5432 \
  --cidr 10.20.4.0/24

Tip: Tag every legitimate cross-account peering connection with the owning team, the partner account name, and a review date. A tag like peering-review=2025-Q3 makes stale connections trivial to find later with a single CLI filter.


How to prevent it from happening again

The strongest control is to stop unwanted peering connections from being accepted in the first place. A peering request is harmless until your account accepts it, so govern the acceptance.

Block acceptance with a Service Control Policy

If your organization never peers with accounts outside the org, deny the accept action for any account not in your org. This SCP allows accepting peering requests only from accounts in your Organization:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyExternalPeeringAccept",
      "Effect": "Deny",
      "Action": "ec2:AcceptVpcPeeringConnection",
      "Resource": "*",
      "Condition": {
        "StringNotEquals": {
          "aws:PrincipalOrgID": "o-exampleorgid"
        }
      }
    }
  ]
}

Note: SCPs constrain the principals in your accounts, not the requesting account. The pattern above is most effective combined with IAM conditions on the requester side. For genuine third-party integrations, consider AWS PrivateLink instead of peering, since it exposes a single service endpoint rather than a whole network range.

Gate infrastructure changes in CI/CD

If you manage networking with Terraform, fail the pipeline when a peering connection references an account ID outside an approved allowlist. A simple OPA or Conftest policy against the Terraform plan does this well:

# Run during the plan stage in CI
terraform plan -out tfplan.binary
terraform show -json tfplan.binary > tfplan.json
conftest test tfplan.json --policy policy/peering.rego

A Rego rule that rejects peer owner IDs not on the allowlist:

package main

approved_accounts := {"111122223333", "444455556666"}

deny[msg] {
  resource := input.resource_changes[_]
  resource.type == "aws_vpc_peering_connection"
  peer := resource.change.after.peer_owner_id
  not approved_accounts[peer]
  msg := sprintf("Peering to unapproved account: %s", [peer])
}

Alert on new connections in real time

Wire an EventBridge rule to the CreateVpcPeeringConnection and AcceptVpcPeeringConnection API calls so security gets notified the moment a connection appears. Pair that with continuous Lensix scanning so any cross-account peering that slips past the gates is flagged on the next run rather than discovered during an incident.


Best practices

  • Default to no external peering. Treat cross-account, cross-org peering as an exception that requires a documented justification and an owner, not a routine operation.
  • Prefer PrivateLink for vendor access. When a third party needs to reach one service, expose that service through PrivateLink instead of peering two whole networks. It is far easier to reason about.
  • Keep an inventory. Maintain a living record of every peering connection, who owns it, the partner account, and why it exists. Review it quarterly and delete anything stale.
  • Route minimally. Never route a peer's full VPC CIDR when a single subnet will satisfy the requirement. The route table is your real access boundary.
  • Enable Flow Logs on peered VPCs. Visibility into what crosses the link is the difference between catching exfiltration early and reading about it in a breach report.
  • Centralize networking. A Transit Gateway with a clear hub-and-spoke design and strict route table segmentation is easier to audit than a sprawl of point-to-point peering connections.

Cross-account peering is not inherently dangerous, but it is inherently worth questioning. Every external connection is a standing decision to trust another account's security as much as your own. Review them, scope them down, and remove the ones nobody can explain.