Introduction:

I don't like security. It just gets in the way when you're trying to get stuff done. But that is no excuse to have nothing in place!

I typically trust NAT, and don't tend to operate with much in the way of firewalling on home router setups. NAT will implicitly block inbound connections to stuff - because your internal addressing is not routable. That's not to say you shouldn't have any security, VTY ACLs on routers are especially important, and blacklists for nasty stuff are also useful.

So why bother now? Simply put, IPv6. IPv6 is publicly routable, so as soon as you've got it you have to have some way of securing your devices. Given the number of IPv6 addresses which an attacker would have to scan to find something vulnerable it's fairly unlikely. But what happens if they get your IP from some service you're using? Without any firewall in place, they can connect straight to it and any services you're running! Not good.

Solution:

Zone Based Firewall. I am a fan of the Context Based Access Control (CBAC) firewall (because it's simple and I don't like security). However the platform I am using no longer supports it, and it really is worth using something a bit more up to date.

First, define the zones you want. I have two, Inside and Outside. Inside is for LAN and Outside for WAN, pretty straightforward!

zone security Inside
zone security Outside

Let's now focus on outbound communications. Inbound communications are only required if you've got servers or services running within your network you want to expose.

Define the protocols you want to allow outbound in a class-map. Mine are generic because I just want to allow everything out.

class-map type inspect match-any Outbound_Protocols
 match protocol tcp
 match protocol udp
 match protocol icmp

Now we define policy maps to allow communication. It's worth noting that I have an Inside-Inside policy to allow communication explicitly, so I can modify it in future if required.

We inspect outbound protocols, so any return traffic will be allowed without having to match an inbound policy. This is why we need stateful!

policy-map type inspect Inside_Policy
 class class-default
  pass

policy-map type inspect Inside_to_Outside_Policy
 class type inspect Outbound_Protocols
  inspect 
 class class-default
  drop

Define the zone pairs, and then we can attach our policy maps:

zone-pair security Inside-Inside source Inside destination Inside
 service-policy type inspect Inside_Policy

zone-pair security Inside->Outside source Inside destination Outside
 service-policy type inspect Inside_to_Outside_Policy

Finally, don't forget to associate the zones with interfaces! In this example I don't actually have multiple Inside zone members, so could have omitted the Inside-Inside zone pair and associated policies. Doesn't hurt to keep it though.

interface gig0/0/1
 zone-member security Outside

interface vlan20
 zone-member security Inside

Now, what about if we're running services internally? We need to define a path for traffic to get in without having an existing inspection entry.

First, define an ACL with the entries you need to permit. In my case I permit VPN traffic, as well as HTTP and HTTPS (in my case this blog).

ip access-list extended ACL_Inbound_Protocols
 1 permit udp any any eq isakmp
 2 permit udp any any eq non500-isakmp
 3 permit esp any any
 4 permit ahp any any
 10 permit tcp any any eq 443
 20 permit tcp any any eq 80

Note: You MUST have the corresponding NAT entries configured already! If you don't then your service won't work before the firewall rules are in place!

Make sure that your port numbering in the above ACL uses the internal port number as NAT happens first. I.e. if you're hosting a webserver internally on port 80, but exposing it to the world on port 8080, you must use port 80 in your ACL!

Now it's the same structure as before, class-map first. We match the ACL above.

class-map type inspect match-any Inbound_Protocols
 match access-group name ACL_Inbound_Protocols

Then the policy-map:

policy-map type inspect Outside_to_Inside_Policy
 class type inspect Inbound_Protocols
  inspect
 class class-default
  drop

Then finally the zone-pair. You need a zone-pair for each direction:

zone-pair security Outside->Inside source Outside destination Inside
 service-policy type inspect Outside_to_Inside_Policy

That's it! Done!

...wait a second, what about IPv6?!

It's actually super easy, it's already working! If you've got IPv6 up and running, you'll notice you can no longer reach internal addressing from the outside world, awesome!

If you need to pinhole specific stuff, so you can get access to servers etc, we'll have to add another ACL. Mine's denying everything explicitly as a placeholder for future services.

ipv6 access-list ACL_Inbound_Protocols_v6
<permit stuff here>
 deny ipv6 any any

Then all you've got to do, is modify the Inbound protocols class-map, from this:

class-map type inspect match-any Inbound_Protocols
 match access-group name ACL_Inbound_Protocols

To this:

class-map type inspect match-any Inbound_Protocols
 match access-group name ACL_Inbound_Protocols
 match access-group name ACL_Inbound_Protocols_v6

Done! How easy is that!

Verification Commands:

There's a few commands to verify and check things - these work rather nicely for the basics:

show policy-map type inspect zone-pair
show policy-firewall stats global
show zone-pair security
show zone security
show policy-firewall sessions platform

Final Configuration (If you don't host anything internally):

class-map type inspect match-any Outbound_Protocols
 match protocol tcp
 match protocol udp
 match protocol icmp


policy-map type inspect Inside_Policy
 class class-default
  pass

policy-map type inspect Inside_to_Outside_Policy
 class type inspect Outbound_Protocols
  inspect 
 class class-default
  drop
       

zone security Inside
zone security Outside


zone-pair security Inside-Inside source Inside destination Inside
 service-policy type inspect Inside_Policy

zone-pair security Inside->Outside source Inside destination Outside
 service-policy type inspect Inside_to_Outside_Policy

Final Configuration (If you do host stuff):

class-map type inspect match-any Outbound_Protocols
 match protocol tcp
 match protocol udp
 match protocol icmp

class-map type inspect match-any Inbound_Protocols
 match access-group name ACL_Inbound_Protocols
 match access-group name ACL_Inbound_Protocols_v6


ip access-list extended ACL_Inbound_Protocols
 !<add permit statements for internal services>
 !<remember that NAT happens first!>

ipv6 access-list ACL_Inbound_Protocols_v6
 !<add permit statements for internal services>
    
     
policy-map type inspect Inside_Policy
 class class-default
  pass

policy-map type inspect Inside_to_Outside_Policy
 class type inspect Outbound_Protocols
  inspect 
 class class-default
  drop

policy-map type inspect Outside_to_Inside_Policy
 class type inspect Inbound_Protocols
  inspect
 class class-default
  drop

     
zone security Inside
zone security Outside

     
zone-pair security Inside-Inside source Inside destination Inside
 service-policy type inspect Inside_Policy

zone-pair security Inside->Outside source Inside destination Outside
 service-policy type inspect Inside_to_Outside_Policy

zone-pair security Outside->Inside source Outside destination Inside
 service-policy type inspect Outside_to_Inside_Policy