A field-tested playbook for moving from Cisco ASA to pfSense — the security-level trap that breaks one-to-one ACL translation, NAT and VPN mapping, the AnyConnect problem, gotchas, and a rollback plan. Plus the Claude prompt that does the line-by-line translation.
CISCO ASA pfSense ────────────────────────────────────────────────── security-level 100 / 0 → (no equivalent) access-list OUTSIDE_IN → Firewall ▸ Rules nat (inside,outside) → Firewall ▸ NAT crypto map / IKEv2 → VPN ▸ IPsec AnyConnect SSL VPN → VPN ▸ OpenVPN ⚠ new client object-group → Firewall ▸ Aliases ────────────────────────────────────────────────── [!] the trap: ASA trusts by security-level. pfSense trusts nothing.
The reason ASA-to-pfSense migrations go sideways is almost never the firewall rules people worry about. It is the rules they don’t write — because on the ASA, the rule was never explicit in the first place. The ASA has a trust model baked into every interface called the security level, and pfSense has nothing like it. Translate the ACLs line-for-line, forget the security levels, and you will ship a firewall that blocks traffic your old one quietly allowed.
This is the playbook: what maps to what, the order to do it in, the gotchas that cost a weekend, and a rollback plan that does not involve a panicked drive to the datacenter. Claude does the mechanical translation; you make the judgment calls.
Run the decision tree before you touch anything:
If you cleared the tree, here is the work.
Most of the migration is translating one mental model into another. This table is the whole migration in miniature — print it.
| Cisco ASA | pfSense | The catch |
|---|---|---|
security-level (0–100) | Nothing — trust is expressed only through rules | The #1 trap. ASA implicitly permits high→low; pfSense permits nothing implicitly |
access-list ... in interface X | Firewall ▸ Rules, per-interface tab | pfSense rules are first-match, top-down, evaluated on the ingress interface |
nat (inside,outside) / object NAT | Firewall ▸ NAT ▸ Outbound (Auto/Hybrid/Manual) | Switch to Hybrid or Manual to recreate specific rules |
| Static / twice NAT, mapped IPs | NAT ▸ Port Forward and 1:1 NAT | The auto-created firewall rule matches the internal target IP, not the WAN IP |
object-group network/service | Firewall ▸ Aliases | Aliases nest and can be reused across rules — cleaner than ASA groups |
crypto map / IKEv1 / IKEv2 site-to-site | VPN ▸ IPsec (Phase 1 / Phase 2) | Match DH group, PFS, and lifetimes to the peer, which you don’t control |
| AnyConnect SSL VPN | VPN ▸ OpenVPN or WireGuard | pfSense cannot be an AnyConnect headend. Clients must change |
nameif / subinterfaces | Interfaces + VLANs | Assign VLANs first, then interfaces, then rules |
route outside ... | System ▸ Routing ▸ Static Routes | Gateways are first-class objects you create before routes |
| Active/standby failover | CARP + pfsync + XMLRPC config sync | Active/passive; sync the state table or VPNs drop on failover |
On the ASA, this config silently permits all traffic from inside (level 100) to outside (level 0):
interface GigabitEthernet0/0
nameif inside
security-level 100
interface GigabitEthernet0/1
nameif outside
security-level 0
There is no ACL line authorizing outbound traffic. The security level is the authorization. An admin reading the running-config sees no “permit inside to outside” rule because none exists — the permission is structural.
pfSense has no structural permissions. A freshly assigned interface passes nothing inbound until you write a rule. So when you migrate, you have to make every implicit ASA permission explicit:
# pfSense LAN rule that recreates "security-level 100 → 0"
Action: Pass
Interface: LAN
Source: LAN subnets
Destination: any
Miss this and “the internet stopped working” after cutover — not because you mistranslated an ACL, but because you translated the ACLs perfectly and forgot the rule the ASA never made you write. Walk every interface pair and ask: did the ASA allow this implicitly by security level? If yes, write an explicit pass rule.
Do it in this sequence. Each step depends on the one before it.
show running-config is the source of truth. Pull out, in order: interfaces + nameif + security-levels, object / object-group, access-list, all NAT (object nat, nat twice-NAT), crypto/VPN, static routes, DHCP.sloppy state where genuinely required.The mechanical part — turning ASA syntax into pfSense intent — is exactly what an LLM is good at, if you make it account for the security-level trap instead of translating blind. Keep this in your pack:
You are migrating a Cisco ASA configuration to pfSense.
Here is the relevant ASA config: [paste interfaces with security-levels,
the object-groups, and the access-lists].
Do all of the following:
1. List every interface with its nameif and security-level.
2. For each interface PAIR, state whether the ASA implicitly permits
traffic by security level with NO matching ACL line. Flag each
implicit permission — these need EXPLICIT pfSense pass rules.
3. Translate each access-list entry into a pfSense firewall rule
(interface, action, source, destination, port), preserving order.
4. Convert each object-group into a pfSense Alias.
Output a table per interface. Do not assume pfSense permits anything by
default — it does not.
The line that earns its keep is #2. Without it the model dutifully translates your ACLs and never tells you about the permissions the ASA was granting for free — which is the exact way these migrations break.
Then audit the result against the firewall-rule review prompt before go-live.
A migration without a tested rollback is a gamble. Before the cutover window:
Diagnostics ▸ Backup & Restore) the moment it’s working, so a failed experiment costs minutes, not the whole build.Translating the ACLs is the easy 80%. The migration lives or dies on the permissions the ASA granted implicitly by security level — make every one of them an explicit pfSense rule, or ship a firewall that’s quietly stricter than the one it replaced.
Migration #02 — FortiGate → OPNsense. Different vendor pair, different trap: FortiGate’s policy-based NAT and the way its implicit deny interacts with the SD-WAN rules people forget they enabled.
— 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 Cisco ASA configuration to pfSense?
A: Yes, but not as a literal line-by-line port. Interfaces, ACLs, NAT, object-groups, static routes, and site-to-site IPsec VPNs all have clean pfSense equivalents. The two things that do not port directly are ASA security levels (pfSense has no implicit trust — you write explicit rules) and AnyConnect SSL VPN (pfSense uses OpenVPN or WireGuard, so remote clients change). Plan those two as their own workstreams.
Q: What is the biggest difference between Cisco ASA and pfSense?
A: The trust model. The ASA assigns every interface a security level (0–100) and implicitly permits traffic from a higher level to a lower one with no explicit rule. pfSense has no security levels and permits nothing inbound until you write a pass rule. Migrations break when admins translate the ACLs perfectly but forget to recreate the permissions the ASA was granting implicitly by security level.
Q: Does pfSense support AnyConnect VPN clients?
A: No. pfSense cannot act as a Cisco AnyConnect headend. The standard replacements are OpenVPN (with the OpenVPN Connect client) or WireGuard. Your LDAP/RADIUS authentication and certificates can usually carry over, but every remote user must install a new client and import a new profile — communicate this well before cutover.