The documentation for setting up NAT Instances calls out that you must disable Source / Destination Checks on your NAT Instance for it to work. From https://docs.aws.amazon.com/vpc/latest/userguide/VPC_NAT_Instance.html#EIP_Disable_SrcDestCheck
Each EC2 instance performs source/destination checks by default. This
means that the instance must be the source or destination of any
traffic it sends or receives. However, a NAT instance must be able to
send and receive traffic when the source or destination is not itself.
Therefore, you must disable source/destination checks on the NAT
instance.
Conceptually, that makes sense. But what I'm curious about is how that's actually enforced. My non-expert understanding of NAT is that the packets sent by the NAT node should look just like they came from the NAT node itself, with its IP Address as the source.
So the only thing I can think of is for traffic source checking, with a scheme where AWS is looking at the TTL value, and knows the OS for each instance, and if it's not the default starting TTL, then the packet is rejected (which wouldn't be a novel approach to blocking NAT). On outbound / SNAT, it would be starting value - 1, and on inbound / DNAT, it would be one less than whatever TTL was received from the packet from the Internet gateway (which could happen to match if the sender had a higher starting TTL and the hop count was just right).
However, for traffic destination checking, how would it know that an incoming packet to the NAT instance, which will have that instance's public IP, doesn't have a (final) destination of that NAT instance? The fact that the NAT instance will attempt to forward the packet after receiving it seems secondary. Is it doing some stateful combination of TTL detection on the outbound to know that an ephemeral port on the NAT instance is for receiving replies to a NAT-ed connection? Or is the destination checking only for the NAT's instance ability to receive packets on the private subnet (where that's an easy destination IP address check)?