Convert a Cisco ASA configuration to FortiGate, and get a report of everything a human still needs to check.
Moving from ASA to FortiGate is rarely hard because of syntax. It's hard because the two firewalls think differently, and a line-by-line translation quietly changes behaviour. fwmigrate handles the parts that usually go wrong:
| ASA behaviour | What goes wrong in a naive migration | What fwmigrate does |
|---|---|---|
| Traffic from a higher to a lower security level is allowed without any ACL | Users lose internet access after cutover | Generates explicit policies for those flows (with NAT where ASA applied it) and lists them in the report |
| ACLs name the real IP; NAT happens separately | Inbound rules point at the server's private IP and never match | Rules towards a statically NATed host are rewritten to use the VIP, but only for traffic arriving on the NAT's outside interface |
| ACLs have no egress interface | Policies get dstintf any or the wrong port |
Derives dstintf from connected networks and static routes |
Object NAT dynamic interface / pool |
PAT applied to too much or too little | Applies NAT only to egress interfaces the ASA NAT covers; splits a policy when needed; flags partial coverage |
Remarks, inactive, log |
Context and intent lost | Kept as policy comments, status disable, logtraffic all |
| Things that can't be converted safely | Silently dropped | Listed under Needs review with the config line number |
It also audits the ASA rule base before you migrate (risky services open to any, shadowed rules, duplicates, rules with zero hits), so you don't copy years of rule debt onto new hardware.
One Python package, standard library only.
pipx install git+https://github.com/hrkh1990/fwmigrate # or: pip install .
fwmigrate convert asa-running.cfg --map interfaces.map -o out/✓ 10 addresses → config firewall address
✓ 2 address groups → config firewall addrgrp
✓ 11 services/groups → config firewall service
✓ 2 NAT objects → config firewall vip / ippool
✓ 12 policies → config firewall policy
+ 1 implicit security-level permits made explicit
⚠ 2 items need review (see report.md)
⚠ 1 high-risk rules found in the ASA rule base (see report.md)
wrote: out/fortigate.conf out/report.md
interfaces.map maps ASA interface names to FortiGate ports:
outside port1
inside port2
dmz port3
See a full run on a realistic config: input → fortigate.conf + report.md.
fwmigrate audit asa-running.cfg
ssh admin@asa 'show access-list' | fwmigrate audit - # adds hit counts and real line numbers
fwmigrate audit asa-running.cfg --fail-on medium # exit 1: block a change in CI| Finding | Severity |
|---|---|
ANY_ANY: permit ip any any |
high |
RISKY_SERVICE_FROM_ANY: SSH, RDP, SMB, databases, VNC, SNMP… reachable from any |
high |
ALL_PORTS_FROM_ANY / BROAD_PORT_RANGE |
medium |
SHADOWED: below a catch-all, can never match |
medium |
CLEARTEXT_TELNET |
medium |
DUPLICATE |
low |
UNUSED: hitcnt=0 |
info |
- Interfaces (
nameif, security level, IP), static routes,namealiases object network(host, subnet, range, FQDN) andobject-group network(nested)object service,object-group service(port groups and mixed service groups)- Extended ACLs bound with
access-group … in interfaceorglobal - Object NAT:
static(incl.static interfaceand port forwarding) → VIP,dynamic interface/ pool → policy NAT / IP pool
Not yet: IPv6, twice NAT (nat (a,b) source …), time ranges, VPN, routing protocols. These are reported, never dropped.
- Read
report.md, especially Needs review and Implicit permits added - Pick a
--start-idthat doesn't collide with existing policy IDs (default 1000) - Load objects, then VIPs / pools, then policies, in a maintenance window
- Test what must work and what must stay blocked
python -m unittest discover tests -vIssues with a (sanitised) config snippet that converts badly are the most useful contribution.
Built by Hamidreza Khazaie, a network & cloud security consultant in Rome. I plan and run firewall migrations across Cisco, Fortinet, Juniper and Palo Alto. If you have one coming up, get in touch.
MIT licensed.