https://travis-ci.org/sl0/conv.svg?branch=master

iptables-converter

iptables-converter: convert iptables to iptables-save format, output comes with [0:0] for iptables-restore -c

ip6tables-converter: convert ip6tables to ip6tables-save format, output comes with [0:0] for ip6tables-restore -c

Source: https://github.com:sl0/conv.git

Version: 0.9.11

Date: 2019-08-04

Licenses:

  • GNU GENERAL PUBLIC LICENSE Version 3 or any later
  • Apache License Version 2.0

Author: Johannes Hubertz johannes@hubertz.de

iptables-converter speeds up loading of iptables-commands by converting them to iptables-save format, and then loading them through iptables-restore is much more quicker than loading the plain commands. The loading itself is not part of iptables-converter.

Usage:

iptables-converter [ -d destination ] [ -s source ] [ --sloppy ]
ip6tables-converter [ -d destination ] [ -s source ] [ --sloppy ]

This assumes that source is a plain ascii-file containing lines starting with iptables to build a firewall ruleset. Lines starting with /sbin/iptables are understood as well. Omitting -s source defaults to reading a file named rules. An optional sloppy parameter makes premature definitions (-N name) of any user defined chains unneccessary, they are defined automatically by first mentioning them. Optionally -d destination writes everything into the given destination file since verstion 0.9.10. Omitting this option results in writing Output to stdout, which is the default behavior. Packet-counters and byte-counters include [0:0] which keeps compatibility to iptables-restore as well as to iptables-restore -c.

ip6tables-converter works for ip6tables just the same way.

Both they work for filter, mangle, nat and raw tables, security tables are not supported for now.

From version 0.9.10 on it works as a python-module using entry-points for easier imports. For your convienience, the module is named iptables_conv.

At travis-ci.org the tests are run automatically, thanks to Guido! To run them locally, please use python:

python setup test

or tox, which is the preferred testrunner. iptables-converter is tested to work well with python2.7, python3.5 and python3.6. The tests are done using pytest for easier writing future testcases. Some sphinx documentation is prepared. Debian packages are provided for the binaries and sphinx-documentation. git-buildpackage creates them on the fly. RPMs may be created by python:

setup.py bdist_rpm

Any comments welcome.

Have fun!

Johannes

iptables-converter - description

In linux iptables exists since kernel version 2.4.

A corresponding userland command iptables is used to control them.

The tables are grouped in filter, nat, mangle and raw. In every group there are chains, which contain more or less traffic specific rules. Predefined in filter tables are INPUT, FORWARD and OUTPUT chain. Perhaps you like to read the fine manuals about the iptables command in Linux to get to know more about these.

Usually a systemadministrator wants to write his rules in a shell script, which is run within the boot sequence or every now and then on every change… Over the time this script will grow. The longer it gets, the more time takes it to complete. Exactly this is the reason, why iptables-converter was written. Only motivation was to speed up the loading of long scripts with multiple iptables commands.

The iptables-converter is a pure python script with two entrypoints:

  • iptables-converter
  • ip6tables-converter

They reside in linux userland, that means to run one of them you don’t need root priviledges, which are only needed to load the output of the converters into the kernelspace.

Command line interface

To have an idea which command line options are supported, just ask it for help:

$ iptables-converter --help
Usage:  iptables-converter --help | -h

        iptables-converter: version 0.9.11  Have Fun!

Options:
  -h, --help            show this help message and exit
  -d DESTFILE, --dest-file=DESTFILE
                        output filename, default: stdout
  -s SOURCEFILE, --source-file=SOURCEFILE
                        file with iptables commands, default: rules
  --sloppy              -N name-of-userchain is inserted automatically, by
                        default -N is neccessary in input

ip6tables-converter surprisingly behaves exactly the same way, except from the ‘6’ in the command and version line:

$ ip6tables-converter --help
Usage:  ip6tables-converter --help | -h

    ip6tables-converter: version 0.9.11 Have Fun!

