Zone Based IPV4 Firewall Generator via Python

I’ve been using zoned based firewalls with increasing granularity. This has caused a spike in the number of zones and “copied” rules.

I read that an undocumented firewall is not a firewall, so I’ve been documenting by using a shorthand then using that to translate in my head what to write.

Being human I made a bunch of mistakes translating my own shorthand, so for my first python script I made a routine to parse my shorthand and write the firewall commands for me.

I’d like to post the script here for anyone who is interested, it has saved me a bunch of time and allows me to make significant changes to a large firewall fairly quickly.

It’s based on everything being in a firewall group, so you’ll need to create your port/ip/network groups in addition to this, and also to define your zones.

The following basic shorthand represents rules from the WAN0 zone to other zones (WAN0 to CA_LAN will be the best example of a zone pair):

WAN0:
to LOCAL: STOCK, IPSEC-IKE-ESP, [DT-REMOTE-NET](CA_LAN-NET|MATCH_IPSEC)  
to MGMT_LAN: STOCK
to CA_LAN: STOCK, <HTTPS-TCP>(ZPUSH-IP), <HTTPS-TCP>(ZIMBRA-PROXY-IP), [SPAM-FILTER-IP]<SMTP-TCP>(ZIMBRA-SERVER-IP), <SMTP-SUBMISSION-TCP>(ZIMBRA-SERVER-IP), <IMAPS-TCP>(ZIMBRA-PROXY-IP), <IT-OVPN-UDP>(IT-OVPN-IP), <UNIV-OVPN-UDP>(UNIV-OVPN-IP),  [DT-REMOTE-NET](CA_LAN-NET|MATCH_IPSEC)
to CA_LAN2: STOCK
to CA_WRKSHP: STOCK
to TSLA_LAN: STOCK
to BMW_ISPI: STOCK
to VOIP: STOCK
to VLVO_CAR_WIFI: STOCK
to VLVO_CUST_WIFI: STOCK
to MEDIA_LAN: STOCK
to SU_LAN: STOCK

The resulting commands after running it through the python script is:

echo =====WAN0 to LOCAL=====
echo delete firewall name WAN0.LOCAL
set firewall name WAN0.LOCAL description "WAN0 to LOCAL: STOCK, IPSEC-IKE-ESP, [DT-REMOTE-NET](CA_LAN-NET|MATCH_IPSEC)"
set firewall name WAN0.LOCAL rule 100 description "Allow established/related"
set firewall name WAN0.LOCAL rule 100 action accept
set firewall name WAN0.LOCAL rule 100 state established enable
set firewall name WAN0.LOCAL rule 100 state related enable
set firewall name WAN0.LOCAL rule 100 log disable
set firewall name WAN0.LOCAL rule 105 description "Drop invalid"
set firewall name WAN0.LOCAL rule 105 action drop
set firewall name WAN0.LOCAL rule 105 state invalid enable
set firewall name WAN0.LOCAL rule 105 log disable
set firewall name WAN0.LOCAL rule 110 description "IPSEC IKE/ESP/NAT-T Group; Accept IKE"
set firewall name WAN0.LOCAL rule 110 action accept
set firewall name WAN0.LOCAL rule 110 destination port 500
set firewall name WAN0.LOCAL rule 110 protocol udp
set firewall name WAN0.LOCAL rule 110 log enable
set firewall name WAN0.LOCAL rule 115 description "Accept ESP"
set firewall name WAN0.LOCAL rule 115 action accept
set firewall name WAN0.LOCAL rule 115 protocol esp
set firewall name WAN0.LOCAL rule 115 log enable
set firewall name WAN0.LOCAL rule 120 description "Accept NAT-T"
set firewall name WAN0.LOCAL rule 120 action accept
set firewall name WAN0.LOCAL rule 120 destination port 4500
set firewall name WAN0.LOCAL rule 120 protocol udp
set firewall name WAN0.LOCAL rule 120 log enable
set firewall name WAN0.LOCAL rule 125 description "from DT-REMOTE-NET to CA_LAN-NET"
set firewall name WAN0.LOCAL rule 125 action accept
set firewall name WAN0.LOCAL rule 125 source group network-group DT-REMOTE-NET
set firewall name WAN0.LOCAL rule 125 destination group network-group CA_LAN-NET
set firewall name WAN0.LOCAL rule 125 ipsec match-ipsec
set firewall name WAN0.LOCAL rule 125 state new enable
set firewall name WAN0.LOCAL rule 125 log enable
set firewall name WAN0.LOCAL enable-default-log
set zone-policy zone LOCAL from WAN0 firewall name WAN0.LOCAL
echo =====WAN0.LOCAL Rules Generated 2022-07-20 19:13:10.297058=====


