|
aclxml2.pl - Produce Cisco ACLs from XML definition files. |
<host name='something' ip='an_IP_address_or_range' /><host name='something' ip='an_IP_address_or_range' flags='no_autogroup' /><group name='something' members='CSV_list_of_IP_or_hostname_or_groupnames' /><group name='something'> <member name='something_else'/> </group><net name='interface_name' ip='IP_address_range' int='other_interface_name' /><synonym alias='something' real='something_else'/><acl .../><acl act='plug'..../> - the degenerate form<acl ... /> - the normal form<IGNORE> and </IGNORE>aclxml2.pl - Produce Cisco ACLs from XML definition files.
perl aclxml2.pl [-x] [ -d number] file1.xml [file2.xml...]
where -x causes reflexive ACLs to be generated
-d plus a number causes debugging information to be output.
The amount increases with the number through 3
The file(s) are XML files describing the network and
the ACLs desired.
Chicago Specific Examples:
perl aclxml2.pl net.xml r1.xml acl.xml >accx.r1.date_stamp
perl aclxml2.pl net.xml r2.xml acl.xml >accx.r2.date_stamp
The definitions can be in one or many files, which will be parsed in the order they are named on the command line.
The ACLs are printed to standard output and are captured using file redirection. Error and debugging messages are printed to standard error and so appear on the terminal even if standard output is redirected.
The program allows specification of the ACLs by address, hostnames or groups thereof, as well a groups of ports. The program automatically figures out where the pairs of ACL entries go. Options are provided in the file formats to override various automatic behavior.
This is particularly useful for paranoid installations where ACLs are desired on a 'per host' basis rather than a 'per network' basis.
As an example, the installation that this was developed for eventually boiled down to 85 ACL statements in the XML files, which generated more than 1200 'permit....' or 'deny...' entries on the output. (264 total XML elements were used defining the network and ACLs in this case.)
XML consists of markup and character data.
Markup is anything between innermost containing < and > and character data is everything else.
Character data cannot contain the < or > or & symbols, though comments can.
An XML document must have a top level element which contains all other elements. So the input files used by this program must have the start markup or tag of
<acls>
(this is the start tag of the acls element)
and an ending tag of
</acls>
(this is the end tag of the acls element).
When elements have a start tag and an end tag, as the acls element above does, they can contain other elements or character data or both.
An element can also be ``empty'' in which case the start and end tags are combined into one tag, as in
<host />
Of course, for our application, a tag like <host /> usually isn't too interesting,
so we add attributes:
<host name='snmp_mgr' ip='192.168.129.76' />
The host element above is still considered ``empty'', because it contains no other elements or non-markup data, but it has 2 attributes, 'name' and 'ip', with values 'snmp_mgr' and '192.168.129.76', respectively (minus the quote marks).
Only markup is important to this program. All the real information is in the attributes.
So, for this program, anything that is not markup is, in effect, a comment.
Comments can also be created by enclosing the comment text between <!-- and --> sequences.
Text so enclosed is comment text to all XML applications, and a good XML editor will display comment
text differently from non-comment text, such as a different color. This can be useful.
The only problem is, this will NOT nest, so if you do something like this:
<!-- here I'm commenting out a bunch of stuff for test reasons
...
...
<!-- here's a comment that was here before I commented out this section -->
<host name='gus' ip='199.169.199.169' />
here's the end of the attempt to comment out a bunch of stuff -->
the parser will puke, as by definition this is not well-formed XML.
Options are: leave comments as character data (without using the <!-- and --> sequences)
and only use the <!-- and --> sequences for commenting out sections for testing, and/or
make use of the
<IGNORE>
</IGNORE>
tags as documented below. Note that if you use a <IGNORE> ... </IGNORE> sequence, the text contained between them must be valid XML or comments.
All files start with a line like this:
<acls>
(Actually, you may see something like this before the <acls>:
<?xml version="1.0"?>
<!DOCTYPE acls SYSTEM "acls.dtd">
but this is neither necesssary nor harmful to the aclxml2.pl program, as of this writing (20020810).)
and end with a line like this:
</acls>
Including these lines a sample file can look like this:
<acls>
<host name='server1' ip='192.168.129.8'/>
<host name='server2' ip='192.168.129.9'/>
<host name='server3' ip='192.168.129.100'/>
<host name='server4' ip='192.168.129.101'/>
<host name='snmp_nms' ip='192.168.129.76'/>
<group name='https' members='server1,server2'/>
<group name='http' members='https,server3,server4'/>
groups can nest
<group name='SMTPrelays' members='172.21.34.34,172.23.14.10' />
<group name='any' members='0.0.0.0/0'/>
use groups of 1 member for aliases - don't use hosts for this
groups and hosts must resolve to an ip address or ip range.
<net name='vlan2' ip='192.168.129.0/27' />
<net name='vlan4' ip='192.168.129.64/27' />
<net name='vlan5' ip='192.168.129.96/27' />
<net name='atm' ip='192.168.254.28/29' />
<net name='DEFAULT' ip='0.0.0.0/0' int='atm' />
this says that the interface atm is the default route out
synonyms can equate to anything.
<synonym alias='any' real='0-0' />
groups and synonyms are different namespaces - no clash
<synonym alias='unpriv' real='gt1023' />
<acl act='permit' proto='tcp' src='any' srcprt='unpriv'
dst='http' dstprt='80' />
<acl act='permit' proto='tcp' src='any' srcprt='unpriv'
dst='https' dstprt='443' />
<acl act='permit' proto='tcp' src='gr_vlan2,gr_vlan4,gr_vlan5' srcprt='any'
dst='SMTPrelays' dstprt='25' />
<acl act='permit' proto='udp' src='snmp_nms' srcprt='any'
dst='gr_vlan2,gr_vlan5' dstprt='161' />
<acl act='permit' proto='udp' src='gr_vlan2,gr_vlan5' srcprt='any'
dst='snmp_nms' dstprt='162' no_return='y' />
snmp trivia - traps are not acknowledged
</acls>
Comments can appear within a file as bare text. Not a feature of XML in general, but of the fact that in this project, all the information is contained in attributes.
Comments could also be contained between <!-- and --> sequences. The bad news about
these is they DON'T NEST, eg if you put a comment of this sort inside
another comment of this sort, your typical XML parser will puke.
For this program, whitespace (spaces, tabs, and end-of-line sequences) can occur anywhere between except:
The XML above produces this output:
no ip access-list extended vlan2_in
no ip access-list extended vlan4_in
no ip access-list extended vlan5_in
no ip access-list extended atm_in
ip access-list extended vlan2_in
permit tcp host 192.168.129.8 eq 80 any gt 1023 established
permit tcp host 192.168.129.9 eq 80 any gt 1023 established
permit tcp host 192.168.129.8 eq 443 any gt 1023 established
permit tcp host 192.168.129.9 eq 443 any gt 1023 established
permit tcp host 192.168.129.8 host 172.21.34.34 eq 25
permit tcp host 192.168.129.8 host 172.23.14.10 eq 25
permit tcp host 192.168.129.9 host 172.21.34.34 eq 25
permit tcp host 192.168.129.9 host 172.23.14.10 eq 25
permit udp host 192.168.129.8 eq 161 host 192.168.129.76
permit udp host 192.168.129.9 eq 161 host 192.168.129.76
permit udp host 192.168.129.8 host 192.168.129.76 eq 162
permit udp host 192.168.129.9 host 192.168.129.76 eq 162
deny ip any any log
ip access-list extended vlan4_in
permit tcp host 192.168.129.76 host 172.21.34.34 eq 25
permit tcp host 192.168.129.76 host 172.23.14.10 eq 25
permit udp host 192.168.129.76 host 192.168.129.8 eq 161
permit udp host 192.168.129.76 host 192.168.129.9 eq 161
permit udp host 192.168.129.76 host 192.168.129.100 eq 161
permit udp host 192.168.129.76 host 192.168.129.101 eq 161
deny ip any any log
ip access-list extended vlan5_in
permit tcp host 192.168.129.100 eq 80 any gt 1023 established
permit tcp host 192.168.129.101 eq 80 any gt 1023 established
permit tcp host 192.168.129.100 host 172.21.34.34 eq 25
permit tcp host 192.168.129.100 host 172.23.14.10 eq 25
permit tcp host 192.168.129.101 host 172.21.34.34 eq 25
permit tcp host 192.168.129.101 host 172.23.14.10 eq 25
permit udp host 192.168.129.100 eq 161 host 192.168.129.76
permit udp host 192.168.129.101 eq 161 host 192.168.129.76
permit udp host 192.168.129.100 host 192.168.129.76 eq 162
permit udp host 192.168.129.101 host 192.168.129.76 eq 162
deny ip any any log
ip access-list extended atm_in
permit tcp any gt 1023 host 192.168.129.8 eq 80
permit tcp any gt 1023 host 192.168.129.9 eq 80
permit tcp any gt 1023 host 192.168.129.100 eq 80
permit tcp any gt 1023 host 192.168.129.101 eq 80
permit tcp any gt 1023 host 192.168.129.8 eq 443
permit tcp any gt 1023 host 192.168.129.9 eq 443
permit tcp host 172.21.34.34 eq 25 host 192.168.129.8 established
permit tcp host 172.23.14.10 eq 25 host 192.168.129.8 established
permit tcp host 172.21.34.34 eq 25 host 192.168.129.9 established
permit tcp host 172.23.14.10 eq 25 host 192.168.129.9 established
permit tcp host 172.21.34.34 eq 25 host 192.168.129.76 established
permit tcp host 172.23.14.10 eq 25 host 192.168.129.76 established
permit tcp host 172.21.34.34 eq 25 host 192.168.129.100 established
permit tcp host 172.23.14.10 eq 25 host 192.168.129.100 established
permit tcp host 172.21.34.34 eq 25 host 192.168.129.101 established
permit tcp host 172.23.14.10 eq 25 host 192.168.129.101 established
deny ip any any log
end
Below are listed the different elements.
<host name='something' ip='an_IP_address_or_range' />This 'empty' element is more or less like a line in the /etc/hosts file - it equates a name with an IP address. It is different in that it can also equate a name to an IP network. Valid formats looks like: '192.168.5.4' or '192.168.5.0/24' or '192.168.5.0 255.255.255.0'.
Most of the time, you will want to use this only for real hosts, because there is an automatic function to create a group of all the hosts on a given network. If you've put something that matches such a group as a host entry, then when you write ACLs against that group, you'll see weird stuff in the output.
Instead, use <group .../> elements of 1 member to alias multiple names to a host, or
to name an IP range so it won't get autogrouped.
<host name='something' ip='an_IP_address_or_range' flags='no_autogroup' />Same as above, but with the flags attribute. The flags attribute is optional. As of now, the only defined value is no_autogroup, which prevents the inclusion of this host in autogrouping by network (see the <net.../> element below).
Back to Top
<group name='something' members='CSV_list_of_IP_or_hostname_or_groupnames' />There are 2 forms of the group element. This is the 'empty' or 'one-liner' version. The name is a name that can be used as a member in other group definitions, or as the src or dst in an acl element. members is one or items as a comma separated list. These items can be other (already defined) group names, or anything that's valid in the IP attribute of a host element. These items will be evaluated recursively to ensure that these conditions are met.
Back to Top
<group name='something'> <member name='something_else'/> </group>This is an element that contains member elements. The member elements serve the same purpose as the members attribute, so the following snippets are equivalent:
<group name='httpservers' members='server1,server2,server3'/>
and
<group name='httpservers'>
<member name='server1'/>
<member name='server2'/>
<member name='server3'/>
</group>`
and
<group name='httpservers' members='server1'>
<member name='server2'/>
<member name='server3'/>
</group>
are all the same group.
The values of the name attribute in a member element is subject to the same constraints as the values of members attribute in the group element.
Back to Top
<net name='interface_name' ip='IP_address_range' int='other_interface_name' />This element is critically important. The program uses the information in the <net ... /> elements to determine which interfaces the endpoints of an ACL are on.
The 'int' attribute is optional (except in the <net name='DEFAULT".../> element). It allows
the network range to be assigned to a different interface. This is useful when you want
to tell the program where the default route is,which is where stuff to/from networks not defined
leave/enter the router, or to use the auto-grouping feature for a network to
which the router doesn't directly connected.
Auto-grouping is a convenience feature. When a <net.../> tag is processed
and a <net name='DEFAULT" ... /> exists, then groups are created for each net name.
The groups are named with the net name perpended with 'gr_' and contain as their members
the names of all the host elements whose IP entries fall within the IP range for that interface.
Example:
<net name='vlan1' ip='192.168.129.0/27' />
<net name='vlan2' ip='192.168.129.32/27' />
<net name='atm' ip='192.168.254.28/29' />
<net name='vlan6' ip='192.168.169.32/27' int='atm'/>
<net name='DEFAULT' ip='0.0.0.0/0' int='atm' />
Access lists will be created named vlan1_in, vlan2_in and atm_in. Endpoints falling
into IP address ranges not defined in a <net ... /> entry (other than the DEFAULT entry)
will be considered as connected via the atm interface because of
the int='atm' in the <net name='DEFAULT'.../> entry. Endpoints falling in the vlan6 range
will also be considered as connected via interface atm, due to the int='atm' in the
<net name='vlan6'... /> entry.
A group named 'gr_vlan1' to be auto-created that will contain
the ip attribute of each host element whose ip attribute was in the range 192.168.129.1-192.168.129.31
at the time the <net name='DEFAULT' .../> tag was processed.
A group named 'gr_vlan2' to be auto-created that will contain the ip attribute of each
host element whose ip attribute was in the range 192.168.129.33-192.168.129.63,
at the time the <net name='DEFAULT' .../> was processed.
A group named 'gr_vlan6' will be auto-created that will contain the ip attributes of all host elements
whose ip attributes were in the range 192.168.169.33-192.168.169.63
at the time the <net name='DEFAULT' .../> was processed.
If you have a host that falls into the range of one of the nets defined in the
<net .../> elements, but you don't want it in the auto-created groups, you can:
<host .../> entry in AFTER the <net name='DEFAULT' .../> element.
Of course, this means that anything that refers to that host by name must also occur
after this <host.../> element.
The 'DEFAULT' entry is important. It tells the program you're finished defining nets. It tells the program where to send stuff that you haven't defined nets for, by using the int attribute.
The DEFAULT net entry, which MUST be the last <net.../> entry, will always look like this:
<net name='DEFAULT' ip='0.0.0.0/0' int='some_interface' />
Except that the <net name='DEFAULT'.../> MUST be the final <net../> entry,
the order of the <net .../> entries isn't important, as they will be sorted so that
testing will match the most-specific net (longest netmask) first.
<synonym alias='something' real='something_else'/>These are used to alias one text string for another. The alias can be chose arbitrarily. synonym aliases and group/host names are different namespaces, so 'any' as a group or host name doesn't clash with 'any' as the alias of a synonym. This is because groups and hosts are used for endpoints, where as synonyms are used for other attributes (ports and ints ) of the acl entry. The 'real' attribute can be pretty much anything. When used in an attribute of an <acl ...> element, it will be expanded recursively if it is the alias attribute of a previously defined synonym. If it contains commas, it is treated as a list of literals or aliases to be expanded.
Back to Top
<acl .../>Finally, the reason we're doing all this. several forms exist.
<acl act='plug'..../> - the degenerate formThe crudest of these is:
<acl act='plug' proto='literal ACL expression' int='interfaces_to_apply_this_to' />
This form is for ACL forms I haven't written the code to handle yet. It could also be used to put comments into the ACLs output file. The value of the proto attribute is literally applied to any interfaces listed in the int attribute.
The value of the int attribute can be a single interface, an alias name, or a CSV list of either of both. Aliases are expanded recursively here.
No validation of any sort is performed on the proto statement.
Back to Top
<acl ... /> - the normal formThe next form of the <acl .../> element looks like:
<acl act='permit' proto='tcp' src='endpoint_spec' srcprt='portspec'
dst='other_endpoint_spec' dstprt='other_port_spec'
int='int_spec' no_return='y' non_reflexive='y' return_only='y' />
The attributes are defined as follows:
this is 'permit' or 'deny'
this is actually anything that can fit the format. if 'tcp', then in non-reflexive mode, the return path will have the 'established' key word added. Otherwise, not tested.
These are the endpoints. they can be an IP address, a range of same, a hostname, or a group name or a CSV list of any combination of these.
ports for ACL. if '0-0' or synonym therefore, none specified in ACL. if num1-num2, then ACL will contain 'range num1 num2' if 'num1', 'eq num1' in ACL. if 'eqNUM' or 'gtNUM' or 'ltNUM' or 'neqNUM', the expected text is created. a port spec can be a CSV list of any of these, plus aliases, but only one of srcprt and dstprt can be (or expand to) a list. The author thinks that a list in both places is too weird.
Back to Top
Normally, the program uses the ip address information for each src and dst to figure out where the ACL entries go. This attribute overrides the interface where the source is considered to be connected. Useful for when the source is 0.0.0.0/0 (typically aliased to 'any'), but the ACL is to be applied to other than the DEFAULT interface. This can be an interface name, an alias, or a CSV of either or both. Aliases are expanded recursively.
This is used to suppress the return direction. Useful when the target is contained within the router for which ACLs are being generated (such as SNMP or HSRP) and the return ACL would be on the same interface as the forward ACL and would be gibberish. return_only Only make the ACL from dst to src.
Used to create only the return direction (from dst to src).
Even if the command-line switch for reflexive is specified, do this ACL non-reflexively. If you think this connection can pass through this router in one direction but not the other, use this.
This is a CSV list of flags. A flag can be just a flag whose presence does its job, or if can
be in the form flag_name:flag_value.
flag_name and flag_value can contain neither commas nor colons (:).
The only flag currently defined for the <acl ... /> element is
the 'comment', in the form: flags='comment:comment_text_for_ACL_output_here. If the commant text
doesn't start with a ! symbol, a 2 character '! ' sequence will be prepended. This comment will then
precede each ACL entry, forward or reverse, created by this <acl ... /> element.
<IGNORE> and </IGNORE>Any elements between these element will be ignored.
Unlike XML comment
delimiters <!-- and --> these nest,
the outermost pair define the start/end of 'ignoring'.
Note that if you use a <IGNORE> ... </IGNORE> sequence, the text contained between them must be valid XML or comments.
This hasn't been tested much in reflexive mode.
Empty ACLs are created for interfaces even if their networks were forced.
If you have an <acl ... /> element for which both src and dst are external via forcing of their networks to the same interface, both the src and dst entries will appear in the ACL for that interface. This is sort of undesireable, since in this case, legitimate traffic wouldn't typically flow this way, plus at least one of the entries looks silly. Someday I'll figure out correct logic to deal with this.
Send bug reports, hints, tips, suggestions to Bob Niederman at: bob@bob-n.com
|
aclxml2.pl - Produce Cisco ACLs from XML definition files. |