Options:
  -h, --help            show this help message and exit
  -d DESTFILE, --dest-file=DESTFILE
                        output filename, default: stdout
  -s SOURCEFILE, --source-file=SOURCEFILE
                        file with iptables commands, default: rules
  --sloppy              -N name-of-userchain is inserted automatically, by
                        default -N is neccessary in input

The only difference in between them is what is looked at. iptables-converter just handles lines starting with iptables or /sbin/iptables, ip6tables-converter only lines starting with ip6tables or /sbin/ip6tables.

DEST- and SOURCEFILE

These should be clear, the default values are build in for your convenience only. A script generating iptables command writes them into a file named rules, which is then read by the converter, if the -s option is not used. The converters ouptut is then written to stdout, if the -d option is not used. So it might be piped or somehow else processed.

–sloppy

The option –sloppy perhaps needs some explanation. Usually the iptables command insists on defined chains, especially you cannot insert or append a rule into a non-existent user defined chain. Especially the -N UserChain is needed normally in advance to the append operation. Inserting into an empty chain is forbidden as well. By using the –sloppy option this -N command is not needed in the input for the converter as it defines the UserCHain automatically on the first occurance of any.

Default operating

Assume a plain file with following contents:

iptables -F
iptables -t nat -F
iptables -N USER_CHAIN
iptables -A INPUT -p tcp --dport 23 -j ACCEPT
iptables -A USER_CHAIN -p icmp -j DROP
iptables -P INPUT DROP
iptables -t nat -A POSTROUTING -s 10.0.0.0/21 -p tcp --dport   80 -j SNAT --to-source 192.168.1.15
iptables -t nat -A PREROUTING  -d 192.0.2.5/32 -p tcp --dport 443 -j DNAT --to-destination 10.0.0.5:1500

As times goes by, this script will grow. The more lines it has, the longer will it take to be loaded. This is because every iptables statement needs to modify the kernels iptables as an atomar operation, which is a lot of overhead from locking the tables, modifying and unlocking them. There should be a quicker way of getting things done. Using iptables-save we can save easily the actual ruleset from the kernel to a file. To load it’s content into the kernel again is a very quick action compared to the loading of the originating shellscript. The iptables-restore operation is one atomar operation from the kernels view regardless of the number of modifications. It’s clear to be the much quicker the more lines are covered. The disadvantage of this proceeding is, the table, f.e. the filter tables, are loaded at once, i.e. no appending or inserterting using the same command is possible. You only need a complete set of iptable commands within a file, just like iptables-save gives it. So the idea came up to have a converter just for saving time.

Lets assume, the file shown above is named generated-rules, then we have easy going:

$ iptables-converter -s generated-rules -d converted-rules
$ cat converted-rules
*raw
:OUTPUT ACCEPT [0:0]
:PREROUTING ACCEPT [0:0]
COMMIT
*nat
:OUTPUT ACCEPT [0:0]
:PREROUTING ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
-A PREROUTING -d 192.0.2.5/32 -p tcp --dport 443 -j DNAT --to-destination 10.0.0.5:1500
-A POSTROUTING -s 10.0.0.0/21 -p tcp --dport 80 -j SNAT --to-source 192.168.1.15
COMMIT
*mangle
:FORWARD ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
:PREROUTING ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
COMMIT
*filter
:FORWARD ACCEPT [0:0]
:INPUT DROP [0:0]
:USER_CHAIN - [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -p tcp --dport 23 -j ACCEPT
-A USER_CHAIN -p icmp -j DROP
COMMIT
$

On the same machine or after beeing transferred to another one, the converted-rules file can be loaded into the kernel by using the command iptables-restore as root user:

# iptables-restore -c converted-rules

Of course you use pathnames where filenames are mentioned.

Usage example

So you probably may want to run the converter from within a shell script or the like:

#!/bin/bash

set -e
INPUT_FILE=rules
OUTPUT_FILE=iptables-converted

# needs to be executable as indicator that writing has ended
[ ! -r $INPUT_FILE ] && exit 0
[ ! -x $INPUT_FILE ] && exit 0

iptables-converter.py -s $INPUT_FILE -d $OUTPUT_FILE

# do it only once!
mv $INPUT_FILE $INPUT_FILE}.old

