Turn iptables-save Output into XML with iptables-xml

iptables-xml converts an iptables-save snapshot into structured XML a script can parse properly instead of scraping text. The examples target the installed iptables 1.8.10 package and never touch the running firewall.

Allow about fifteen minutes. You need iptables-xml, a shell, and either an iptables-save file or permission to read the host firewall. Reading the live ruleset may need elevated privileges and can expose sensitive network policy. This guide does not restore rules or change the firewall.

1. Check the installed command

Confirm the binary and package version first. These are ordinary, read-only checks:

$ command -v iptables-xml
/usr/bin/iptables-xml
$ dpkg-query -W -f='${Package} ${Version}\n' iptables
iptables 1.8.10-3ubuntu2

The local manual identifies this command as iptables-xml(1) from iptables 1.8.10. Its interface is small: -c or --combine, -v or --verbose, and -h or --help.

Tip: do not use --version as a version check. This build reports it as an unrecognised option but still exits successfully, which makes it a poor test in a script. Stick with command -v and check the package version instead.

2. Convert a saved ruleset without touching the firewall

iptables-xml reads iptables-save format from standard input and writes XML to standard output. Start with a small fixture so you can learn the shape without reading or changing a real ruleset:

$ printf '%s\n' \
  '*filter' \
  ':INPUT ACCEPT [0:0]' \
  '-A INPUT -p tcp --dport 22 -j ACCEPT' \
  'COMMIT' | iptables-xml
<iptables-rules version="1.0">
  <table name="filter" >
    <chain name="INPUT" policy="ACCEPT" packet-count="0" byte-count="0" >
      <rule >
       <conditions>
        <match >
          <p >tcp</p>
          <dport >22</dport>
        </match>
       </conditions>
       <actions>
        <ACCEPT  />
       </actions>
      </rule>
    </chain>
  </table>
</iptables-rules>

The conversion is mechanical:

The root includes version="1.0", which describes this XML format, not the installed iptables package version.

Use input redirection to save the result. The shell creates or truncates the destination before the converter runs, so choose a new path or make a backup first:

$ iptables-xml < rules.v4 > rules.xml
$ test -s rules.xml && echo 'XML file is non-empty'
XML file is non-empty

For a file named on the command line, pass one input path after the options: iptables-xml rules.v4 > rules.xml. Keep the source file unchanged until you have inspected the result.

3. Export the live ruleset only when you need it

To convert the host's current IPv4 rules, put iptables-save before the converter:

$ sudo iptables-save | iptables-xml > live-rules.xml
$ test -s live-rules.xml && echo 'live XML file is non-empty'
live XML file is non-empty

sudo is shown because reading the live ruleset commonly needs elevated privileges. It is not needed for the conversion itself when the input is already in a readable file. This pipeline does not install, flush, append or restore a rule, but the resulting file may contain your host's firewall layout and counters, so treat it as sensitive operational data.

If the first command says it cannot fetch the ruleset or permission is denied, stop and fix access. Do not replace iptables-save with a command that changes rules. If you do not need the live state, use a saved snapshot or the fixture above instead.

4. Add source-line comments when reviewing a conversion

Use -v or --verbose when you need to trace XML back to its input:

$ iptables-xml --verbose < rules.v4 > rules-trace.xml
$ rg -n '<!-- line ' rules-trace.xml
5:<!-- line 2 :INPUT ACCEPT [0:0] -->
6:<!-- line 3 *-A INPUT  *-p tcp *- *-dport 22 *-j ACCEPT -->

The exact line numbers depend on blank lines and comments in the input. These comments are useful during review, but they are not rule data. If another program parses the XML, test whether it ignores comments as XML parsers normally do.

5. Combine consecutive matching actions deliberately

Without options, each input rule becomes its own XML rule. Add -c or --combine to collect consecutive rules with identical matches but different targets:

$ printf '%s\n' \
  '*filter' \
  ':INPUT ACCEPT [0:0]' \
  '-A INPUT -p tcp --dport 22 -j MARK --set-mark 10' \
  '-A INPUT -p tcp --dport 22 -j LOG --log-prefix "ssh "' \
  'COMMIT' | iptables-xml --combine | rg -n 'rule|MARK|LOG|Combine'
4:      <rule >
12:        <MARK >
15:<!-- Combine action from next rule -->
16:        <LOG >

Only consecutive rules with identical matches are eligible. A different port, protocol, interface or other match starts a separate XML rule. Terminating actions such as RETURN, DROP, ACCEPT and QUEUE are not combined with a following target. This option changes the representation, not the live ruleset, but it can change how your XML consumer interprets action order, so use it only when that consumer expects the grouped form.

6. Check the XML before handing it to another tool

Check that the output is non-empty and has the expected root element. If an XML-aware parser is installed, use it as an additional check:

$ test -s rules.xml
$ rg -q '^<iptables-rules version="1.0">$' rules.xml
$ xmllint --noout rules.xml

The last command is optional and belongs to xmllint, not iptables-xml. Inspect a few chains, conditions and actions as well. A successful conversion proves the text was converted; it does not prove a later XML-to-rules workflow preserves the policy you intended.

Warning: the manual describes a possible reverse workflow using an iptables.xslt stylesheet, xsltproc and iptables-restore. Treat that as a separate, high-risk operation. Restoring rules can disrupt active connections and lock out remote access. Do not pipe generated XML into a restore command until you have reviewed it, tested a rollback path and arranged console access or a maintenance window.

Done means