Cisco made huge changes to the configuration syntax on ASA firewalls starting in version 8.3. The Cisco website has extensive documentation on both the old and new syntax, which is great if you really want to read through all of it.
But I’ve never seen a nice, concise description of how to migrate an existing ASA firewall. I’ve noticed a lot of people are desperately trying to cling to the old software to avoid the massive task of converting the configuration. Sooner or later, though, you’ll have to do it.
One of my clients was recently migrating from an old ASA firewall to a new model running the new software. Updating gave them access to the really good SourceFire features that have recently been integrated into the ASA platform. But the conversion involved a painful process of going through all the Network Address Translation (NAT) rules and access lists (ACLs) line by line.
Here are some of the most important things I tripped over during the ASA migration.
Network Address Translation
The most obvious syntax change involves NAT. The new syntax looks completely different, but it’s actually quite simple and flexible once you see how it works.
First, let’s look at the example of Port Address Translation (PAT) because PAT is something that appears in almost every Internet-facing firewall.
The old syntax looked something like this:
global (outside) 1 interface nat (inside) 1 0.0.0.0 0.0.0.0
In the newer ASA software versions, address translation is configured using objects. Here’s the equivalent in the new syntax:
object network PAT-0_0_0_0 subnet 0.0.0.0 0.0.0.0 nat (inside,outside) dynamic interface
I gave this object a hopefully meaningful name of PAT-0_0_0_0. Then I defined the “real” address ranges to which this rule will apply, namely all addresses. The third line defines the actual address translation as a dynamic translation between the inside and outside interfaces, using the IP address of the outside interface as the translated value.
Obviously, you could use a more precise subnet definition, but I generally like to make this kind of outbound web browsing rule as broad as possible. Then I nail down the exact things I want to allow or deny using access control lists on the interfaces.
The other type of very common NAT configuration is a static translation between one internal and one external address. Here’s the old style syntax:
static (dmz,outside) 198.51.100.55 172.25.13.6 netmask 255.255.255.255
In this case, 198.51.100.55 is the address that will be visible to the outside world. Naturally you should replace this with your real address. I’m using a special RFC reserved address range here for demonstration purposes. (Don’t worry, it’s not my real IP address.)
The 172.25.13.6 address is a server located on the “dmz” segment. As a general design principle, you shouldn’t make static NAT rules like this for systems on your internal network because they’re effectively exposed to the Internet, free to be poked and probed on all ports (at least until we turn on the ACL on the outside interface).
That’s why I’ve shown this rule as a translation between the DMZ and outside interfaces. However, you could just as easily have any other interface specified in the first half of the bracket where we have “dmz”.
In the new syntax, the configuration again involves an object definition:
object network NAT-MAIL-PROXY host 172.25.13.6 nat (dmz,outside) static 198.51.100.55
I’ve given this NAT object a locally meaningful name that should help anyone looking at the configuration to understand what’s going on.
You can also do port-based static NAT, which used to look like this:
static (inside,outside) tcp interface 4493 10.10.85.15 443 netmask 255.255.255.255
The equivalent configuration using the new syntax looks like this:
object network NAT-interface-4493 host 10.10.85.15 nat (inside,outside) static interface service tcp 443 4493
Once again, I used the “interface” address for the translation. But you could also use a specific IP, like in the previous example.
Note that the TCP port numbers match in the two examples. They use 443 as the real port the server is using on the inside and translate it to 4493 on the outside. If the firewall receives packets that don’t match these port numbers, the rule won’t apply.
One of the more obscure and confusing things the ASA does with its new syntax appears when you view the configuration. It displays the first part and the second part separately. So, for example, this last example would display like this:
object network NAT-interface-4493 host 10.10.85.15 [many lines deleted] object network NAT-interface-4493 nat (inside,outside) static interface service tcp 443 4493
Order of operations
The other really big difference between the old and new ASA syntax is in the order of operations.
In the old version, you had to configure inbound ACLs on the outside of your firewall to include the translated IP, which would be the IP address as the packets are received on the outside interface.
Now, it does the NAT first, and then applies the ACL. So now you have to use the real internal IP, the untranslated address.
For example, in the above NAT rule for the mail proxy, the old ACL would have had a line like this:
access-list outside_in-old extended permit tcp any host 198.51.100.55 eq 25
And you would have naturally applied it to the outside interface of the firewall like this:
access-group oustide_in-old in interface outside
In the new ASA software, the syntax is the same, but you use the inside address:
access-list outside_in-new extended permit tcp any host 172.25.13.6 eq 25
And, of course, you apply it in the same way.
access-group oustide_in-new in interface outside
The other thing that might trip you up when translating a config from old to new is the use of the “names” command. In both old and new versions, you can define human-readable names for specific hosts and subnets. In this example we might have:
name 172.25.13.6 MAIL-PROXY-INSIDE name 198.51.100.55 MAIL-PROXY-OUTSIDE
In the old ASA software, you were able to use these names in both NAT rules and ACLs:
static (dmz,outside) MAIL-PROXY-OUTSIDE MAIL-PROXY-INSIDE netmask 255.255.255.255 ! access-list outside_in-old extended permit tcp any host MAIL-PROXY-OUTSIDE eq 25
That’s nice because it makes things readable.
In the new software, you can still use names in ACLs, but not in NAT object definitions. You have to use numeric IP addresses instead. This is all the more reason to use meaningful names on your NAT object definitions.
How to test
So how do you know you’ve migrated your configuration correctly? Use the “packet-tracer” command. It’s easily one of the most useful features of the ASA feature set, and it works identically in both old and new code versions.
For example, if you wanted to make sure your inbound email traffic will work properly, you could do something like this:
packet-tracer input outside tcp 22.214.171.124 1234 198.51.100.5 smtp
This will simulate the firewall receiving an inbound TCP packet from some fictitious Internet source IP (126.96.36.199) with a source port number of 1234 (generally the source port number is irrelevant to these types of tests) addressed to the external NAT IP address that we configured above, and with a destination port of SMTP.
This command will show you step by step how the firewall evaluates the packet. It even shows you exactly how it applies NAT and what the resulting address is. The steps are different in the old and new ASA software, but the final result should be the same. So a really useful testing method is to connect the new firewall to an isolated test environment and open SSH sessions on both old and new devices. Then you can try all of the important traffic flows for your applications one by one.
Cisco’s ASA syntax changes can be difficult to navigate but the newer features in these firewalls make it worth the effort to convert and upgrade. Also, as I discovered when going through my client’s firewall configuration line by line, it’s a great opportunity to eliminate all those old and obsolete rules that wind up in every firewall configuration over time.