# FortiGate policy zone then NAT then session

Source: https://ai.techclick.in/blog_fortinet_fortigate_zone_policy_nat_flow
Markdown: https://ai.techclick.in/blog_fortinet_fortigate_zone_policy_nat_flow.md
Publisher: Techclick Infosec Pvt Ltd

FortiGate builds a session only after zone/interface, address and service match an accept policy. SNAT and DNAT are separate from allow. Proof is diagnose sys session filter / list. Lab LAN 10.20.20.0/24, WAN 192.0.2.0/24, FGT 10.20.20.1.

Lessons  ·  FortiGate series  · Zones, policy, NAT

   This page vs first-match and VDOM

   This lesson is  how FortiOS turns a packet into a session : zone membership, policy lookup, then NAT, then the session table. Rule order and implicit deny depth live on first-match. Tenant isolation is VDOM.

    Policy first-match  ·  VDOMs  ·  Command center

   Hero · host hits a zone, not a port nickname

   Mood, not a wiring diagram. Exact path is in the SVG: 10.20.20.10 ingresses zone LAN, policy LAN-to-WAN accepts, SNAT to 192.0.2.1, session installed. Direct to 10.20.20.80 never hits WAN policy.

   Quick answer

   FortiGate policy matches  incoming interface or zone ,  destination zone , source/dest  address , and  service  (plus schedule/user if set). First match wins; nothing matching is implicit deny.  SNAT  ( set nat enable  / IP pool / Central SNAT) and  DNAT  (VIP) rewrite addresses — they do not grant the permit. An entry is placed in the session table for each session that passes a security policy. Prove with  diagnose sys session filter  then  diagnose sys session list . Lab: LAN  10.20.20.0/24 , FGT  10.20.20.1 , WAN  192.0.2.0/24 .

## Why a route is not a session

 The day-one ticket is always the same: “Routing is fine, so the firewall is broken.” Wrong. RIB lookup only names the  egress interface . FortiOS still needs a firewall policy whose  srcintf  /  dstintf  (interface or zone), addresses and service cover that 5-tuple. No match → implicit deny. No accept → no session to list.

 Three silent-zero states look identical from the host (timeout):

- Default route exists; policy is still LAN→LAN or missing dest zone WAN.

- Policy is accept, but srcintf is port2 while the packet arrived on a VLAN that is not in that object — zone membership is the real incoming interface.

- VIP exists; no WAN→LAN policy with that VIP as dstaddr . DNAT without accept is still a drop.

   Ping to 10.20.20.1 proves L2/L3 to the FortiGate, not policy

   Traffic  to  the FortiGate uses local-in policy. Traffic  through  it uses firewall policy. Do not debug SNAT until  diagnose debug flow  shows a policy id other than 0 and  diagnose sys session list  shows the flow.

## Zone, policy, NAT — three objects

 A  zone  is an interface object: one or more physical, VLAN, LAG or tunnel interfaces grouped so a policy can say LAN instead of listing every port. Members of the same zone do not need a policy to talk to each other unless you enable intra-zone block. Policies still match on the zone name as  srcintf  /  dstintf .

 A  firewall policy  is the allow/deny. Required match fields: incoming interface(s), outgoing interface(s), source address(es), destination address(es), service(s), schedule. Optional: user, internet-service, security profiles. Action is  accept  or  deny . NAT is a  separate  knob on that policy, or a separate VIP / Central NAT table.

   Path · zone, then policy, then NAT, then session

   Feel of the order. Exact FortiOS sequence — DNAT before policy, SNAT after accept — is in the SVG below. Do not read the NAT panel as “NAT is the firewall.”

#### Zone

  config system zone . GUI: Network › Interfaces › Create New › Zone. Lab: LAN = internal, WAN = wan1. Policy talks in zone names.

#### Address / service

  LAN_NET  =  10.20.20.0/24 . Service is ALL, HTTPS, or a custom TCP port. Objects, not raw CIDR typed in the policy every time.

#### Allow

  set action accept . First matching policy wins. Implicit deny is policy 0 —  debug flow  says denied by policy 0 when nothing hit.