echo =====WAN0 to MGMT_LAN=====
echo delete firewall name WAN0.MGMT_LAN
set firewall name WAN0.MGMT_LAN description "WAN0 to MGMT_LAN: STOCK"
set firewall name WAN0.MGMT_LAN rule 100 description "Allow established/related"
set firewall name WAN0.MGMT_LAN rule 100 action accept
set firewall name WAN0.MGMT_LAN rule 100 state established enable
set firewall name WAN0.MGMT_LAN rule 100 state related enable
set firewall name WAN0.MGMT_LAN rule 100 log disable
set firewall name WAN0.MGMT_LAN rule 105 description "Drop invalid"
set firewall name WAN0.MGMT_LAN rule 105 action drop
set firewall name WAN0.MGMT_LAN rule 105 state invalid enable
set firewall name WAN0.MGMT_LAN rule 105 log disable
set firewall name WAN0.MGMT_LAN enable-default-log
set zone-policy zone MGMT_LAN from WAN0 firewall name WAN0.MGMT_LAN
echo =====WAN0.MGMT_LAN Rules Generated 2022-07-20 19:13:10.297058=====


echo =====WAN0 to CA_LAN=====
echo delete firewall name WAN0.CA_LAN
set firewall name WAN0.CA_LAN description "WAN0 to CA_LAN: STOCK, <HTTPS-TCP>(ZPUSH-IP), <HTTPS-TCP>(ZIMBRA-PROXY-IP), [SPAM-FILTER-IP]<SMTP-TCP>(ZIMBRA-SERVER-IP), <SMTP-SUBMISSION-TCP>(ZIMBRA-SERVER-IP), <IMAPS-TCP>(ZIMBRA-PROXY-IP), <IT-OVPN-UDP>(IT-OVPN-IP), <UNIV-OVPN-UDP>(UNIV-OVPN-IP),  [DT-REMOTE-NET](CA_LAN-NET|MATCH_IPSEC)"
set firewall name WAN0.CA_LAN rule 100 description "Allow established/related"
set firewall name WAN0.CA_LAN rule 100 action accept
set firewall name WAN0.CA_LAN rule 100 state established enable
set firewall name WAN0.CA_LAN rule 100 state related enable
set firewall name WAN0.CA_LAN rule 100 log disable
set firewall name WAN0.CA_LAN rule 105 description "Drop invalid"
set firewall name WAN0.CA_LAN rule 105 action drop
set firewall name WAN0.CA_LAN rule 105 state invalid enable
set firewall name WAN0.CA_LAN rule 105 log disable
set firewall name WAN0.CA_LAN rule 110 description "to HTTPS-TCP to ZPUSH-IP"
set firewall name WAN0.CA_LAN rule 110 action accept
set firewall name WAN0.CA_LAN rule 110 destination group port-group HTTPS-TCP
set firewall name WAN0.CA_LAN rule 110 protocol tcp
set firewall name WAN0.CA_LAN rule 110 destination group address-group ZPUSH-IP
set firewall name WAN0.CA_LAN rule 110 state new enable
set firewall name WAN0.CA_LAN rule 110 log enable
set firewall name WAN0.CA_LAN rule 115 description "to HTTPS-TCP to ZIMBRA-PROXY-IP"
set firewall name WAN0.CA_LAN rule 115 action accept
set firewall name WAN0.CA_LAN rule 115 destination group port-group HTTPS-TCP
set firewall name WAN0.CA_LAN rule 115 protocol tcp
set firewall name WAN0.CA_LAN rule 115 destination group address-group ZIMBRA-PROXY-IP
set firewall name WAN0.CA_LAN rule 115 state new enable
set firewall name WAN0.CA_LAN rule 115 log enable
set firewall name WAN0.CA_LAN rule 120 description "from SPAM-FILTER-IP to SMTP-TCP to ZIMBRA-SERVER-IP"
set firewall name WAN0.CA_LAN rule 120 action accept
set firewall name WAN0.CA_LAN rule 120 source group address-group SPAM-FILTER-IP
set firewall name WAN0.CA_LAN rule 120 destination group port-group SMTP-TCP
set firewall name WAN0.CA_LAN rule 120 protocol tcp
set firewall name WAN0.CA_LAN rule 120 destination group address-group ZIMBRA-SERVER-IP
set firewall name WAN0.CA_LAN rule 120 state new enable
set firewall name WAN0.CA_LAN rule 120 log enable
set firewall name WAN0.CA_LAN rule 125 description "to SMTP-SUBMISSION-TCP to ZIMBRA-SERVER-IP"
set firewall name WAN0.CA_LAN rule 125 action accept
set firewall name WAN0.CA_LAN rule 125 destination group port-group SMTP-SUBMISSION-TCP
set firewall name WAN0.CA_LAN rule 125 protocol tcp
set firewall name WAN0.CA_LAN rule 125 destination group address-group ZIMBRA-SERVER-IP
set firewall name WAN0.CA_LAN rule 125 state new enable
set firewall name WAN0.CA_LAN rule 125 log enable
set firewall name WAN0.CA_LAN rule 130 description "to IMAPS-TCP to ZIMBRA-PROXY-IP"
set firewall name WAN0.CA_LAN rule 130 action accept
set firewall name WAN0.CA_LAN rule 130 destination group port-group IMAPS-TCP
set firewall name WAN0.CA_LAN rule 130 protocol tcp
set firewall name WAN0.CA_LAN rule 130 destination group address-group ZIMBRA-PROXY-IP
set firewall name WAN0.CA_LAN rule 130 state new enable
set firewall name WAN0.CA_LAN rule 130 log enable
set firewall name WAN0.CA_LAN rule 135 description "to IT-OVPN-UDP to IT-OVPN-IP"
set firewall name WAN0.CA_LAN rule 135 action accept
set firewall name WAN0.CA_LAN rule 135 destination group port-group IT-OVPN-UDP
set firewall name WAN0.CA_LAN rule 135 protocol udp
set firewall name WAN0.CA_LAN rule 135 destination group address-group IT-OVPN-IP
set firewall name WAN0.CA_LAN rule 135 state new enable
set firewall name WAN0.CA_LAN rule 135 log enable
set firewall name WAN0.CA_LAN rule 140 description "to UNIV-OVPN-UDP to UNIV-OVPN-IP"
set firewall name WAN0.CA_LAN rule 140 action accept
set firewall name WAN0.CA_LAN rule 140 destination group port-group UNIV-OVPN-UDP
set firewall name WAN0.CA_LAN rule 140 protocol udp
set firewall name WAN0.CA_LAN rule 140 destination group address-group UNIV-OVPN-IP
set firewall name WAN0.CA_LAN rule 140 state new enable
set firewall name WAN0.CA_LAN rule 140 log enable
set firewall name WAN0.CA_LAN rule 145 description "from DT-REMOTE-NET to CA_LAN-NET"
set firewall name WAN0.CA_LAN rule 145 action accept
set firewall name WAN0.CA_LAN rule 145 source group network-group DT-REMOTE-NET
set firewall name WAN0.CA_LAN rule 145 destination group network-group CA_LAN-NET
set firewall name WAN0.CA_LAN rule 145 ipsec match-ipsec
set firewall name WAN0.CA_LAN rule 145 state new enable
set firewall name WAN0.CA_LAN rule 145 log enable
set firewall name WAN0.CA_LAN enable-default-log
set zone-policy zone CA_LAN from WAN0 firewall name WAN0.CA_LAN
echo =====WAN0.CA_LAN Rules Generated 2022-07-20 19:13:10.297058=====


