FortiGate: Missing Policy on Virtual Server Causes Silent Drop
Traffic was routing correctly, policies looked clean, and the flow debug showed no denial - yet the connection stalled.
Client connecting from a remote site over IPsec to a TCP service. Routing correct, tunnel up, firewall policy allowing traffic to the destination subnet. Connection stalls.
Debugging
diagnose debug flow shows packets arriving on the correct interface, session being matched, route resolving. Some ipsec_spoofed4 hits distract the investigation for a while — turns out those were a dead end. Packet capture shows traffic arriving and apparently leaving. No drops, no RST, no deny log entries anywhere.
TLDR - root cause
The destination IP belongs to a Virtual Server object. The backend servers happen to sit in the same subnet as the VS IP.
A policy existed permitting traffic to that subnet via a regular address object — which covers the VS IP. That is not sufficient. FortiGate requires the policy destination to reference the VS object explicitly. A subnet address object covering the same IP does not trigger the VS processing path. The traffic gets silently mishandled with no log entry indicating why.
Add an explicit policy referencing the virtual server
Add a policy with the VS object as destination:
config firewall policy
edit <id>
set srcintf "tunnel-interface"
set dstintf "internal-interface"
set srcaddr "<src>"
set dstaddr "<VirtualServer-Object>"
set action accept
set service "<svc>"
set schedule "always"
next
endexplicit policy
Clear stale sessions
diagnose sys session filter dstaddr 10.y.y.y
diagnose sys session clearclear sessions
Technical details - Packet flow
As reference, that's how the flow trace looks like:
func=print_pkt_detail msg="vd-ROOT:0 received a packet(proto=6, 10.a.a.a:49084->10.b.b.b:12345)
tun_id=10.t.t.t from TUNNEL-IF. flag [.], seq 3379842855, ack 211734161, win 402"
func=resolve_ip_tuple_fast msg="Find an existing session, id-d82fffc8, original direction"
func=npu_handle_session44 msg="Trying to offloading session from TUNNEL-IF to LAN-IF,
skb.npu_flag=00000400 ses.state=04010200 ses.npu_state=0x00000d08"
func=fw_forward_dirty_handler msg="state=04010200, state2=00000000, npu_state=00000d08"paket flow
As you can see - the packet is received, session is established and offloaded. Should be fine unless the required policy is missing.
Why are there sessions?
Do get into more details, here's an explanation.
- The VIP of the destination (the VS's IP) is matching on another policy
- The VS would map this to a set of backend endpoints
- FYI: Sessions for denied connections are not enabled
Having a policy serves multiple use cases:
- Authorize the connection / traffic
This one has been matching on another policy - clients can go via VS or use pooling directly client-side for the affected service - Wire the virtual server into the traffic flow
This means that the authorization of traffic is fine, it has been caught by another policy. The missing piece is the association with the virtual server which results in the seemingly silent packet drop.
Fairly simple afterwards - during diag certainly nasty because you don't check policies if you see the session established :-)