#### NAT

 SNAT:  set nat enable  (outgoing interface IP or IP pool). DNAT: VIP as destination. Central NAT if  central-nat  is on — then the policy NAT checkbox is the wrong place.

   Say this out loud

   The packet hits a zone. Policy matches that zone pair plus address and service. Accept builds the session. SNAT or DNAT only rewrites. No accept, no session.

## How a session is built

 Existing session first. If FortiOS already has the 5-tuple, later packets skip policy lookup and follow the installed NAT hooks. New flows walk the full path. DNAT (VIP / Central DNAT) rewrites destination  before  routing and policy so the RIB and the dest-zone match the real server. SNAT runs  after  accept.

   Flow 1 · lab packet 10.20.20.10 → 203.0.113.80:443

       Ingress LAN, session miss, route WAN, policy accept, SNAT, session install

- 1 Ingress zone LAN · port2 2 Session? hit → use hooks 3 DNAT / VIP outbound: noop 4 Route egress zone WAN 5 Policy LAN → WAN · HTTPS 6 Accept → SNAT → 7 Session install hook=post dir=org act=snat 10.20.20.10:54321→203.0.113.80:443(192.0.2.1:54321) policy_id=1 · proto=6 proto_state=01 then 11 · nsrc=192.0.2.1 Miss at step 5: implicit deny (policy 0). No SNAT. No session to list. Fix the zone pair / address / service. Do not add a second default route. Reply packets reverse the hooks. Clear the session after a policy edit or you keep the old NAT. Source: FortiOS Administration Guide — firewall policy parameters; Using a session table. Read left → right, then the gold bar. Route names the dest zone; policy still has to accept that pair. SNAT is after accept. Object Lab value If missing LAN zone / IP zone LAN · 10.20.20.1/24 Host cannot ARP the gateway. Local ping fails — not a policy problem. WAN zone / IP zone WAN · 192.0.2.1/24 No egress. Route lookup fails before policy. Address LAN_NET = 10.20.20.0/24 Policy matches nothing; debug flow policy 0. Outbound policy LAN-to-WAN accept + nat enable Route yes, session no. Return from 203.0.113.80 has no state. VIP web_pub 192.0.2.80 → 10.20.20.80:443 Published IP answers nothing. VIP without a WAN→LAN policy is not an open port. Session proof diagnose sys session list GUI hit counter can lie on cached sessions. The table is the wire. ## Policy NAT vs VIP vs Central NAT Pick the NAT engine the VDOM is actually running. Mixing them is the usual “NAT is on but nsrc is wrong” ticket. Flow 2 · allow is not NAT Accept versus SNAT versus DNAT as three separate decisions Allow action = accept srcintf LAN · dstintf WAN addr + service match SNAT nat enable / IP pool or Central SNAT table after accept · nsrc=192.0.2.1 DNAT firewall vip web_pub 192.0.2.80 → 10.20.20.80 before policy · dstaddr=VIP You can accept without NAT (east-west, or inbound VIP with nat disable). You cannot SNAT a deny. Central NAT on: policy NAT checkbox is ignored — edit Policy & Objects › Central SNAT. VIP policies have priority over ordinary dest-all policies. Deny in front of a VIP needs match-vip enable. Source: FortiOS firewall policy; Destination NAT / Configuring VIPs; Central SNAT. Three columns, three tickets. Do not toggle NAT to “fix” a zone mismatch. Need Use Skip LAN hosts share WAN IP Policy NAT: set nat enable on LAN-to-WAN (outgoing interface address) VIP. VIP is dest rewrite, not hide-NAT. Many-to-one with a dedicated public IP pool + set ippool enable / set poolname Outgoing-interface SNAT if you must pin the source. Publish 192.0.2.80:443 to 10.20.20.80 VIP + WAN→LAN accept, dstaddr = VIP, usually nat disable SNAT on that policy unless the server’s default gw is not the FGT. NAT rules independent of every policy Central SNAT / Central DNAT ( config system settings set central-nat enable ) Leaving policy NAT enabled and wondering why it does nothing. ## Runbook Side A / B / C Side A is objects. Side B is the policy and NAT. Side C is the session table. Do not start at C. ### Side A — zones and addresses #### Zone LAN and WAN Network › Interfaces › Create New › Zone . Lab: LAN member = internal (or port2); WAN member = wan1. CLI config system zone . Address on the member interface, not on the zone: LAN 10.20.20.1/24 , WAN 192.0.2.1/24 . Source: FortiOS Administration Guide — Zone.