iptables-restore < $OUTPUT_FILE
echo "$INPUT_FILE successfully converted and loaded"
exit 0
# EoF

Error handling

In accidental cases of errors the converter should give you a traceback wherin the word ConverterError appears. This is to let you get to know, where in your whole programming universe the error happend.

Two things can not be handled: Shell functions and shell variables, because the converter does not interpret your input-file.

Shell functions and shell commands

As the file which is read is not interpreted in any way, there are few known error conditions:

  1. the file contains some shell variables, indicated by ‘$’, this leads to an errormessage and exits immediately with returncode 1.
  2. the file contains some shell functions, indicated by ‘(‘ and/or ‘)’, this leads to an errormessage and exits immediately with returncode 1.

If you have such a file, and you want to speed up by converting, please execute it and feed the output as a file to iptables-converter.

Non existent user chains

iptables-converter does some more error-checking while reading input.

Normal behavior is to raise an ConverterError, if any append or insert statement to an userdefined chain is not preceeded by a corresonding chain creation statement ‘-N’. This may be changed to a more smooth handling with an additional commandline option –sloppy. Having this, a non existent userchain is created on the fly when the first append statement is seen. So it is set as first entry gracefully.

Inserting into an emtpy chain anyhow raises an error as iptables-restore would do it later on trying to set the files content into the kernel.

Not implemented

Just to mention it: iptables -E xyz and iptables -L are not implemented in the iptables-converter and throw exceptions for now!

iptables-converter - tests

Untested software, that means software which isn’t accompanied by automated functional tests, is assumed to be broken by design. As iptables-converter is written in python, use of the popular unittests is done for your convienience. The unittests were developed and run by nose, which later have been replaced by pytest. The advantages of pytest over nose are much simpler tests and less overhead. So all newer testcases are written for pytest and thus rely on the plain python assert statement.

Two testclasses are build: Chains_Test and Tables_Test accordingly to the two classes from which the iptables-converter module and script is build from.

Basic usage

Lets see, how to run the tests within the source-tree by just calling pytest:

$ pytest
========================== test session starts ==========================
platform linux -- Python 3.6.6, pytest-3.2.3, py-1.4.34, pluggy-0.4.0
rootdir: /home/hans/devel/conv, inifile:
plugins: cov-2.5.1
collected 37 items

tests/test_iptables_converter.py ............................
tests/test_iptables_options.py .........

======================= 37 passed in 0.05 seconds =======================
$

All tests passed. Fine.

Verbose usage

If you want to see more, just add a -v to the commandline:

$ pytest -v
================================ test session starts =================================
platform linux -- Python 3.6.6, pytest-3.2.3, py-1.4.34, pluggy-0.4.0 -- /home/hans/wb/bin/python3.6
cachedir: .cache
rootdir: /home/hans/devel/conv, inifile:
plugins: cov-2.5.1
collected 37 items