echo =====WAN0 to CA_LAN2=====
echo delete firewall name WAN0.CA_LAN2
set firewall name WAN0.CA_LAN2 description "WAN0 to CA_LAN2: STOCK"
set firewall name WAN0.CA_LAN2 rule 100 description "Allow established/related"
set firewall name WAN0.CA_LAN2 rule 100 action accept
set firewall name WAN0.CA_LAN2 rule 100 state established enable
set firewall name WAN0.CA_LAN2 rule 100 state related enable
set firewall name WAN0.CA_LAN2 rule 100 log disable
set firewall name WAN0.CA_LAN2 rule 105 description "Drop invalid"
set firewall name WAN0.CA_LAN2 rule 105 action drop
set firewall name WAN0.CA_LAN2 rule 105 state invalid enable
set firewall name WAN0.CA_LAN2 rule 105 log disable
set firewall name WAN0.CA_LAN2 enable-default-log
set zone-policy zone CA_LAN2 from WAN0 firewall name WAN0.CA_LAN2
echo =====WAN0.CA_LAN2 Rules Generated 2022-07-20 19:13:10.297058=====


echo =====WAN0 to CA_WRKSHP=====
echo delete firewall name WAN0.CA_WRKSHP
set firewall name WAN0.CA_WRKSHP description "WAN0 to CA_WRKSHP: STOCK"
set firewall name WAN0.CA_WRKSHP rule 100 description "Allow established/related"
set firewall name WAN0.CA_WRKSHP rule 100 action accept
set firewall name WAN0.CA_WRKSHP rule 100 state established enable
set firewall name WAN0.CA_WRKSHP rule 100 state related enable
set firewall name WAN0.CA_WRKSHP rule 100 log disable
set firewall name WAN0.CA_WRKSHP rule 105 description "Drop invalid"
set firewall name WAN0.CA_WRKSHP rule 105 action drop
set firewall name WAN0.CA_WRKSHP rule 105 state invalid enable
set firewall name WAN0.CA_WRKSHP rule 105 log disable
set firewall name WAN0.CA_WRKSHP enable-default-log
set zone-policy zone CA_WRKSHP from WAN0 firewall name WAN0.CA_WRKSHP
echo =====WAN0.CA_WRKSHP Rules Generated 2022-07-20 19:13:10.297058=====


echo =====WAN0 to TSLA_LAN=====
echo delete firewall name WAN0.TSLA_LAN
set firewall name WAN0.TSLA_LAN description "WAN0 to TSLA_LAN: STOCK"
set firewall name WAN0.TSLA_LAN rule 100 description "Allow established/related"
set firewall name WAN0.TSLA_LAN rule 100 action accept
set firewall name WAN0.TSLA_LAN rule 100 state established enable
set firewall name WAN0.TSLA_LAN rule 100 state related enable
set firewall name WAN0.TSLA_LAN rule 100 log disable
set firewall name WAN0.TSLA_LAN rule 105 description "Drop invalid"
set firewall name WAN0.TSLA_LAN rule 105 action drop
set firewall name WAN0.TSLA_LAN rule 105 state invalid enable
set firewall name WAN0.TSLA_LAN rule 105 log disable
set firewall name WAN0.TSLA_LAN enable-default-log
set zone-policy zone TSLA_LAN from WAN0 firewall name WAN0.TSLA_LAN
echo =====WAN0.TSLA_LAN Rules Generated 2022-07-20 19:13:10.297058=====