- #### Address object Policy & Objects › Addresses — LAN_NET subnet 10.20.20.0/24 . Do not use all as source on the first outbound policy if you want a readable session later.

   CLI — Side A
   config system zone
    edit "LAN"
        set interface "internal"
    next
    edit "WAN"
        set interface "wan1"
    next
end
config firewall address
    edit "LAN_NET"
        set subnet 10.20.20.0 255.255.255.0
    next
end
config router static
    edit 1
        set gateway 192.0.2.254
        set device "wan1"
    next
end

### Side B — policy then NAT

     https://10.20.20.1/ · Policy &amp; Objects › Firewall Policy › Create New

     Training mock · not live

       Policy &amp; Objects › Firewall Policy › Create New

### LAN-to-WAN

          Incoming interface  LAN

          Outgoing interface  WAN

          Source  LAN_NET (10.20.20.0/24)

          Destination  all

          Service  ALL

          Action / NAT  ACCEPT · NAT enable · outgoing IP

         Cancel
         OK

    Source:  FortiOS Administration Guide — Firewall policy parameters (incoming/outgoing interface, address, service, schedule). NAT enable is the SNAT checkbox, not the action. Log all on the first lab policy so Forward Traffic has a row when the session exists.

- #### Outbound accept + SNAT Policy id 1 LAN-to-WAN : srcintf LAN , dstintf WAN , srcaddr LAN_NET , dstaddr all , service ALL , action accept , nat enable , logtraffic all . This is hide-NAT to 192.0.2.1 .

- #### Inbound VIP (separate from allow) Policy & Objects › Virtual IPs — web_pub , interface WAN, external 192.0.2.80 map to 10.20.20.80 , port 443→443. Then policy id 2 WAN→LAN, dstaddr web_pub , service HTTPS, accept, NAT off unless the server’s gateway is not the FGT.

     https://10.20.20.1/ · Policy &amp; Objects › Virtual IPs › Create New

     Training mock · not live

       Policy &amp; Objects › Virtual IPs › Create New

### web_pub

          Interface  WAN

          External IP  192.0.2.80

          Map to IPv4  10.20.20.80

          Port forward  443 TCP → 443

         Cancel
         OK

    Source:  FortiOS — Configuring VIPs. After OK you still need the WAN→LAN policy with destination  web_pub . VIP object ≠ open port.

   CLI — Side B
   config firewall policy
    edit 1
        set name "LAN-to-WAN"
        set srcintf "LAN"
        set dstintf "WAN"
        set srcaddr "LAN_NET"
        set dstaddr "all"
        set action accept
        set schedule "always"
        set service "ALL"
        set nat enable
        set logtraffic all
    next
end
config firewall vip
    edit "web_pub"
        set extintf "WAN"
        set extip 192.0.2.80
        set mappedip "10.20.20.80"
        set portforward enable
        set protocol tcp
        set extport 443
        set mappedport 443
    next
end
config firewall policy
    edit 2
        set name "WAN-to-WEB"
        set srcintf "WAN"
        set dstintf "LAN"
        set srcaddr "all"
        set dstaddr "web_pub"
        set action accept
        set schedule "always"
        set service "HTTPS"
        set nat disable
        set logtraffic all
    next
end

### Side C — prove the session

- #### Filter, then list From 10.20.20.10 open HTTPS to 203.0.113.80. On the FGT: diagnose sys session filter clear , diagnose sys session filter src 10.20.20.10 , diagnose sys session filter dport 443 , diagnose sys session list . Source: FortiOS — Using a session table.

- #### Read the hooks You want policy_id=1 , hook=post dir=org act=snat with (192.0.2.1:…) , and bytes on org and reply. Empty list after a live attempt = no accept. act=noop on org = accept without SNAT.

- #### Clear after an edit diagnose sys session filter src 10.20.20.10 then diagnose sys session clear so the next packet re-walks policy. Old sessions keep old NAT.

   CLI — session proof (outbound SNAT)
   diagnose sys session filter clear
