First note in a series on cross-vendor firewall migration: where two platforms disagree, verified against vendor docs and real migrations.
If you are moving a policy set onto FortiGate from an ASA or a Palo Alto, there is a decision you have to make before you write a single rule, and FortiOS will not let you unmake it afterwards without tearing the policies down.
This is about central NAT. Not what it is — the documentation covers that — but what it does to a migration, and why it has to be the first thing you decide rather than the last.
Why migrations run into this at all
Per-policy NAT is the FortiGate default, and for a greenfield FortiGate it is usually the right choice. Each firewall policy carries its own nat enable and optionally an IP pool. Simple, local, readable.
The trouble is that neither an ASA nor a PAN-OS policy set is shaped that way.
On an ASA, NAT lives in its own ordered table — object NAT and twice NAT, evaluated in sections, entirely separate from the ACLs. On PAN-OS, NAT rules are their own rulebase, also separate from security rules. In both, one NAT rule serves many policy rules.
Per-policy NAT has no way to express that. If a single ASA twice-NAT statement covers traffic matched by fourteen ACEs, per-policy NAT makes you write the translation fourteen times, once on each converted policy. It works, and it is a maintenance problem the day you have to change it.
Central NAT restores the shape you came from: one ordered SNAT table, one DNAT table, and firewall policies that only decide allow or deny. That is why converted configurations tend to land in central NAT, and why anyone doing this by hand usually ends up there too.
The order problem — read this one first
You cannot switch to central NAT on a FortiGate that already has policies referencing VIPs or IP pools.
FGT # config sys settings
FGT (settings) # set central-nat enable
Cannot enable central-nat with firewall policy using vip
Enter fullscreen mode Exit fullscreen mode
There is an equivalent error for IP pools, and it names the policy ID. Every VIP and every IP pool has to reach zero references before the toggle will take. Not deleted necessarily — dereferenced — but on a freshly built policy set that usually amounts to the same thing.
So the failure mode is this. You spend two days building out interfaces, zones, address objects, service objects and four hundred policies. You get to NAT, decide central NAT is the right model, and FortiOS refuses. Now you are editing four hundred policies to strip VIP references before you can enable the mode you should have enabled on an empty box.
Decide central NAT before you build the policy set. On a new appliance the sequence is: enable central NAT, log out and back in so the GUI redraws, then build.
What flips underneath you: the destination address
This is the one that costs a cut-over window, because it fails in a direction that looks like a routing problem.
With per-policy NAT, an inbound policy references the VIP object as its destination address. That is the familiar pattern and it matches how an ASA or PAN-OS engineer thinks: the rule talks about the public address.
With central NAT enabled, that is wrong. The DNAT and Virtual IPs table applies to every policy on the FortiGate, whether or not the policy mentions a VIP. Destination translation has already happened by the time the policy is evaluated. So the policy must reference the VIP’s mapped address — the internal one.
Policy destination address FortiGate, per-policy NAT the VIP object (public address) FortiGate, central NAT the mapped address (internal address) Cisco ASA real address, post-8.3 PAN-OS security rule pre-NAT address, post-NAT zoneFour platforms, and the answer is different on each. PAN-OS is the cruellest of them — pre-NAT address but post-NAT zone in the same rule — and if you have spent years there, “destination is the public IP” is muscle memory. On a central-NAT FortiGate that muscle memory silently denies every inbound session.
Worth saying plainly: under central NAT, all VIP/DNAT rules are in effect regardless of the central SNAT table. The two tables are independent. A VIP you created and forgot about is live the moment central NAT is on, with no policy referencing it anywhere.
Central SNAT is first-match, and your old order does not carry over
The central SNAT table is read top down and the first match wins. Same as an ACL, same as an ASA NAT section — but the match keys are not the same, and that is where converted tables go wrong.
A central SNAT entry matches on source interface, destination interface, source address, destination address, protocol, destination port and original source port. An ASA NAT rule has no concept of destination interface as a match key in the same way, and section ordering in ASA (manual NAT before auto NAT before after-auto NAT) does not map onto a single flat list.
The practical consequence: an ASA NAT table that was correct in its own ordering can be flattened into a central SNAT table that is also individually correct rule by rule, and still behave differently, because the first-match boundary now falls somewhere else.
The specific thing to check is your catch-all. Most estates end with a broad “everything out to the internet, hide behind the interface address” rule. If a narrower rule that used to be evaluated in an earlier ASA section ends up below that catch-all in the flat table, it never matches, and the traffic silently leaves with the wrong source address. Nothing breaks. Nothing logs an error. A far-end ACL somewhere starts dropping you a week later.
Read the table bottom-up looking for anything below your broadest rule. That is a five-minute check and it is the highest-yield five minutes in the whole exercise.
Explicit port mapping and the ICMP trap
Central SNAT lets you pin an original source port range to a translated port range — set orig-port and set nat-port. Two constraints come with it:
Only the Overload IP pool type supports it. And any protocol without ports — ICMP first among them — cannot match a rule that uses explicit port mapping.
So the rule you wrote to pin a port range for one application quietly does not apply to ping, traceroute or path-MTU discovery from the same hosts. That traffic falls through to whatever rule comes next, which is usually the interface-address catch-all, which is usually a different source address.
You will find this the first time someone asks why the monitoring system sees a different source IP for ICMP than for TCP from the same server. Better to know before they ask.
Two more that catch people
NGFW policy-based mode implies central NAT. If the VDOM is in policy-based NGFW mode, central SNAT is on implicitly. You do not get to choose. If you are converting into a policy-based VDOM, you are in central NAT whether you planned for it or not.
With VDOMs, the SNAT and DNAT tables are per-VDOM, not global. Obvious once said, easy to assume otherwise when you are consolidating several ASA contexts into VDOMs on one chassis and thinking of the NAT table as a property of the box.
Pre-cut-over checklist
- Central NAT decided and enabled before the policy set is built, on an empty box
- Logged out and back in so the GUI shows the Central SNAT and DNAT tables
- Every inbound policy references the mapped address, not the VIP object
- VIP table audited — under central NAT everything in it is live, referenced or not
- Central SNAT table read bottom-up: nothing meaningful sits below the broad catch-all
- Any rule with explicit port mapping checked against the ICMP behaviour above, and the fall-through rule confirmed to be acceptable
-
diagnose sys session liston real traffic before you call it done — thehook=post dir=org act=snatline tells you the actual translated address, which is the only answer that counts
That last one is worth more than any amount of reading the config. The session table does not have opinions.
The part I cannot check for you
Everything above is behaviour I have hit on real migrations and then confirmed against the FortiOS documentation. What I cannot tell you is which of it applies to your build — central NAT behaviour has moved between major versions, and the version you are landing on matters.
Verify against your own lab before your change window. The cost of being wrong here is not a config error, it is a cut-over that half works.
Written by someone who does firewall migrations for a living, after doing this one the hard way. Corrections welcome — if your experience differs on any of the above, I would rather hear it.
Sources: FortiOS 7.4.3 Administration Guide, Central SNAT · Fortinet community article 181805, cannot enable central-nat error · Fortinet community article 97658, Configuration changes regarding Central NAT and Virtual IPs
More notes like this, as they get written: github.com/nizartuanku/firewall-migration-notes