echo =====WAN0 to BMW_ISPI=====
echo delete firewall name WAN0.BMW_ISPI
set firewall name WAN0.BMW_ISPI description "WAN0 to BMW_ISPI: STOCK"
set firewall name WAN0.BMW_ISPI rule 100 description "Allow established/related"
set firewall name WAN0.BMW_ISPI rule 100 action accept
set firewall name WAN0.BMW_ISPI rule 100 state established enable
set firewall name WAN0.BMW_ISPI rule 100 state related enable
set firewall name WAN0.BMW_ISPI rule 100 log disable
set firewall name WAN0.BMW_ISPI rule 105 description "Drop invalid"
set firewall name WAN0.BMW_ISPI rule 105 action drop
set firewall name WAN0.BMW_ISPI rule 105 state invalid enable
set firewall name WAN0.BMW_ISPI rule 105 log disable
set firewall name WAN0.BMW_ISPI enable-default-log
set zone-policy zone BMW_ISPI from WAN0 firewall name WAN0.BMW_ISPI
echo =====WAN0.BMW_ISPI Rules Generated 2022-07-20 19:13:10.297058=====


echo =====WAN0 to VOIP=====
echo delete firewall name WAN0.VOIP
set firewall name WAN0.VOIP description "WAN0 to VOIP: STOCK"
set firewall name WAN0.VOIP rule 100 description "Allow established/related"
set firewall name WAN0.VOIP rule 100 action accept
set firewall name WAN0.VOIP rule 100 state established enable
set firewall name WAN0.VOIP rule 100 state related enable
set firewall name WAN0.VOIP rule 100 log disable
set firewall name WAN0.VOIP rule 105 description "Drop invalid"
set firewall name WAN0.VOIP rule 105 action drop
set firewall name WAN0.VOIP rule 105 state invalid enable
set firewall name WAN0.VOIP rule 105 log disable
set firewall name WAN0.VOIP enable-default-log
set zone-policy zone VOIP from WAN0 firewall name WAN0.VOIP
echo =====WAN0.VOIP Rules Generated 2022-07-20 19:13:10.297058=====


echo =====WAN0 to VLVO_CAR_WIFI=====
echo delete firewall name WAN0.VLVO_CAR_WIFI
set firewall name WAN0.VLVO_CAR_WIFI description "WAN0 to VLVO_CAR_WIFI: STOCK"
set firewall name WAN0.VLVO_CAR_WIFI rule 100 description "Allow established/related"
set firewall name WAN0.VLVO_CAR_WIFI rule 100 action accept
set firewall name WAN0.VLVO_CAR_WIFI rule 100 state established enable
set firewall name WAN0.VLVO_CAR_WIFI rule 100 state related enable
set firewall name WAN0.VLVO_CAR_WIFI rule 100 log disable
set firewall name WAN0.VLVO_CAR_WIFI rule 105 description "Drop invalid"
set firewall name WAN0.VLVO_CAR_WIFI rule 105 action drop
set firewall name WAN0.VLVO_CAR_WIFI rule 105 state invalid enable
set firewall name WAN0.VLVO_CAR_WIFI rule 105 log disable
set firewall name WAN0.VLVO_CAR_WIFI enable-default-log
set zone-policy zone VLVO_CAR_WIFI from WAN0 firewall name WAN0.VLVO_CAR_WIFI
echo =====WAN0.VLVO_CAR_WIFI Rules Generated 2022-07-20 19:13:10.297058=====


echo =====WAN0 to VLVO_CUST_WIFI=====
echo delete firewall name WAN0.VLVO_CUST_WIFI
set firewall name WAN0.VLVO_CUST_WIFI description "WAN0 to VLVO_CUST_WIFI: STOCK"
set firewall name WAN0.VLVO_CUST_WIFI rule 100 description "Allow established/related"
set firewall name WAN0.VLVO_CUST_WIFI rule 100 action accept
set firewall name WAN0.VLVO_CUST_WIFI rule 100 state established enable
set firewall name WAN0.VLVO_CUST_WIFI rule 100 state related enable
set firewall name WAN0.VLVO_CUST_WIFI rule 100 log disable
set firewall name WAN0.VLVO_CUST_WIFI rule 105 description "Drop invalid"
set firewall name WAN0.VLVO_CUST_WIFI rule 105 action drop
set firewall name WAN0.VLVO_CUST_WIFI rule 105 state invalid enable
set firewall name WAN0.VLVO_CUST_WIFI rule 105 log disable
set firewall name WAN0.VLVO_CUST_WIFI enable-default-log
set zone-policy zone VLVO_CUST_WIFI from WAN0 firewall name WAN0.VLVO_CUST_WIFI
echo =====WAN0.VLVO_CUST_WIFI Rules Generated 2022-07-20 19:13:10.297058=====