diagnose sys session filter src 10.20.20.10
diagnose sys session filter dport 443
diagnose sys session list
# session info: proto=6 proto_state=11 …
# hook=post dir=org act=snat 10.20.20.10:54321-&gt;203.0.113.80:443(192.0.2.1:54321)
# hook=pre dir=reply act=dnat 203.0.113.80:443-&gt;192.0.2.1:54321(10.20.20.10:54321)
# policy_id=1  vd=0
# nsrc / nport match the SNAT you intended

diagnose firewall iprope lookup 10.20.20.10 54321 203.0.113.80 443 6 internal
# which policy would match — use when list is empty (device = ingress member, proto 6 = TCP)

   Green proof

   Session list shows  policy_id=1 , SNAT to  192.0.2.1 , reply packets increment. Forward Traffic log has the same policy name. That is the close — not “the route is there.”

## One flow after go-live

 Host 10.20.20.10 SYN to 203.0.113.80:443. Ingress LAN. Session miss. No VIP. RIB: default via 192.0.2.254, egress WAN. Policy 1 matches LAN/WAN/LAN_NET/all/ALL. Accept. SNAT source to 192.0.2.1. Session installed. Reply hits WAN, reverse DNAT to 10.20.20.10, egress LAN. Second packet of the same 5-tuple skips policy and uses the hooks.

 Inbound: SYN to 192.0.2.80:443. VIP DNAT to 10.20.20.80:443. Route to LAN. Policy 2 matches WAN/LAN/all/web_pub/HTTPS. Accept, no SNAT. Session  act=dnat  on org. Hairpin from 10.20.20.10 to 192.0.2.80 needs extra policy + often SNAT so the server replies via the FGT — that is a different ticket.

   Proof · session built is a table row, not a dashboard tile

   Ops feel. The actual evidence is  diagnose sys session list  fields: policy_id, hook act, nsrc, proto_state. Artwork checkmarks are not FortiOS.

   debug flow — when the session table is empty
   diagnose debug reset
diagnose debug flow filter addr 10.20.20.10
diagnose debug flow filter port 443
diagnose debug flow show function-name enable
diagnose debug enable
diagnose debug flow trace start 20
# look for: find a route / allowed by policy 1 / SNAT 10.20.20.10 -&gt; 192.0.2.1
# or: denied by policy 0  → zone/address/service miss, not a NAT bug
diagnose debug disable

## Traps + proof

       Symptom  Likely cause  Proof

        Route exists, no session  No accept for that zone pair / service. Implicit deny.  debug flow policy 0.  session list  empty after filter.
        Policy on port2, packet on vlan20  Incoming interface is the member, not the alias in your head. Zone never included vlan20.   diagnose netlink interface list  vs  srcintf  on the policy.
        NAT checkbox on, nsrc still RFC1918  Central NAT enabled, or dest is not leaving WAN, or policy is deny.   get system settings | grep central-nat . Session  act=noop .
        VIP created, port still closed  No WAN→LAN policy with dstaddr = VIP. VIP is DNAT only.  sniffer on WAN sees SYN, no session, no LAN SYN.
        Changed NAT, still old public IP  Existing session kept old hooks.  Filter +  diagnose sys session clear , re-test.
        LAN host to own VIP fails  Hairpin: dest after DNAT is LAN, src is LAN — missing LAN→LAN VIP policy / SNAT.  debug flow dest zone LAN not WAN.
        Deny in front of VIP does not block  VIP policies take priority unless  set match-vip enable  on the deny.  FortiOS firewall policy — match-vip. Session still policy_id of the VIP rule.

   Do not set srcintf any to “make it work”

   Best practice: policies that allow traffic apply to a specific interface or zone, not any. Any hides the dest-zone mistake until the next bypass. Fix the zone membership.

   Pilot checklist

- Host pings 10.20.20.1 (local-in). Separate from through-traffic.

- Policy list shows LAN-to-WAN above implicit deny; NAT enable; log all.

- From 10.20.20.10: HTTPS to 203.0.113.80. session filter src + list → policy_id=1, act=snat to 192.0.2.1, reply bytes > 0.

- VIP: external 443 to 192.0.2.80, session act=dnat to 10.20.20.80, policy_id=2.

- After any NAT edit: session clear, then the same test. Hit counters on old sessions are not a new proof.