tests/test_iptables_converter.py::Chains_Test::test_01_create_a_chain_object PASSED
tests/test_iptables_converter.py::Chains_Test::test_02_prove_policies PASSED
tests/test_iptables_converter.py::Chains_Test::test_03_tables_names PASSED
tests/test_iptables_converter.py::Chains_Test::test_04_flush PASSED
tests/test_iptables_converter.py::Chains_Test::test_05_new_chain PASSED
tests/test_iptables_converter.py::Chains_Test::test_06_new_existing_chain_fails PASSED
tests/test_iptables_converter.py::Chains_Test::test_07_insert_rule_fail PASSED
tests/test_iptables_converter.py::Chains_Test::test_08_insert_rule_fail PASSED
tests/test_iptables_converter.py::Chains_Test::test_09_insert_rule_works PASSED
tests/test_iptables_converter.py::Chains_Test::test_10_append_rule PASSED
tests/test_iptables_converter.py::Chains_Test::test_11_remove_predef_chain PASSED
tests/test_iptables_converter.py::Chains_Test::test_12_remove_chain PASSED
tests/test_iptables_converter.py::Chains_Test::test_13_illegal_command PASSED
tests/test_iptables_converter.py::Tables_Test::test_01_create_a_tables_object PASSED
tests/test_iptables_converter.py::Tables_Test::test_02_nat_prerouting PASSED
tests/test_iptables_converter.py::Tables_Test::test_03_mangle_table PASSED
tests/test_iptables_converter.py::Tables_Test::test_04_raw_table PASSED
tests/test_iptables_converter.py::Tables_Test::test_05_not_existing_chain PASSED
tests/test_iptables_converter.py::Tables_Test::test_06_read_not_existing_file PASSED
tests/test_iptables_converter.py::Tables_Test::test_07_read_empty_file PASSED
tests/test_iptables_converter.py::Tables_Test::test_08_reference_one PASSED
tests/test_iptables_converter.py::Tables_Test::test_09_shell_variables PASSED
tests/test_iptables_converter.py::Tables_Test::test_10_shell_functions PASSED
tests/test_iptables_converter.py::Tables_Test::test_11_reference_sloppy_one PASSED
tests/test_iptables_converter.py::Tables_Test::test_12_create_a_tables6_object PASSED
tests/test_iptables_converter.py::Tables_Test::test_13_re6ference_one PASSED
tests/test_iptables_converter.py::Tables_Test::test_14_re6ference_sloppy_one PASSED
tests/test_iptables_converter.py::test_15_tables_printout PASSED
tests/test_iptables_options.py::test_01_iptables_converter_option_h PASSED
tests/test_iptables_options.py::test_02_iptables_converter_option_s PASSED
tests/test_iptables_options.py::test_03_iptables_converter_option_d PASSED
tests/test_iptables_options.py::test_04_iptables_converter_option_sd PASSED
tests/test_iptables_options.py::test_05_iptables_converter_main_dlft PASSED
tests/test_iptables_options.py::test_06_iptables_converter_main_ok PASSED
tests/test_iptables_options.py::test_07_iptables_converter_main_write PASSED
tests/test_iptables_options.py::test_08_iptables_converter_option_dest_file PASSED
tests/test_iptables_options.py::test_09_iptables_converter_option_source_file PASSED

============================= 37 passed in 0.06 seconds ==============================
$

Code coverage

If you want to get to know something about the test-coverage, just give pytest a try:

$ pytest --cov=iptables_conv --cov-report=term-missing
=============================== test session starts ================================
platform linux -- Python 3.6.6, pytest-3.2.3, py-1.4.34, pluggy-0.4.0
rootdir: /home/hans/devel/conv, inifile:
plugins: cov-2.5.1
collected 37 items

tests/test_iptables_converter.py ............................
tests/test_iptables_options.py .........

----------- coverage: platform linux, python 3.6.6-final-0 -----------
Name                                  Stmts   Miss  Cover   Missing
-------------------------------------------------------------------
iptables_conv/__init__.py                 8      0   100%
iptables_conv/iptables_converter.py     230      7    97%   28-29, 72, 129-131, 314
-------------------------------------------------------------------
TOTAL                                   238      7    97%


============================ 37 passed in 0.10 seconds =============================
$

If you like to have a look into the sources, you will find the tests directory. Therein all the tests reside. I hope they are self explaining.

testrunner

To simply run the tests, setup.py has a test target:

$ python setup.py test
   ...
$

This runs flake8 and pytest. If you prefer less typing:

$ pytest
   ...
$

Or, possibly the best way of doing is the following super power.

Check tests, syntax and style

For your convenience, a tox.ini is prepared. Give tox a try to check altogether in one single run:

  • python2.7
  • python3.5
  • python3.6
  • flake8
  • docs

iptables-converter - pythonic view

module: iptables_conv

class iptables_conv.iptables_converter.ConverterError
class iptables_conv.iptables_converter.Chains(name, tables, sloppy=False)

chains are grouped in iptables

Parameters:
  • name (str) – chain group name, ‘filter’, ‘nat’, …
  • tables (list) – list of chains
  • sloppy (bool) – needs ‘-N’(default) or not
Returns:

object representing chain group

Return type:

Chains

Raises:

ConverterError – on some illegal conditions

put_into_fgr(content)

fill this line into this tabular

Parameters:content (str) – one line of inputfile
Returns:None
Raises:ConverterError – on some illegal conditions
reset()

action method for iptables -F

class iptables_conv.iptables_converter.Tables(destfile, sourcefile='reference-one', sloppy=False, ipversion=4)

some chaingroups in tables are predef: filter, nat, mangle, raw

Parameters:
  • destfile (str) – which file or pathname is to be written
  • sourcefile (str) – which file or pathname is to be read
  • sloppy (bool) – ‘-N’ is needed(default) or not
  • ipversion (int) – 4(default) or 6
Returns:

Tables after read sourcefile written into destfile

Return type:

Tables

Raises:

ConverterError – on some illegal conditions

put_into_tables(line)

put line into matching Chains-object

Parameters:line – one line of inputfile
Returns:None
read(file_descriptor)

read data from file like object into Tables-object

Parameters:file_descriptor – filedescriptor of file to be read
Returns:None
Raises:ConverterError – on some illegal conditions
read_file(sourcefile)

open file or error

Parameters:sourcefile – file or pathname of file to be read
Returns:file_descriptor
Raises:ConverterError – on IOError
reset(sourcefile, ipversion)

all predefined Chains aka lists are setup as new here

Parameters:
  • sourcefile (str) – file to be read
  • ipversion (int) – 4 or 6
Returns:

None

table_printout()

printout nonempty tabulars in fixed sequence

module: tests

From 0.9.10 on all future tests will be run by pytest. Prior tests are written to use the unittest module.

class tests.test_iptables_converter.Chains_Test(methodName='runTest')

some tests for class Chain

test_01_create_a_chain_object()

Chain 01: create a Filter group, f.e. filter

test_02_prove_policies()

Chain 02: check 3 valid policies, 1 exception

test_03_tables_names()

Chain 03: 3 cases OK, 1 Exception

test_04_flush()

Chain 04: flush filter group, 2 rules and an invalid chain

test_05_new_chain()

Chain 05: create a new chain in filtergroup,

test_06_new_existing_chain_fails()

Chain 06: create an exsiting chain should fail

test_07_insert_rule_fail()

Chain 07: insert a rule into an empty chain fails

test_08_insert_rule_fail()

Chain 08: insert a rule into a non_existing chain fails

test_09_insert_rule_works()

Chain 09: insert a rule into a nonempty chain works at start

test_10_append_rule()

Chain 10: append a rule to a chain

test_11_remove_predef_chain()

Chain 11: try to remove a prefined chain

test_12_remove_chain()

Chain 12: try to remove an existing chain

test_13_illegal_command()

Chain 13: try an ilegal command

class tests.test_iptables_converter.Tables_Test(methodName='runTest')

Tables: some first tests for the class

test_01_create_a_tables_object()

Tables 01: create a Tables object, check chains

test_02_nat_prerouting()

Tables 02: nat PREROUTING entry

test_03_mangle_table()

Tables 03: mangle INPUT entry

test_04_raw_table()

Tables 04: raw OUTPUT entry

test_05_not_existing_chain()

Tables 05: INPUT to not existing chain

test_06_read_not_existing_file()

Tables 06: read non existing file

test_07_read_empty_file()

Tables 07: read empty file (in relation to iptables-commands)

test_08_reference_one()

Tables 08: read default file: reference-one, check chains

test_09_shell_variables()

Tables 09: read buggy file with shell variables

test_10_shell_functions()

Tables 10: read buggy file with shell functions

test_11_reference_sloppy_one()

Tables 11: read sloppy input file: reference-sloppy-one, check chains

test_12_create_a_tables6_object()

Tables 12: create an ipv6 Tables object, check chains

test_13_re6ference_one()

Tables 13: read default file: re6ference-one, check chains

test_14_re6ference_sloppy_one()

Tables 14: read sloppy input file: re6ference-sloppy-one, check chains

tests.test_iptables_converter.test_15_tables_printout(capsys)

Tables 15: check table_printout as well

Contents:

Indices and tables