echo =====WAN0 to MEDIA_LAN=====
echo delete firewall name WAN0.MEDIA_LAN
set firewall name WAN0.MEDIA_LAN description "WAN0 to MEDIA_LAN: STOCK"
set firewall name WAN0.MEDIA_LAN rule 100 description "Allow established/related"
set firewall name WAN0.MEDIA_LAN rule 100 action accept
set firewall name WAN0.MEDIA_LAN rule 100 state established enable
set firewall name WAN0.MEDIA_LAN rule 100 state related enable
set firewall name WAN0.MEDIA_LAN rule 100 log disable
set firewall name WAN0.MEDIA_LAN rule 105 description "Drop invalid"
set firewall name WAN0.MEDIA_LAN rule 105 action drop
set firewall name WAN0.MEDIA_LAN rule 105 state invalid enable
set firewall name WAN0.MEDIA_LAN rule 105 log disable
set firewall name WAN0.MEDIA_LAN enable-default-log
set zone-policy zone MEDIA_LAN from WAN0 firewall name WAN0.MEDIA_LAN
echo =====WAN0.MEDIA_LAN Rules Generated 2022-07-20 19:13:10.297058=====


echo =====WAN0 to SU_LAN=====
echo delete firewall name WAN0.SU_LAN
set firewall name WAN0.SU_LAN description "WAN0 to SU_LAN: STOCK"
set firewall name WAN0.SU_LAN rule 100 description "Allow established/related"
set firewall name WAN0.SU_LAN rule 100 action accept
set firewall name WAN0.SU_LAN rule 100 state established enable
set firewall name WAN0.SU_LAN rule 100 state related enable
set firewall name WAN0.SU_LAN rule 100 log disable
set firewall name WAN0.SU_LAN rule 105 description "Drop invalid"
set firewall name WAN0.SU_LAN rule 105 action drop
set firewall name WAN0.SU_LAN rule 105 state invalid enable
set firewall name WAN0.SU_LAN rule 105 log disable
set firewall name WAN0.SU_LAN enable-default-log
set zone-policy zone SU_LAN from WAN0 firewall name WAN0.SU_LAN
echo =====WAN0.SU_LAN Rules Generated 2022-07-20 19:13:10.297058=====

Here you can see that the description contains the original shorthand so you can verify it quickly. The echo line with the title is to give it structure for easier browsing while allowing you to copy and paste without worrying about errors from the title. The “echo delete” is in case you want to start over for that set of rules, you can just remove the echo from “echo delete” everwhere to rebuild the whole firewall or just on one group if you’re only updating a single zone pair. The date generated is included so you can see when a group was last updated since you’ll likely update single zone pairs more often that the whole firewall. Maybe I should be put the generated date in the description too???

Here is a rough overview of the rules involved in writing the shorthand:

==Shorthand Syntax Rules==
Brackets define whether it's a source port/ip or destination port/ip. Options and preconfigured rules are allowed.
Defaults for <destination-port>,(destination-ip),{source-port},[source-ip] rules unless otherwise specified:
action = accept
state = new enable (if you specify any state then you must specify the new state as well since it will be removed with the addition of other states, per rule that is)
log = enable

Zone names must be 14 characters or less, be all caps and use only the characters A-Z, 0-9, -,_

Rule names must be 31 characters or less, be all caps and use only the characters A-Z, 0-9, -, _, !

Rules for port groups must end in: -TCP, -UDP, -TCP_UDP
Rules for ip address groups must end in -IP
Rules for network addresss groups must end in -NET

SYNTAX:
SRC_ZONE:
to DST_ZONE_A: [SOURCE-IP]{SOURCE-PORT-TCP}<DESTINATION-TCP>(DESTINATION-IP), [SOURCE-IP|OPTION_A;OPTION_B;OPTION_C]
to DST_ZONE_B: [!SOURCE-IP]{SOURCE-PORT-TCP}<!DESTINATION-TCP>(DESTINATION-IP), [SOURCE-IP|OPTION_A;OPTION_B;OPTION_C]