## Knowledge check

   Six judgment items. Submit once. Reasons point back at the section to re-read.

       Q1
       Default route is installed. Host 10.20.20.10 times out to 203.0.113.80. What must match before FortiOS will even consider SNAT?

           Any route to 203.0.113.80, because FortiGate is a router first.
           An accept policy matching incoming zone, dest zone, address and service.
           An IP pool object, even if action is deny.
           A VIP for 10.20.20.10 so the host can leave LAN.

       Correct:  b . Route only names egress. Policy match is srcintf/zone, dstintf/zone, address, service. SNAT runs after accept. Re-read Why a route is not a session and Flow 1.

       Q2
       Which statement treats SNAT/DNAT as separate from allow?

           set action accept is the permit; set nat enable or a VIP only rewrites. A VIP still needs a WAN-to-LAN accept.
           Enabling NAT on a deny policy is how you publish 192.0.2.80.
           Creating the VIP object opens 443; the policy list is documentation.
           Central NAT is just a GUI view of the same policy checkbox.

       Correct:  a . Allow and NAT are different objects. VIP without policy is not an open port. Central NAT replaces policy NAT when enabled. Re-read How to choose and Side B.

       Q3
       What is the proof that LAN-to-WAN actually built a session?

           The WAN interface is up/up and the default route is in the RIB.
           A screenshot of the policy with NAT enabled, no traffic generated.
           diagnose sys session filter src 10.20.20.10 then list, showing policy_id and hook act=snat to 192.0.2.1.
           Ping success to 10.20.20.1 from the host.

       Correct:  c . Session table is the wire. Ping to the FGT is local-in. Re-read Side C and Traps.

       Q4
       Inbound HTTPS to VIP 192.0.2.80 maps to 10.20.20.80. Where does DNAT sit relative to policy?

           After SNAT, so the policy destination is always 192.0.2.80.
           Before routing and policy; the policy destination is the VIP object, and dest zone is LAN after rewrite.
           Only if you also set nat enable on the WAN-to-LAN policy.
           DNAT is a session helper, not a VIP.

       Correct:  b . VIP DNAT rewrites dest first so route/policy see LAN and the VIP as dstaddr. SNAT is the later, optional hide. Re-read Flow 1 and Flow 2.

       Q5
       You edited SNAT on policy 1. The next connection from the same host still uses the old public IP. First move?

           Reboot the FortiGate so IP pools reload.
           Add a second default route with a better metric.
           Switch the policy incoming interface to any.
           Filter the host in the session table and diagnose sys session clear, then re-test.

       Correct:  d . Installed sessions keep old hooks and skip policy. Clear, then list again. Re-read Side C and Traps.

       Q6
       Why put internal and a VLAN into zone LAN instead of two policies?

           A zone is one srcintf/dstintf object; one LAN-to-WAN policy covers every member. Miss a member and that VLAN never matches.
           Zones replace firewall policy; members of LAN are allowed to the internet with no accept rule.
           A zone assigns the IP 10.20.20.1 to every member automatically.
           Zones are only for VDOM inter-links.

       Correct:  a . Zone is an interface object used in policy. IP stays on the member. Intra-zone is not WAN access. Re-read Zone, policy, NAT — three objects and Side A.

       Check answers
       Reset

## Sources

- FortiOS Administration Guide — Firewall policy (incoming/outgoing interface, address, service, schedule; VIP vs ordinary policy / match-vip)

- FortiOS Administration Guide — Zone

- FortiOS Administration Guide — Using a session table ( diagnose sys session filter / list / clear ; nsrc/nport)

- FortiOS Best Practices — Policies (specific interface, not any; zones group interfaces)

- FortiOS Administration Guide — Destination NAT / Configuring VIPs; Source NAT / Central SNAT (policy NAT vs central-nat)

- Fortinet Community — Technical Tip: How to check NGFW policy matching ( diagnose sys session list shows policy_id and SNAT hooks)

- FortiOS — Using the debug flow tool ( diagnose debug flow trace start )

 Related:  Policy first-match  ·  FortiGate command center  ·  FortiGate interview  ·  VDOMs

---
Cite this Techclick lesson with the source URL. Do not invent fees, batch dates, or job guarantees.
Browse all lessons: https://ai.techclick.in/blogs
AI index: https://ai.techclick.in/llms.txt
