What Happens If EC2 Source/Destination Check Stays Enabled?

When an Amazon EC2 instance attempts to route traffic as a virtual NAT device or firewall while the Source/Destination Check remains enabled, Amazon VPC immediately drops any network packets where the instance is neither the original sender nor the final intended recipient. This built-in security mechanism ensures that standard instances cannot spoof IP addresses or intercept traffic meant for other hosts. Consequently, all transit traffic passing through the virtual appliance fails to reach its destination, resulting in complete connectivity loss for downstream resources relying on that instance for routing, network address translation, or traffic inspection.

Understanding the EC2 Source/Destination Check

Every Elastic Network Interface (ENI) attached to an Amazon EC2 instance comes with an attribute called the Source/Destination Check. By default, AWS enables this attribute on all new network interfaces to maintain strict network isolation and security within a Virtual Private Cloud (VPC).

When this setting is active, the underlying AWS Nitro or Xen hypervisor inspects the IP headers of every packet entering and leaving the instance. Specifically, the VPC SDN (Software-Defined Networking) layer verifies two conditions:

  1. For outbound traffic leaving the instance, the source IP address in the packet header must match one of the private IP addresses assigned to the instance's ENI.
  2. For inbound traffic arriving at the instance, the destination IP address in the packet header must match one of the private IP addresses assigned to the instance's ENI.

We can express the hypervisor validation rule for an instance with a set of assigned IP addresses \(I_{ENI}\) processing a packet with source address \(S_{pkt}\) and destination address \(D_{pkt}\) as follows:

\[\text{Valid Packet} \iff (S_{pkt} \in I_{ENI}) \lor (D_{pkt} \in I_{ENI})\]

If neither condition is met, the VPC fabric silently discards the packet before it can traverse the network or reach the guest operating system.

Why Virtual NATs and Firewalls Trigger Drops

Standard servers only communicate on their own behalf, meaning they only receive packets addressed to them and send packets originating from their own IP address. However, network appliances such as virtual routers, firewalls, VPN endpoints, and Network Address Translation (NAT) instances operate differently. Their primary function is to forward transit traffic between separate subnets, VPCs, or on-premises networks.

Inbound Transit Traffic Failure

Consider a scenario where an EC2 instance in a private subnet (\(10.0.1.50\)) wants to reach an external server on the internet (\(203.0.113.10\)). The private subnet's route table directs all internet-bound traffic (\(0.0.0.0/0\)) to the ENI of a custom EC2 NAT or firewall instance (\(10.0.0.10\)).

When the private instance sends a packet to \(203.0.113.10\), the VPC routing fabric attempts to deliver that packet to the firewall's network interface. Before the packet even reaches the firewall's operating system, the AWS hypervisor inspects the packet header:

  • Packet Source IP: \(10.0.1.50\)
  • Packet Destination IP: \(203.0.113.10\)
  • Firewall ENI IP: \(10.0.0.10\)

Because \(203.0.113.10 \neq 10.0.0.10\), the hypervisor sees that the packet is not addressed to the firewall instance. With the Source/Destination Check enabled, the VPC fabric drops the packet immediately. If you run a packet capture utility like tcpdump on the firewall instance, you will not even see the incoming packet arrive at the network interface.

Outbound Routing Failure (Without SNAT)

Now consider a transparent firewall or virtual router that forwards traffic between two internal subnets without performing Source Network Address Translation (SNAT). Even if a packet somehow originated from or passed through the appliance toward another internal host while preserving the original client's source IP address (\(10.0.1.50\)), the outbound check would fail.

When the appliance tries to transmit a packet where the source IP is \(10.0.1.50\) rather than its own IP (\(10.0.0.10\)), the VPC fabric blocks the transmission as suspected IP spoofing.

Symptoms of an Enabled Source/Destination Check

When administrators deploy a third-party firewall, OpenVPN server, or custom Linux NAT instance and forget to disable this check, several distinct symptoms appear during troubleshooting:

  • Complete blackholing of transit traffic: Ping requests (ICMP) and TCP connections from private subnets time out completely when routed through the appliance.
  • Direct connections still succeed: You can still SSH directly into the NAT or firewall instance, and the firewall instance itself can still access the internet or ping other hosts directly, because traffic originating from or addressed directly to the appliance satisfies the check.
  • Empty packet captures on the appliance: Running tcpdump on the virtual appliance shows zero incoming packets from the client instances for non-local destinations, leading engineers to mistakenly blame VPC route tables or Security Groups.
  • VPC Flow Logs show REJECT records: If VPC Flow Logs are enabled on the network interface, transit traffic rejected by the hypervisor or security boundaries may not appear on the target ENI as ACCEPT, making path tracing stop abruptly at the source.

How to Disable the Source/Destination Check

To allow an EC2 instance to function properly as a NAT device, router, or firewall, you must disable the Source/Destination Check on the specific Elastic Network Interface handling transit traffic. Notice that this setting is applied per ENI, not globally at the instance level, though single-ENI instances expose it directly in the instance actions menu.

Using the AWS Management Console

  1. Open the Amazon EC2 Console and navigate to Instances.
  2. Select the NAT or firewall instance from the list.
  3. Click Actions, navigate to Networking, and select Change source/destination check.
  4. In the dialog box, uncheck the Enable box (or select Stop depending on the console version) to disable the check.
  5. Click Save.

If your virtual firewall has multiple network interfaces (for example, eth0 for untrusted public traffic and eth1 for trusted private traffic), you must navigate to the Network Interfaces section in the EC2 console and disable the check on each interface that forwards transit packets.

Using the AWS CLI

You can disable the check programmatically using the modify-instance-attribute command for the primary interface, or modify-network-interface-attribute for specific ENIs:

aws ec2 modify-instance-attribute \
    --instance-id i-0123456789abcdef0 \
    --no-source-dest-check

To target a specific Elastic Network Interface directly:

aws ec2 modify-network-interface-attribute \
    --network-interface-id eni-0123456789abcdef0 \
    --no-source-dest-check

Using Terraform

When provisioning virtual network appliances via Infrastructure as Code, always set the source_dest_check argument to false:

resource "aws_instance" "nat_firewall" {
  ami               = "ami-0c55b159cbfafe1f0"
  instance_type     = "t3.micro"
  subnet_id         = aws_subnet.public.id
  source_dest_check = false
}

Once the Source/Destination Check is disabled, the VPC fabric stops filtering transit headers on that ENI, allowing packets with foreign source and destination IP addresses to reach the instance's operating system for IP forwarding, NAT masquerading, or deep packet inspection.