Preconfigured Rules (preconfigured rules don't go inside brackets):
STOCK = allow establised/related, drop invalid, no log
PING = allow ICMP echo, no log
OSPF = allow OSPF, no log

! means NOT

Options:
NOLOG = log disable
LOG = log enable
DROP = action drop
REJECT = action reject
ACCEPT = action accept
ST_ESTB_ENBL = state established enable
ST_ESTB_DSBL = state established disable
ST_RLTD_ENBL = state related enable
ST_RLTD_DSBL = state related disable
ST_INVLD_ENBL = state invalid enable
ST_INVLD_DSBL = state invalid disable
ST_NEW_ENBL = state new disable
ST_NEW_DSBL = state new disable
MATCH-IPSEC = ipsec match-ipsec

I’ve setup extensive error checking in the script to validate the shorthand so that human error will be noticed right away. Things like if you put 2 separate destination port rules in the same rule #, or if you have conflicting options. Most of the error checking is documented in the script. Also, as it’s my first python script it includes all the stack exchange/etc references I used to cobble it together. And it is cobbled together, not as good a peach cobbler but it’s fit for consumption.

Before I posted it I wanted to make sure this is acceptable for this forum. And if allowed should I post as code or attach it as txt file? It’s just under 900 lines long.

4 Likes

When I posted this originally I had hoped to cleanup the code and post it here… I’ve been too busy using it. It makes changing things trivial and adds a good amount of confidence that I didn’t fat finger anything. Documenting a firewall is now easy and I can make very granular changes without worrying about breaking a bunch of other rules.

So I’ll post it here in it’s current state. If you have any questions I’ll answer them the next time I’m sick and am tired of reading the news…

As an example, a set of shorthand rules adding up to 1800 lines when run through the script generates about 18,000 lines. 1,800 lines of simple text is easier to read and audit than 18,000 lines of commands.

Firewall groups are used to make this possible, so there is a group for everything. Below is an example of just one part of a set of shorthand rules. The first line is the “from zone” and the rest are the “to zones”. If you want to put a comment or prevent a zone from being included in the rule generation just comment it out with #. I keep all zones listed in every group even if they’re not used so I can use them later with little fuss if needed, until then I comment them out.

Example Shorthand:

WRKSTN:
to LOCAL: STOCK, <LOCAL-TCP>, <LOCAL-UDP>, PING
to EXT0: ALL
to IT_ADMIN_VPN: STOCK
to MIGRATION_VPN: ALL
to WRKSTN_WIFI: STOCK, <RDP-TCP>
###to SHOP: STOCK
###to SHOP_WIFI: STOCK
to IT_WRKSTN: STOCK
to IT_WRKSTNWFI: STOCK
###to MBZ_SVC: STOCK
###to MBZ_SVC_WIFI: STOCK
###to SU_SALES: STOCK
###to SU_SALES_WIFI: STOCK
###to TLSA: STOCK
###to TLSA_WIFI: STOCK
###to VOIP_A: STOCK
###to VOIP_WIFI_A: STOCK
to SEC-CAM_A: STOCK, <SEC_NVR-TCP>(SEC_NVR-IP)
###to IOT_A: STOCK
###to MEDIA_LAN: STOCK
###to MEDIA_WIFI: STOCK
###to MBZ_MEDIA2: STOCK
to BMW_ISPI: STOCK, <ISPA_HUB-TCP>(ISPA_HUB-IP), <ISPA_HUB-UDP>(ISPA_HUB-IP), <WIN_SMB-TCP>(ISPI_NAS-IP)
###to CUST_WIFI: STOCK
###to VVO_CAR_WIFI: STOCK
###to VVO_CLI_WIFI: STOCK
to DEALERTRACK: ALL
to PRIV_SVR_A: STOCK, <WSUS01-TCP>(WSUS01-IP), <KSC01-TCP>(KSC01-IP),<KSC01-UDP>(KSC01-IP)
to PRIV_SVR_B: STOCK, <ZBBX_ACTV-TCP>(ZBBX_PRXY01-IP)
to PRIV_SVR_C: STOCK, <WIN_DC-TCP>(WIN_DC-IP), <WIN_DC-UDP>(WIN_DC-IP), <WIN_NPS-UDP>(WIN_NPS01-IP), <WIN_CA-TCP>(WIN_CA02-IP), <OPENFIRE-TCP>(OPENFIRE-IP)
to PRIV_SVR_D: STOCK, <WIN_SMB-TCP>(WIN_FS01-IP), <WIN_PRNTSVR-TCP>(WIN_PRNTSVR01-IP)
to PRIV_SVR_E: STOCK, <RPROXY_PRIV01-TCP>(RPROXY_PRIV01-IP), <OS_IMAGES-TCP>(OS_IMAGES-IP), <ZBBX_ACTV-TCP>(ZBBX_PRXY02-IP)
to PRIV_SVR_F: STOCK, <BDYSHP_SVR-TCP>(BDYSHP_SVR-IP), <BDYSHP_SVR-UDP>(BDYSHP_SVR-IP)
to PUBPRV_SVR_A: STOCK, <SMTP-TCP>(ZMBRA_PRXY_MTA-IP), <RPROXY_PUB01-TCP>(RPROXY_PUB01-IP)
to PUBPRV_SVR_B: STOCK
to IT_VPN_SVR: STOCK
to CA_VPN_SVR: STOCK
###to MGMT_LAN_A: STOCK
###to MGMT_LAN_B: STOCK
###to MGMT_LAN_C: STOCK
###to MGMT_LAN_D: STOCK
###to MGMT_LAN_E: STOCK

The stuff commented out doesn’t get parsed by the python script.
STOCK = allow established/related/drop invalid
The group rules are controlled by the brackets like this [source IP group]{source port group}(destination IP group)
There are options as well as shown in the rough guide below

Rough Rules Guide

==Shorthand Syntax Rules==
Defaults for <>,(),{},[] rules unless otherwise specified:
action = accept
state = new enable (if you specify any state then you must specify the new state as well since it will be removed with the addition of other states, per rule that is)
log = enable

Zone names must be 14 characters or less, be all caps and use only the characters A-Z, 0-9, -,_

Rule names must be 31 characters or less, be all caps and use only the characters A-Z, 0-9, -, _, !

Rules for port groups must end in: -TCP, -UDP, -TCP_UDP
Rules for ip address groups must end in -IP
Rules for network addresss groups must end in -NET

SYNTAX:
SRC_ZONE:
DST_ZONE_A: [SOURCE-IP]{SOURCE-PORT-TCP}<DESTINATION-TCP>(DESTINATION-IP), [SOURCE-IP|OPTION_A;OPTION_B;OPTION_C]
DST_ZONE_B: [!SOURCE-IP]{SOURCE-PORT-TCP}<!DESTINATION-TCP>(DESTINATION-IP), [SOURCE-IP|OPTION_A;OPTION_B;OPTION_C]

! means NOT

Pre-defined Options:
NOLOG = log disable
LOG = log enable
DROP = action drop
REJECT = action reject
ACCEPT = action accept
ST_ESTB_ENBL = state established enable
ST_ESTB_DSBL = state established disable
ST_RLTD_ENBL = state related enable
ST_RLTD_DSBL = state related disable
ST_INVLD_ENBL = state invalid enable
ST_INVLD_DSBL = state invalid disable
ST_NEW_ENBL = state new disable
ST_NEW_DSBL = state new disable
MATCH-IPSEC = ipsec match-ipsec

Pre-defined Rules:
STOCK, PING, ALL, OSPF, IPSEC-IKE-ESP

The rules rules above were written about 10 revisions ago so to see any changes please review the actual python script.

There is extensive error checking for the rules so that if you don’t follow them it will give you useful output. If you can’t have confidence in the generated rules then why do this in the first place!

Below is an example output of the actual commands for half of a zone pair:

echo =====MGMT_LAN_E to MGMT_LAN_A=====
echo delete firewall name MGMT_LAN_E.MGMT_LAN_A
set firewall name MGMT_LAN_E.MGMT_LAN_A description "MGMT_LAN_E to MGMT_LAN_A: STOCK, <DNS-TCP_UDP>(UBND03-IP), <NTP-UDP>(UBND03-IP), [VHSVR-IP](RPROXY_MGMT01-IP), (OVPN_S2S01-IP), [SYSLOG-RELAY-IP]<SYSLOG_TLS-TCP>(SYSLOG-IP) ###Rules Generated 2024-02-23 19:34:10.287745###"
set firewall name MGMT_LAN_E.MGMT_LAN_A rule 100 description "Allow established/related"
set firewall name MGMT_LAN_E.MGMT_LAN_A rule 100 action accept
set firewall name MGMT_LAN_E.MGMT_LAN_A rule 100 state established enable
set firewall name MGMT_LAN_E.MGMT_LAN_A rule 100 state related enable
set firewall name MGMT_LAN_E.MGMT_LAN_A rule 100 log disable
set firewall name MGMT_LAN_E.MGMT_LAN_A rule 105 description "Drop invalid"
set firewall name MGMT_LAN_E.MGMT_LAN_A rule 105 action drop
set firewall name MGMT_LAN_E.MGMT_LAN_A rule 105 state invalid enable
set firewall name MGMT_LAN_E.MGMT_LAN_A rule 105 log enable
set firewall name MGMT_LAN_E.MGMT_LAN_A rule 110 description "to DNS-TCP_UDP to UBND03-IP"
set firewall name MGMT_LAN_E.MGMT_LAN_A rule 110 action accept
set firewall name MGMT_LAN_E.MGMT_LAN_A rule 110 destination group port-group DNS-TCP_UDP
set firewall name MGMT_LAN_E.MGMT_LAN_A rule 110 protocol tcp_udp
set firewall name MGMT_LAN_E.MGMT_LAN_A rule 110 destination group address-group UBND03-IP
set firewall name MGMT_LAN_E.MGMT_LAN_A rule 110 state new enable
set firewall name MGMT_LAN_E.MGMT_LAN_A rule 110 log enable
set firewall name MGMT_LAN_E.MGMT_LAN_A rule 115 description "to NTP-UDP to UBND03-IP"
set firewall name MGMT_LAN_E.MGMT_LAN_A rule 115 action accept
set firewall name MGMT_LAN_E.MGMT_LAN_A rule 115 destination group port-group NTP-UDP
set firewall name MGMT_LAN_E.MGMT_LAN_A rule 115 protocol udp
set firewall name MGMT_LAN_E.MGMT_LAN_A rule 115 destination group address-group UBND03-IP
set firewall name MGMT_LAN_E.MGMT_LAN_A rule 115 state new enable
set firewall name MGMT_LAN_E.MGMT_LAN_A rule 115 log enable
set firewall name MGMT_LAN_E.MGMT_LAN_A rule 120 description "from VHSVR-IP to RPROXY_MGMT01-IP"
set firewall name MGMT_LAN_E.MGMT_LAN_A rule 120 action accept
set firewall name MGMT_LAN_E.MGMT_LAN_A rule 120 source group address-group VHSVR-IP
set firewall name MGMT_LAN_E.MGMT_LAN_A rule 120 destination group address-group RPROXY_MGMT01-IP
set firewall name MGMT_LAN_E.MGMT_LAN_A rule 120 state new enable
set firewall name MGMT_LAN_E.MGMT_LAN_A rule 120 log enable
set firewall name MGMT_LAN_E.MGMT_LAN_A rule 125 description "to OVPN_S2S01-IP"
set firewall name MGMT_LAN_E.MGMT_LAN_A rule 125 action accept
set firewall name MGMT_LAN_E.MGMT_LAN_A rule 125 destination group address-group OVPN_S2S01-IP
set firewall name MGMT_LAN_E.MGMT_LAN_A rule 125 state new enable
set firewall name MGMT_LAN_E.MGMT_LAN_A rule 125 log enable
set firewall name MGMT_LAN_E.MGMT_LAN_A rule 130 description "from SYSLOG-RELAY-IP to SYSLOG_TLS-TCP to SYSLOG-IP"
set firewall name MGMT_LAN_E.MGMT_LAN_A rule 130 action accept
set firewall name MGMT_LAN_E.MGMT_LAN_A rule 130 source group address-group SYSLOG-RELAY-IP
set firewall name MGMT_LAN_E.MGMT_LAN_A rule 130 destination group port-group SYSLOG_TLS-TCP
set firewall name MGMT_LAN_E.MGMT_LAN_A rule 130 protocol tcp
set firewall name MGMT_LAN_E.MGMT_LAN_A rule 130 destination group address-group SYSLOG-IP
set firewall name MGMT_LAN_E.MGMT_LAN_A rule 130 state new enable
set firewall name MGMT_LAN_E.MGMT_LAN_A rule 130 log enable
set firewall name MGMT_LAN_E.MGMT_LAN_A enable-default-log
set zone-policy zone MGMT_LAN_A from MGMT_LAN_E firewall name MGMT_LAN_E.MGMT_LAN_A
echo =====MGMT_LAN_E.MGMT_LAN_A Rules Generated 2024-02-23 19:34:10.287745=====

the echo lines are to make things easier to read while not causing problems when cutting and pasting.
the “echo delete…” line is so that if you are regenerating a bunch of zones (or one zone) you search and replace all the “echo delete” with just “delete” so the existing rules are removed and the new set replaces them, no worries of overwriting existing rules or messing up orders. The order is generated by your shorthand rules. The description includes the zone pair, the original shorthand rules and when it was generated, this helps me audit things quickly.

In the next post I’ll post the actual python script.

Below is the python code (I had to attach it). It was made in PyCharm on Linux. It’s my first python code… so there are a bunch of references to where I grabbed other peoples examples in it. It’s ugly and I’m sure can be done better but it saves me a lot of time so I’ll learn python and do it right, right after that next thing on my todo list…

Firewall Zones VyOS Python Script Revision 20.txt (67.7 KB)

In the script it will reference a file where you put your shorthand and it outputs to another file (you’ll see this in the script). I had to attach the script since it made the post to big.

Like I said, there is extensive error checking, it’s one of the things I’m proudest of. Go ahead and try to make it generate rules that won’t work in VyOS, let me know if you can. You still need to be accurate with your firewall groups, I can’t generate those from shorthand… The options testing I haven’t done as much on but I’ve tried to include most mistakes people will make during basic use.

You can even make your own predefined rules where the normal port/IP isn’t sufficient, just look in the code for how I add them. The STOCK rule is an example of one.

Below are some output examples of the error checking:

I put an unexpected character (a !) at the end of a line:

Destination Zone Name is "PUBPRV_SVR_A" from line 210
ERROR: invalid character "!" at end of line 210.
Allowed characters are: A-Z, 0-9, ), ], } and >.
Please check the syntax of your firewall rules.


