Moving from FortiGate to OPNsense means unbundling one of FortiGate's all-in-one firewall policies into the separate rule, NAT, and inspection objects OPNsense uses — and rebuilding the SD-WAN and UTM behavior people forget they turned on. The conceptual map, the policy-unbundling trap, gotchas, a rollback plan, and the Claude prompts that do the translation.
FORTIGATE OPNsense ───────────────────────────────────────────────────────── firewall policy (all-in-one) → Rule + NAT + UTM (separate) ├ src / dst / service → Firewall ▸ Rules ├ NAT (inside the policy) → Firewall ▸ NAT └ UTM profiles (AV/IPS/web) → Suricata / web proxy (plugins) zones / VDOMs → Interfaces / (no VDOM equivalent) SD-WAN rules → Gateways + Gateway Groups + PBR FortiClient SSL VPN → OpenVPN / WireGuard ⚠ new client ───────────────────────────────────────────────────────── [!] the trap: one Forti policy = four OPNsense objects. Unbundle them.
The FortiGate-to-OPNsense migration breaks differently than the ASA-to-pfSense one. The ASA trap was an implicit permission you had to make explicit. The FortiGate trap is the opposite: a single, over-loaded object you have to take apart. One FortiGate firewall policy quietly does four jobs — filtering, NAT, threat inspection, and logging — and OPNsense splits those across four different places. Translate the policy as if it only filters, and you’ll ship a firewall that passes the right traffic with none of the inspection it used to do.
This is the playbook: the conceptual map, the unbundling discipline, the SD-WAN and UTM work people underestimate, and a rollback that isn’t a prayer. Claude does the decomposition; you make the calls.
| FortiGate | OPNsense | The catch |
|---|---|---|
| Firewall policy (bundles filter + NAT + UTM + log) | Rule + NAT + plugin — separate objects | The whole migration. One policy decomposes into up to four things |
srcintf/dstintf + srcaddr/dstaddr + service | Firewall ▸ Rules, per-interface | OPNsense rules are evaluated on the ingress interface, first match |
NAT inside the policy (nat enable, IP pools) | Firewall ▸ NAT (Outbound / 1:1 / Port Forward) | First find out if the FortiGate uses central NAT or policy NAT |
| Security profiles (AV, IPS, web, app-ctrl, SSL) | Suricata, web proxy, Zenarmor/Sensei plugins | Not one-to-one; each is a separate package to install and tune |
| Address / service objects & groups | Firewall ▸ Aliases | Clean mapping; build these first |
| Zones | Interface groups | Conceptually close |
| VDOMs | Nothing | Multiple virtual firewalls → multiple instances |
| SD-WAN rules + performance SLA | Gateways + Gateway Groups + policy-based routing | Rebuilt from parts; health-check tuning is manual |
| Static / policy routes | System ▸ Routing | Gateways are first-class objects |
| FortiClient SSL VPN | OpenVPN / WireGuard | Clients must change |
| IPsec site-to-site | VPN ▸ IPsec | Match Phase 1/2 to the peer exactly |
A single FortiGate policy looks like this in spirit:
config firewall policy
edit 12
set srcintf "internal"
set dstintf "wan1"
set srcaddr "LAN_NET"
set dstaddr "all"
set service "HTTPS" "DNS"
set action accept
set nat enable ← NAT lives INSIDE the policy
set av-profile "default" ← so does antivirus
set ips-sensor "protect_client" ← and IPS
set webfilter-profile "corp" ← and web filtering
set ssl-ssh-profile "deep" ← and SSL inspection
next
end
That is one line in the GUI and four jobs. In OPNsense it becomes:
internal → any, service HTTPS/DNS, pass) — the filtering.LAN_NET — the nat enable part, configured in a completely different menu.av/ips/webfilter profiles, as plugins you install and tune.Translate only job #1 and the traffic flows — which is exactly why the mistake survives testing. The malware scanning, IPS, and web filtering that the FortiGate was silently doing are just gone, and nothing about a passing connection tells you so. For every policy, write down all four jobs before you build anything, and check them off in OPNsense one at a time.
One more thing to resolve up front: which NAT mode the FortiGate runs. With central NAT disabled (the default), NAT is configured per-policy as above. With central NAT enabled, NAT lives in separate Central SNAT/DNAT tables and the policies don’t carry it. You read — and translate — the config completely differently depending on which it is, so check first.
show full-configuration (or a GUI revision backup). Pull, in order: VDOMs, zones/interfaces, address & service objects/groups, NAT mode, firewall policies with their UTM profiles noted, SD-WAN config, VPNs, routes.The decomposition is mechanical once you force the model to account for all four jobs, not just the filter:
You are migrating a FortiGate firewall policy to OPNsense.
Here is the policy and its referenced objects: [paste the firewall policy,
the address/service objects, and any referenced security profiles].
Also: central NAT is [enabled/disabled] on this FortiGate.
For this single policy, output FOUR sections:
1. The OPNsense firewall RULE (interface, action, source, dest, service).
2. The NAT translation it implies, and where it goes in OPNsense.
3. Every UTM profile it references (AV, IPS, web filter, app control, SSL)
and the OPNsense plugin that replaces each.
4. Whether logging is on, and the matching OPNsense rule logging setting.
Flag anything that has no clean OPNsense equivalent.
The “FOUR sections” structure is the whole point — it stops the model from translating the filter and dropping the NAT and UTM, which is precisely how these migrations lose their security posture. Then run the result through the firewall-rule review prompt before go-live.
System ▸ Configuration ▸ Backups) the moment it works.A FortiGate policy is four OPNsense objects in a trench coat — a rule, a NAT, an inspection plugin, and a log setting. Unbundle all four for every policy, and remember SD-WAN was quietly doing your path selection the whole time.
Migration #03 — Cisco IOS router → VyOS, and the mindset shift from copy run start to treating your edge config as code in a git repo.
— Subscribe to Packet Drop (newsletter form below) to get the next playbook when it ships.
— Want the translation and audit prompts ready-to-paste? Get the Prompt Pack — 60 production prompts for network admins, $29, lifetime updates.
Q: Can I migrate a FortiGate configuration to OPNsense?
A: Yes, but it is a decomposition, not a one-to-one port. A single FortiGate firewall policy bundles filtering, NAT, threat inspection (AV/IPS/web/app control), and logging. OPNsense splits those across separate firewall rules, NAT settings, and plugins (Suricata, web proxy, Zenarmor). Address and service objects map cleanly to Aliases; VDOMs and the integrated UTM stack do not map directly and need redesign.
Q: What is the hardest part of a FortiGate to OPNsense migration?
A: Unbundling the all-in-one firewall policy without losing the security inspection it performed. Because OPNsense filtering, NAT, and UTM live in different places, it is easy to translate the filter rule, see traffic flow, and never notice the antivirus, IPS, and web filtering are gone. Rebuilding SD-WAN path selection from gateway groups and policy routing, and reproducing SSL deep inspection, are the next hardest pieces.
Q: Does OPNsense replace FortiGate UTM and FortiClient VPN?
A: OPNsense covers most FortiGate UTM functions, but as separate plugins rather than one integrated stack: Suricata for IDS/IPS, a web proxy for category filtering, and Zenarmor/Sensei for application-aware L7 control — each installed and tuned independently. FortiClient SSL VPN is not supported; OPNsense uses OpenVPN or WireGuard instead, so every remote user installs a new client and profile.