pythonciscoconfparse

using CiscoConfParse to find ACL applied to VTY and then check ACL for log statement


I have the code to find the name of the 'access-class' on the 'line vty'

After that I am able to find the ACL but then I want to check each line of the ACL to verify that the 'deny' and 'permit' statements have the 'log' keyword. And print a report of OPEN if there is no 'log' statement or NOT A FINDING if there is a 'log' statement on the entry.

And this is where I am at a loss for how to parse through the ACL statements, whether I can use something from CiscoConfParse or more standard python to do the job?

#Importing the necessary modules.
import sys
from sys import argv
#Importing the necessary modules. 
from ciscoconfparse import CiscoConfParse
import sys
import argparse

def check_VTY_ACL_NET1637():
## Search for LINE VTY access-list name then check if that ACL has    log keyword
#
    for VTY_ACL in parse.find_children_w_parents('line vty', 'access-class'):
        #print(VTY_ACL[14])
        VTY_ACL = VTY_ACL.lstrip('access-class ')
        #print (VTY_ACL)
        VTY_ACL_2 = VTY_ACL.rstrip(' in')
        #print(VTY_ACL_R)
        #has_ACL_in = VTY_ACL.find_lines(r'access-class')
        #print(has_ACL_in)
        #for IP_ACL_LIST in parse.find_objects_w_child(VTY_ACL_R, 'log'):
        #for IP_ACL_LIST in parse.find_lines(VTY_ACL_R):
        for IP_ACL_LIST in parse.find_parents_w_child(VTY_ACL_2, ''):
            #print(IP_ACL_LIST)
            #IP_ACL_ACE = parse.has_line_with(' log')
            IP_ACL_ACE = parse.find_children_w_parents(IP_ACL_LIST, '')
            #print(IP_ACL_ACE)
            has_log_keyword = parse.has_line_with(r' log')
            #print(has_log_keyword)
            #
            #has_log_keyword = has_log_keyword.split()
            for log in IP_ACL_ACE:
                #print (log)
                #has_not_log_keyword = parse.has_line_with(r'. log')
                #print(has_not_log_keyword)
                keyword_log = 'log'
                keyword_permit = 'permit'
                keyword_deny = 'deny'
                log = log.split()
                print (log)
                if (not keyword_log):
                   print('OPEN LINE VTY')
                else:
                   print("Not a Finding: 'NET-VLAN-023'" )


# Main starting of script
def start():
script, input_file = argv
global parse
parse = CiscoConfParse (input_file)
print("Opening config file: %r\n" % input_file)
check_VTY_ACL_NET1637()

def main():
args = sys.argv[1:]
if len(args) == 1:
    start()
#else:
    #usage()

if __name__ == "__main__":
main()

here is the sample configuration file I am using with the ACL on the VTY

Current configuration : 25432 bytes
!
ip access-list extended SSH2-IN
 remark ///\\\///\\\///\\\///\\\///\\\///\\\///
 remark ///\\\***DEC 8 2015***///\\\
 remark SomeSite //VoSIP //
 remark ******************************************
 permit ip 10.227.2.128 0.0.0.63 any
 permit tcp 43.81.133.0 0.0.0.255 any eq 22 log
 deny   ip any any
!
line vty 0 4
 access-class SSH2-IN in
line vty 5 15
access-class SSH2-IN in
!
end

Solution

  • from ciscoconfparse import CiscoConfParse
    
    input_file = 'some_site.conf'
    parse = CiscoConfParse(input_file)
    
    ## Build a list of vty ACLs here (and flag if a vty doesn't have one)
    vty_acl_names = set([])
    for vtyobj in parse.find_objects(r'^line\svty'):
    
        vty_acl_name = vtyobj.re_match_iter_typed('access-class\s+(\S+)\s+in', 
            result_type=str, default="")
    
        if not vty_acl_name:
            print "FAIL: '{0}' doesn't have an ACL".format(vtyobj.text)
        else:
            vty_acl_names.add(vty_acl_name)
    
    ## Check ACL log compliance here (and ensure the ACL is even in the config)
    for vty_acl_name in vty_acl_names:
        try:
            aclobj = parse.find_objects(r'ip\s+access-list\s+extended\s+{0}'.format(vty_acl_name))[0]
        except IndexError:
            print "FAIL: ACL {0} is applied to a vty, but it's missing from the config!".format(vty_acl_name)
        ## NOTE: this only works if you are using extended ACLs on the VTY
        for ace in aclobj.children:
    
            if 'remark' in ace.text:
                continue
            if 'log' in ace.text:
                print "NOT A FINDING - ACL {0}, ACE: {1}".format(vty_acl_name, ace.text)
            else:
                print "OPEN (no log) - ACL {0}, ACE: {1}".format(vty_acl_name, ace.text)
    

    When I run it on your config I get this output:

    (py27_default) mpenning@MPENNING-BRIX C:\Users\mpenning
    > python garrybaker.py
    FAIL: 'line vty 5 15' doesn't have an ACL
    OPEN (no log) - ACL SSH2-IN, ACE:  permit ip 10.227.2.128 0.0.0.63 any
    NOT A FINDING - ACL SSH2-IN, ACE:  permit tcp 43.81.133.0 0.0.0.255 any eq 22 log
    OPEN (no log) - ACL SSH2-IN, ACE:  deny   ip any any
    
    (py27_default) mpenning@MPENNING-BRIX C:\Users\mpenning
    >
    

    I took the liberty of adding to your requirements... I flag vty lines that don't have an ACL.

    Now, you might ask "Why did it say that 'line vty 5 15' doesn't have an ACL, because I put it in the config? It's because I used re_match_iter_typed(), which only searches children of the parent line... when you input a config like this, CiscoConfParse will not associate the access-class with the parent vty line on 5 15, because the access-class line is not indented more than line vty 5 15:

    line vty 0 4
     access-class SSH2-IN in
    line vty 5 15
    access-class SSH2-IN in
    !
    

    Indentation matters (a lot) to CiscoConfParse... you have to know your inputs... if you can't depend on people to indent their configs, then use the method in Kirk's answer :-)