a developing networker

OnePK – Configuring and Assigning an ACL

22 Nov 2014 » Coding, Networking
This entry is part 5 of 5 in the series Getting started with Cisco OnePK

During my research of the ACL module of OnePK I found some interesting behaviors. I think this feature isn’t fully cooked as it has some definite room for improvement. The ACL module (found in `onep.policy`) doesn’t have a way to interact with existing ACLs and ACEs on an IOS device. The ACLs seem to be intended for on-the-fly ACL configuration but with a couple caveats:

  • Applying an ACL to an interface overwrites the existing ACL for that direction. Interfaces must play by the rule: ‘One ACL per direction’.
  • Make sure to use `OnepLifetime.ONEP_PERSISTENT` to keep ACLs applied after the OneP session is disconnected.

First I’ll cover ACL management with OneP, and then I’ll use the `onep.vty` module to show how to workaround some of the shortcomings.

Creating and Applying an ACL On-the-Fly

A use case that fits in well with OnePK ACL management is creating dynamic ACLs based on input from a separate application such as a IDS/IPS system. Let’s use this scenario for the first example: Our OnePK app received an alert (possibly a syslog or SNMP trap) that a device on the network is receiving an attack using a SSH exploit and we need to stop the bogus traffic.  We know that SSH uses TCP port 22 and also that the end host receiving the attack has an IP address of  Here’s one way we can create an ACL to block that traffic:

from onep_connect import connect
from onep.core.util import OnepConstants
from onep.policy import Acl, L3Acl, L3Ace

# Connect to a router inline with the attacker's traffic
ne = connect('', 'admin', 'admin')

#specify the interface towards the attacker
interface = ne.get_interface_by_name('thub2')

#  Create a IPv4 L3 ACL
l3_acl = L3Acl(ne, OnepConstants.OnepAddressFamilyType.ONEP_AF_INET, L3Acl.OnepLifetime.ONEP_PERSISTENT)

# Create ACE that matches the attacker's traffic
l3_ace_10 = L3Ace(10, False)  #False == deny
l3_ace_10.protocol = OnepConstants.AclProtocol.TCP
l3_ace_10.dst_prefix = ''
l3_ace_10.dst_prefix_len = 32            
l3_ace_10.set_dst_port_range(22,22)    #SSH Port

# Create ACE to allow all other traffic
l3_ace_20 = L3Ace(20, True)  #True == permit
l3_ace_20.protocol = OnepConstants.AclProtocol.ALL

# Add both ACEs to the ACL

# Apply the ACL to the interface
l3_acl.apply_to_interface(interface, Acl.Direction.ONEP_DIRECTION_IN)

# End the onep session (ACL remains because we used a persistent lifetime)

There isn’t a lot of logging provided on the IOS device to tell us that the ACL was applied, but we can run a `show ip interface gi2` command at the enable prompt to see the following confirmation:

CSR1#sh ip interface gi2 | i access list
  Outgoing Common access list is not set
  Outgoing access list is not set
  Inbound Common access list is not set
  Inbound  access list is onep-acl-18

Boom, there’s our dynamic ACL on the inbound direction of the ‘gi2’ interface. We could apply this interface in both directions if we used `Acl.Direction.ONEP_DIRECTION_BOTH` in the `.apply_to_interface()` method. Also, you guessed it, we can use `Acl.Direction.ONEP_DIRECTION_OUT` for traffic leaving that interface also.  This ACL could also be applied to multiple interfaces (more than one Internet facing interface?) like this:

# Apply the ACL to multiple interfaces
l3_acl.apply_to_interface(ne.get_interface_by_name('gi2'), Acl.Direction.ONEP_DIRECTION_IN)
l3_acl.apply_to_interface(ne.get_interface_by_name('fa0/1'), Acl.Direction.ONEP_DIRECTION_IN)

Depending on our expectations for the life of the ACL, we can have it automatically removed at the end of the onep session by specifying  `L3Acl.OnepLifetime.ONEP_TRANSIENT` during the creation of the L3Acl Object.

To see a more in-depth example that includes syslog parsing and a bi-directional ACE, check it out on the GitHub repo here.

Viewing and Modifying Existing ACLs

Support for interacting with existing ACLs isn’t directly built-in to OnePK, but we can use the `onep.vty` module to achieve some pretty close capabilities. Here’s a simple way to view existing ACLs:

from one_connect import connect
from onep.vty.util import VtyHelper

ne = connect('', 'admin', 'admin')
ne_vty = VtyHelper(ne)'ip access-list')

Which gives the output:

Extended IP access list DEMO
    10 permit ip any
    20 deny tcp any any eq smtp
    30 permit tcp any range www 443
Extended IP access list onep-acl-18
    10 permit tcp any any range 22 22

Very cool, there’s our dynamically created ACL from the previous example along with an existing ACL on the router. This is returned to us as a string but some fancy parsing could be done in order to confirm compliance to policies. Using that same `VtyHelper` object, we can also modify the DEMO ACL by adding an ACE using the `.config()` method:

from one_connect import connect
from onep.vty.util import VtyHelper

ne = connect('', 'admin', 'admin')
ne_vty = VtyHelper(ne)

ne_vty.vty_exec('conf t')
ne_vty.vty_exec('ip acce ext DEMO')
ne_vty.vty_exec('15 deny udp any any')
ne_vty.vty_exec('no 20')
ne_vty.vty_exec('end')'ip access-list')

And we can see our added sequence #15 ACE and removed #20 sequence ACE in the DEMO ACL:

Extended IP access list DEMO
    10 permit ip any
    15 deny udp any any
    20 deny tcp any any eq smtp
    30 permit tcp any range www 443
Extended IP access list onep-acl-18
    10 permit tcp any any range 22 22

With both of these ACL creation technique, don’t forget to save the config on the device with the VtyHelper `.commit()` method.

As you can see, dynamic ACL creation is very powerful especially since you can manipulate existing ACLs with the `VtyHelper` object. I think this is a good supplement in places where the onep SDK doesn’t have full feature coverage. It certainly does open the door to any configuration that can be done via CLI.