A security review adds a custom network ACL to the private subnets hosting a service: inbound allows 8080 from the load balancer subnets, outbound allows 5432 to the database subnets, everything else denied. The security groups did not change. Now every database query times out. Explain precisely why, and what the correct ACL looks like.
A network ACL is stateless. It evaluates inbound and outbound traffic independently, with no memory that a connection was started. The service's connection to PostgreSQL leaves on destination port 5432, which the outbound rule allows, but the database's reply comes back to the service's ephemeral source port (a high port the OS picked for that connection), and no inbound rule admits it, so the ACL drops it and the client times out. A security group would have admitted the reply automatically because it is stateful. The same bug exists in the other direction: the service's replies to the load balancer leave on ephemeral ports that the outbound rules do not allow. The fix is explicit return rules for the ephemeral range in both directions, scoped to the peer subnets.