Process finished with exit code 1

I put 2 destination IP groups in a single rule:

Destination Zone Name is "BMW_ISPI" from line 243
Source and Destination Zones are: WRKSTN_WIFI and BMW_ISPI
Basic TCP/UDP/IP/NET rule variations not found; checking for pre-defined/custom rule for "STOCK" from line 243.
ERROR: duplicate bracket found in rule "<WIN_SMB-TCP>(ISPI_NAS-IP)(ISPI_NAS2-IP)" on line 243.

Process finished with exit code 1

My hand strayed the keyboard and added a “b” to some rules without me noticing it

Destination Zone Name is "PUBPRV_SVR_A" from line 700
Source and Destination Zones are: TLSA_WIFI and PUBPRV_SVR_A
Basic TCP/UDP/IP/NET rule variations not found; checking for pre-defined/custom rule for "STOCK" from line 700.
ERROR: invalid syntax for rule "b<SMTP-TCP>(ZMBRA_PRXY_MTA-IP)" on line 700.

Process finished with exit code 1

I put 2 source zones in a single group of zones:

Source Zone Name is "PRIV_SVR_C" from line 1326

Source Zone Name is "BOB" from line 1327
ERROR: 2 sequential source zones found. Zone name "BOB" at line 1327 is the 2nd source zone.

Process finished with exit code 1

I think I may have increased or shortened the zone name limit on my last run and saved it, don’t know if it’s correct in the script above. It should be 14 or 13 characters or less, VyOS won’t accept firewall names over 27 or 29 characters or something like that, I forget what…

Anyway, I hope this helps someone. Please use with caution. I use it in production and have been satisfied, and I am thankful for VyOS; tinkering is still an enjoyable thing because of this project.