Search code examples
bashawksedformattingiptables

Selectively remove unnecessary column whitespace from 'iptables -nvL' command output?


The goal

We're looking to change this output (generated from iptables(8)-based script below running on Ubuntu 18.04 that parses the original, raw, anonymized iptables -nvL output from one of our servers):

Chain INPUT (policy DROP 2525 packets, 130K bytes)
target                      prot        opt      in           out   source          destination
ufw-before-logging-input    all         --       *            *     0.0.0.0/0       0.0.0.0/0
ufw-before-input            all         --       *            *     0.0.0.0/0       0.0.0.0/0
ufw-after-input             all         --       *            *     0.0.0.0/0       0.0.0.0/0
ufw-after-logging-input     all         --       *            *     0.0.0.0/0       0.0.0.0/0
ufw-reject-input            all         --       *            *     0.0.0.0/0       0.0.0.0/0
ufw-track-input             all         --       *            *     0.0.0.0/0       0.0.0.0/0

...to be narrower, with less unnecessary whitespace between the columns:

Chain INPUT (policy DROP 2525 packets, 130K bytes)
target                      prot   opt   in   out   source          destination
ufw-before-logging-input    all    --    *    *     0.0.0.0/0       0.0.0.0/0
ufw-before-input            all    --    *    *     0.0.0.0/0       0.0.0.0/0
ufw-after-input             all    --    *    *     0.0.0.0/0       0.0.0.0/0
ufw-after-logging-input     all    --    *    *     0.0.0.0/0       0.0.0.0/0
ufw-reject-input            all    --    *    *     0.0.0.0/0       0.0.0.0/0
ufw-track-input             all    --    *    *     0.0.0.0/0       0.0.0.0/0

We'd prefer a bash script where we can more-easily (than in the script below) tweak, over time, the width of each column spacing on a per-column basis. We've not yet been able to get awk-based things like sprintf, gsub, or this mechanism to do what we want.

Non-bash solutions that accomplish similar/same things might also work.

More-challenging input

This section of the iptables -nvL output (unparsed by the script below) is more difficult, because of the right-most free-form column, the only column with spaces in the content:

Chain ufw-after-input (1 references)
 pkts bytes target     prot opt in     out     source               destination         
    1    78 ufw-skip-to-policy-input  udp  --  *      *       0.0.0.0/0            0.0.0.0/0            udp dpt:137
    0     0 ufw-skip-to-policy-input  udp  --  *      *       0.0.0.0/0            0.0.0.0/0            udp dpt:138
    0     0 ufw-skip-to-policy-input  tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:139
    5   260 ufw-skip-to-policy-input  tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:445
    0     0 ufw-skip-to-policy-input  udp  --  *      *       0.0.0.0/0            0.0.0.0/0            udp dpt:67
    0     0 ufw-skip-to-policy-input  udp  --  *      *       0.0.0.0/0            0.0.0.0/0            udp dpt:68
    0     0 ufw-skip-to-policy-input  all  --  *      *       xxx.xxx.xxx.xxx/yy   xxx.xxx.xxx.xxx/yy   ADDRTYPE match dst-type BROADCAST

Chain ufw-after-logging-forward (1 references)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 LOG        all  --  *      *       xxx.xxx.xxx.xxx/yy   xxx.xxx.xxx.xxx/yy   limit: avg 3/min burst 10 LOG flags 0 level 4 prefix "[UFW BLOCK] "

A good conversion of the above looks like this:

Chain ufw-after-input (1 references)
target                    prot opt in  out  source              destination
ufw-skip-to-policy-input  udp  --  *   *    0.0.0.0/0           0.0.0.0/0           udp dpt:137
ufw-skip-to-policy-input  udp  --  *   *    0.0.0.0/0           0.0.0.0/0           udp dpt:138
ufw-skip-to-policy-input  tcp  --  *   *    0.0.0.0/0           0.0.0.0/0           tcp dpt:139
ufw-skip-to-policy-input  tcp  --  *   *    0.0.0.0/0           0.0.0.0/0           tcp dpt:445
ufw-skip-to-policy-input  udp  --  *   *    0.0.0.0/0           0.0.0.0/0           udp dpt:67
ufw-skip-to-policy-input  udp  --  *   *    0.0.0.0/0           0.0.0.0/0           udp dpt:68
ufw-skip-to-policy-input  all  --  *   *    xxx.xxx.xxx.xxx/yy  xxx.xxx.xxx.xxx/yy  ADDRTYPE match dst-type BROADCAST

Chain ufw-after-logging-forward (1 references)
target                    prot opt in  out  source              destination         
LOG                       all  --  *   *    xxx.xxx.xxx.xxx/yy  xxx.xxx.xxx.xxx/yy  limit: avg 3/min burst 10 LOG flags 0 level 4 prefix "[UFW BLOCK] "

Original iptables bash script

The script that generates the first version of the output above; it's arguably a non-elegant hack:

#!/usr/bin/env bash
#
# Pretty-print 'iptables -nvL' output.
# 1. remove the 'pkts' and 'bytes' columns
# 2. make column-aligned, table-based output per
#    https://gist.github.com/Airdawg5571/1a8c49ca5dd97af55ab9
# 3. attempt to make narrower, removing unnecessary whitespace
#    between columns, to save previous display-screen realestate
#
iptables -nvL                                                      | \
    # remove the 'pkts' and 'bytes' columns
    awk '{ if ($1 != "Chain") { $1=""; $2=""; print } else print}' | \
    # make tabular (table) output
    column -t                                                      | \
    sed 's/^Chain/\n&/g'                                           | \
    sed '/^Chain/ s/[ \t]\{1,\}/ /g'                               | \
    sed '/^[0-9]/ s/[ \t]\{1,\}/ /10g'                             | \
    # reduce the unnecessary whitespace after 'prot' column
    perl -pe 's|^(?!Chain)([^\s]+\s+\w+)\s+|\1  \t|'               | \
    # arbitrarily truncate line length
    cut -c -120

Solution

  • This works:

    #!/usr/bin/env bash
    
    #
    # Pretty-print iptables(8) output.
    #
    # source:
    # https://gitlab.com/johnnyutahh/swmisc/-/blob/master/sysadmin/networking/iptables/iptables-list-pretty.sh
    #
    # (The following script was Ubuntu-18.04 tested on 2020-05-17.)
    #
    
    # Derivered from
    # https://www.reddit.com/r/bash/comments/gl61yb/selectively_remove_unnecessary_column_whitespace/fqw19tv
    
    # Adjust these values to resize column widths
    column_widths=(0 0 27 5 4 8 8 17 17)
    
    iptables_align()
    {
      while read line; do
        if [[ $line =~ Chain ]]; then
          echo "$line"
        else
          line=${line//\*/\\\*}
          array=($line)
          for n in {2..8}; do
            w=${column_widths[$n]}
             printf "%-${w}s" "${array[$n]}"
          done
          lastcol_with_spaces_in_content=("${array[@]:9}")
          printf "%s" "${lastcol_with_spaces_in_content[*]}"
          echo
        fi
      done
    }
    
    iptables -nvL | iptables_align | sed -s 's|\\\*|* |g' | less
    

    Source:

    https://gitlab.com/johnnyutahh/swmisc/-/blob/master/sysadmin/networking/iptables/iptables-list-pretty.sh