Last active
May 15, 2023 17:22
-
-
Save bashtheshell/4a31f1c3704107c591d1156c0b995a12 to your computer and use it in GitHub Desktop.
Translate manually-written '-A INPUT' lines in 'rules.v4' iptables file to the format identical to `iptables -S` output.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
# The purpose of this program is to only slightly modify and rearrange all 'A-INPUT' arguments, that may be manually written to the `iptables`'s `rules.v4` | |
# file, in the order that the `iptables -S` command uses. This would allow one to see less lines when using `diff` command to compare | |
# the newly-translated output from this program with `iptables -S` output. Ideally, you should not find any '-A INPUT' in the `diff` output. Otherwise, there | |
# may have been a bug with the program (e.g. some strings elude the regular expression pattern). Please remember that lines beginning with comment ('#') | |
# are ignored. | |
# USAGE: | |
# You can either give it a file as its argument or pass a file to the program using STDIN. Here are few ways: | |
# | |
# ❯ ./iptables-A_INPUT-translate.py /etc/iptables/rules.v4 > new_temp_rules.v4 | |
# ❯ cat /etc/iptables/rules.v4 | ./iptables-A_INPUT-translate.py > new_temp_rules.v4 | |
# ❯ ./iptables-A_INPUT-translate.py < /etc/iptables/rules.v4 > new_temp_rules.v4 | |
# # EXAMPLE: | |
# ❯ echo '-A INPUT -m state --state RELATED,ESTABLISHED,NEW -m tcp -p tcp -s 123.100.200.99/24 --dport 8080 -m comment --comment "Something here and there - created on 02-14-20" -j ACCEPT' | ./iptables-A_INPUT-translate.py | |
# -A INPUT -s 123.100.200.99/24 -p tcp -m state --state NEW,RELATED,ESTABLISHED -m tcp --dport 8080 -m comment --comment "Something here and there - created on 02-14-20" -j ACCEPT | |
# ❯ echo '-A INPUT -m state --state ESTABLISHED,RELATED -m tcp -p tcp -s mail.google.com --dport 993 -m comment --comment "pending deletion - 10-20-30" -j ACCEPT' | ./iptables-A_INPUT-translate.py | |
# -A INPUT -s 142.251.40.197/32 -p tcp -m state --state RELATED,ESTABLISHED -m tcp --dport 993 -m comment --comment "pending deletion - 10-20-30" -j ACCEPT | |
import re | |
import socket | |
import sys | |
def rearrange_A_INPUT_arguments(parameter): | |
# Split up the line string into portions | |
parameter_line = [" ".join(pair) for pair in re.findall( r'(-{1,2}\w+)\s*([-\w./,]+|".+?")' , parameter)] | |
new_line = [] | |
if '-A INPUT' in parameter and parameter.index('-A INPUT') == 0: | |
# Automatically add the first portion as it's always the case | |
new_line.append(parameter_line[0]) | |
temporary_line = {} | |
for each_item in parameter_line: | |
if re.match(r'-s', each_item) or re.match(r'--source', each_item): | |
temporary_string = "" | |
# Strip both variations of the source option | |
if re.match(r'-s', each_item): | |
temporary_string = each_item.split('-s', 1)[-1].lstrip() | |
else: | |
temporary_string = each_item.split('--source', 1)[-1].lstrip() | |
# Get the address (either IP address or hostname) portion of the string | |
address_string = temporary_string.rsplit('/', 1) | |
ip_address_pattern = re.compile("^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$") | |
# Convert the address to the appropriate format | |
if re.match(ip_address_pattern, address_string[0]): | |
if len(address_string) > 1: | |
temporary_string = '-s ' + address_string[0] + '/' + address_string[1] | |
else: | |
temporary_string = '-s ' + address_string[0] + '/32' | |
else: | |
temporary_string = '-s ' + socket.gethostbyname(address_string[0]) + '/32' | |
temporary_line['source'] = temporary_string | |
if re.match(r'-i', each_item): | |
temporary_line['interface'] = each_item | |
if re.match(r'-p', each_item): | |
temporary_line['protocol'] = each_item | |
if re.match(r'-m state', each_item): | |
temporary_line['match_state'] = each_item | |
if re.match(r'--state', each_item): | |
# Sort the conntrack value in this order if possible: (NEW,RELATED,ESTABLISHED) | |
temporary_string = [] | |
conntrack_string = each_item.split('--state', 1)[-1].lstrip().split(',') | |
if len(conntrack_string) > 1: | |
for conntrack in ['NEW', 'RELATED', 'ESTABLISHED']: | |
if conntrack in conntrack_string: | |
temporary_string.append(conntrack) | |
conntrack_string.remove(conntrack) | |
for conntrack in conntrack_string: | |
temporary_string.append(conntrack) | |
temporary_line['state'] = '--state ' + ','.join(temporary_string) | |
else: | |
temporary_line['state'] = each_item | |
if re.match(r'-m (tc|ud)p', each_item): | |
temporary_line['match_protocol'] = each_item | |
if re.match(r'--dport', each_item): | |
temporary_line['destination_port'] = each_item | |
if re.match(r'-m comment', each_item): | |
temporary_line['match_comment'] = each_item | |
if re.match(r'--comment', each_item): | |
# Remove quotations if comment contains no space except for periods | |
temporary_string = each_item.split('--comment', 1)[-1].lstrip(' "').rstrip('" ') | |
if temporary_string.find(' ') < 0 and temporary_string.find('.') < 0: | |
temporary_line['comment'] = '--comment ' + temporary_string | |
elif temporary_string.find('.') >= 0: | |
temporary_line['comment'] = '--comment ' + '"' + temporary_string + '"' | |
else: | |
temporary_line['comment'] = each_item | |
if re.match(r'-j', each_item): | |
temporary_line['jump'] = each_item | |
# Build the new line (putting the arguments in the correct order) | |
new_argument_order = ['source', 'interface', 'protocol', 'match_state', 'state', 'match_protocol', 'destination_port', 'match_comment', 'comment', 'jump'] | |
for i in new_argument_order: | |
if i in temporary_line: | |
new_line.append(temporary_line[i]) | |
# Return the new rearranged line string | |
return ' '.join(new_line) + '\n' | |
else: | |
return parameter | |
if __name__ == "__main__": | |
# Read file from the first argument only | |
if len(sys.argv) == 2: | |
with open(sys.argv[1], 'r') as f: | |
for line in f: | |
if not line: | |
break | |
sys.stdout.write(str(rearrange_A_INPUT_arguments(line))) | |
# Alternatively, read from STDIN | |
elif not sys.stdin.isatty(): | |
for line in sys.stdin: | |
if not line: | |
break | |
sys.stdout.write(str(rearrange_A_INPUT_arguments(line))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment