TCP RST received immediately after SYN-ACK: connection reset by firewall
ID: networking/firewall-rst-after-synack
Version Compatibility
| Version | Status | Introduced | Deprecated | Notes |
|---|---|---|---|---|
| any | active | — | — | — |
Root Cause
A TCP RST packet is injected by a middlebox (firewall, IDS/IPS, DPI device, or cloud security group) immediately after the three-way handshake SYN-ACK, before any application data is exchanged. This indicates a stateful firewall or deep packet inspection device is actively terminating the connection. Unlike a normal connection refused (RST from the destination host), this RST often has anomalous TCP characteristics (wrong sequence number, TTL inconsistent with the server) revealing it originates from an intermediate device. Resolution depends on identifying and reconfiguring the offending middlebox.
genericWorkarounds
-
70% success Identify the offending middlebox using TTL analysis and traceroute, then update firewall rules
1. Capture the RST packet and compare its TTL with the server's normal packets: tcpdump -nn -i eth0 'tcp[tcpflags] & (tcp-rst) != 0' -v host <server-ip> 2. Note the TTL of the RST vs normal SYN-ACK -- a TTL difference indicates an intermediate device 3. Trace the path to locate the middlebox hop: traceroute -T -p <port> <server-ip> mtr --tcp --port <port> <server-ip> 4. Check cloud security groups / network ACLs: # AWS: aws ec2 describe-security-groups --group-ids sg-xxxxx aws ec2 describe-network-acls --filters Name=association.subnet-id,Values=subnet-xxxxx 5. Update the firewall rule to allow the traffic: # iptables example: iptables -I FORWARD -s <source-ip> -d <dest-ip> -p tcp --dport <port> -j ACCEPT
Sources: https://www.rfc-editor.org/rfc/rfc793 https://docs.aws.amazon.com/vpc/latest/userguide/vpc-network-acls.html
-
65% success Use a TLS tunnel or VPN to encapsulate the traffic and bypass DPI-based RST injection
If the RST is injected by a DPI device based on protocol detection, wrapping the traffic in TLS can prevent inspection: 1. SSH tunnel: ssh -L <local-port>:<remote-host>:<remote-port> bastion-host 2. WireGuard VPN (peer-to-peer): # On server: [Interface] PrivateKey = <server-private-key> Address = 10.0.0.1/24 ListenPort = 51820 [Peer] PublicKey = <client-public-key> AllowedIPs = 10.0.0.2/32 # On client: [Interface] PrivateKey = <client-private-key> Address = 10.0.0.2/24 [Peer] PublicKey = <server-public-key> Endpoint = <server-ip>:51820 AllowedIPs = 10.0.0.1/32 3. stunnel for wrapping arbitrary TCP in TLS: stunnel3 -c -d localhost:<local-port> -r <remote-host>:<remote-port>
Sources: https://www.wireguard.com/quickstart/ https://www.stunnel.org/docs.html
Dead Ends
Common approaches that don't work:
-
Increasing TCP connection timeout or adding retry logic in the application
95% fail
The RST is injected deterministically by the firewall based on policy rules, not due to transient network conditions. Retrying the connection will produce the same RST every time because the firewall policy has not changed. Unlike a timeout or transient packet loss, a firewall RST is a deliberate, policy-driven rejection that persists until the rule is modified.
-
Changing the application port to bypass the firewall rule
70% fail
Modern stateful firewalls and next-gen firewalls (NGFW) inspect traffic at layers 4-7 and can identify protocols regardless of port. Changing the port may temporarily bypass simple port-based rules but sophisticated firewalls use deep packet inspection (DPI) to detect the actual protocol. Additionally, non-standard ports often trigger stricter inspection policies. In cloud environments (AWS Security Groups, Azure NSG), egress rules frequently apply to all ports.