Photo by NASA Hubble Space Telescope / Unsplash

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.

Daniel Nachtrub
Daniel Nachtrub

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
end

explicit policy

Clear stale sessions

diagnose sys session filter dstaddr 10.y.y.y
diagnose sys session clear

clear 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 :-)

NetworkLinux

Daniel Nachtrub Twitter

Kind of likes computers. Linux foundation certified: LFCS / CKA / CKAD / CKS. Microsoft certified: Cybersecurity Architect Expert & Azure Solutions Architect Expert.