iptables -S
出力を受け取り、次に変換するプログラムを探しています。幅優先チェックリスト。
なぜ?ルーターを使って作業しています。ビオ複数層のテーブルが事前にインストールされているため、INPUT、FORWARD、OUTPUTに接続されているすべてのルールを追跡することは困難です。
@JeffSchallerの[要求]に従って解析する必要があるサンプル出力は次のとおりです。
$ sudo iptables -S
-P INPUT ACCEPT
-P FORWARD ACCEPT
-P OUTPUT ACCEPT
-N LAN1_IN
-N MINIUPNPD
-N UBNT_FW_IN_SUSPEND_HOOK
-N UBNT_PFOR_FW_HOOK
-N UBNT_PFOR_FW_RULES
-N UBNT_VPN_IPSEC_FW_HOOK
-N UBNT_VPN_IPSEC_FW_IN_HOOK
-N VYATTA_FW_IN_HOOK
-N VYATTA_FW_LOCAL_HOOK
-N VYATTA_FW_OUT_HOOK
-N VYATTA_POST_FW_FWD_HOOK
-N VYATTA_POST_FW_IN_HOOK
-N VYATTA_POST_FW_OUT_HOOK
-N WAN_IN
-N WAN_LOCAL
-N WAN_OUT
-A INPUT -j UBNT_VPN_IPSEC_FW_HOOK
-A INPUT -j VYATTA_FW_LOCAL_HOOK
-A INPUT -j VYATTA_POST_FW_IN_HOOK
-A FORWARD -j MINIUPNPD
-A FORWARD -j UBNT_VPN_IPSEC_FW_IN_HOOK
-A FORWARD -j UBNT_PFOR_FW_HOOK
-A FORWARD -j UBNT_FW_IN_SUSPEND_HOOK
-A FORWARD -j VYATTA_FW_IN_HOOK
-A FORWARD -j VYATTA_FW_OUT_HOOK
-A FORWARD -j VYATTA_POST_FW_FWD_HOOK
-A OUTPUT -j VYATTA_POST_FW_OUT_HOOK
-A LAN1_IN -m comment --comment LAN1_IN-10 -m state --state INVALID -j LOG --log-prefix "[LAN1_IN-10-D]"
-A LAN1_IN -m comment --comment LAN1_IN-10 -m state --state INVALID -j DROP
-A LAN1_IN -p udp -m comment --comment LAN1_IN-20 -m udp --dport 53 -m set --match-set dnsaddr dst -j RETURN
-A LAN1_IN -p udp -m comment --comment LAN1_IN-30 -m set --match-set dnsaddr src -m udp --dport 53 -j RETURN
-A LAN1_IN -m comment --comment LAN1_IN-60 -m state --state NEW -j RETURN
-A LAN1_IN -m comment --comment LAN1_IN-70 -m state --state RELATED -j RETURN
-A LAN1_IN -m comment --comment LAN1_IN-80 -m state --state ESTABLISHED -j RETURN
-A LAN1_IN -m comment --comment "LAN1_IN-10000 default-action drop" -j LOG --log-prefix "[LAN1_IN-default-D]"
-A LAN1_IN -m comment --comment "LAN1_IN-10000 default-action drop" -j DROP
-A VYATTA_FW_IN_HOOK -i eth0 -j WAN_IN
-A VYATTA_FW_IN_HOOK -i eth1 -j LAN1_IN
-A VYATTA_FW_LOCAL_HOOK -i eth0 -j WAN_LOCAL
-A VYATTA_FW_OUT_HOOK -o eth0 -j WAN_OUT
-A VYATTA_POST_FW_FWD_HOOK -j ACCEPT
-A VYATTA_POST_FW_IN_HOOK -j ACCEPT
-A VYATTA_POST_FW_OUT_HOOK -j ACCEPT
-A WAN_IN -m comment --comment WAN_IN-10 -m state --state ESTABLISHED -j RETURN
-A WAN_IN -m comment --comment WAN_IN-20 -m state --state RELATED -j RETURN
-A WAN_IN -m comment --comment WAN_IN-30 -m state --state INVALID -j LOG --log-prefix "[WAN_IN-30-D]"
-A WAN_IN -m comment --comment WAN_IN-30 -m state --state INVALID -j DROP
-A WAN_IN -m comment --comment "WAN_IN-10000 default-action drop" -j DROP
-A WAN_LOCAL -m comment --comment WAN_LOCAL-10 -m state --state ESTABLISHED -j RETURN
-A WAN_LOCAL -m comment --comment WAN_LOCAL-20 -m state --state RELATED -j RETURN
-A WAN_LOCAL -m comment --comment WAN_LOCAL-30 -m state --state INVALID -j LOG --log-prefix "[WAN_LOCAL-30-D]"
-A WAN_LOCAL -m comment --comment WAN_LOCAL-30 -m state --state INVALID -j DROP
-A WAN_LOCAL -m comment --comment "WAN_LOCAL-10000 default-action drop" -j LOG --log-prefix "[WAN_LOCAL-default-D]"
-A WAN_LOCAL -m comment --comment "WAN_LOCAL-10000 default-action drop" -j DROP
-A WAN_OUT -m comment --comment WAN_OUT-10 -m state --state NEW -j RETURN
-A WAN_OUT -m comment --comment WAN_OUT-20 -m state --state RELATED -j RETURN
-A WAN_OUT -m comment --comment WAN_OUT-30 -m state --state ESTABLISHED -j RETURN
-A WAN_OUT -m comment --comment WAN_OUT-40 -m state --state INVALID -j LOG --log-prefix "[WAN_OUT-40-D]"
-A WAN_OUT -m comment --comment WAN_OUT-40 -m state --state INVALID -j DROP
-A WAN_OUT -m comment --comment "WAN_OUT-10000 default-action drop" -j LOG --log-prefix "[WAN_OUT-default-D]"
-A WAN_OUT -m comment --comment "WAN_OUT-10000 default-action drop" -j DROP
まず、投稿の後半で@LL3の答えを正しい答えとして選択しました。 @LL3の答えがstdinを読むことができるように修正されたので、同じパッチを削除しました。
<patch removed>
ありがとうperl
- 幅優先リストとは別のリストを示す(後で)完全な答えを与えた@JeffSchallerマスター グラフィックビジュアライゼーション出力。
答え1
しばらく前に、幅の優先順位でカスタムファイアウォールのルールツリーを平面化し、すべて1つのファイルに配置する必要がありました。とにかくVyOSではなくiptablesです。次のスクリプトを思いついたのに役立つことを確認してください。
参考にしてくださいこのスクリプトには少なくともBash v4が必要です。
#!/bin/bash -
declare -A all_chains=()
declare -A queued_chains=()
builtin_chains_as_regexp='INPUT|OUTPUT|FORWARD|PREROUTING|POSTROUTING'
queue_list=""
prepend_chain=""
show_chain_heading=false
one_go=false
uniquify=true
_print_usage() {
cat <<- EOF
Usage: $0 [-npofh] <starting-chain>
-n shows chain's creation command as heading, useful for spotting empty chains
-p prepends chain's name to each rule
-o read everything in one go, 10x quicker when many small chains
-f expand all references to a same chain, but beware of chain loops or chains referenced hundreds of times
-h shows this help
EOF
}
_expand_chain() {
local chain_to_expand="${1}"
local rules=""
# if one_go selected, work with in-memory cache of chains
if $one_go ; then
rules="${all_chains[${chain_to_expand}]}"
# otherwise read in chain to consider
else
rules="$(iptables -S "${chain_to_expand}")"
fi
$show_chain_heading && \
! [[ "${chain_to_expand}" =~ ${builtin_chains_as_regexp} ]] && \
echo "-N ${chain_to_expand}"
while read -r cmd chain rule ; do
case "${cmd}" in
-A)
set -- ${rule}
# look for target option in rule
while [ -n "${1}" ] && ! [[ "${1}" =~ -(j|g) ]] ; do shift ; done
# a few sanity checks
[ -n "${1}" ] || continue # a rule with no target, skip it
shift
[ -n "${1}" ] || { echo "what!? empty target in ${rule}" >&2 ; continue ; }
if [ -n "${all_chains[${1}]}" ] ; then
# if target is a chain
# add to queued chains if uniquify *not* requested or if chain never queued
if ! $uniquify || [ -z "${queued_chains[${1}]}" ] ; then
queue_list+="${1} "
queued_chains[${1}]="1"
fi
fi
# show rule
echo "${prepend_chain:+[${chain_to_expand}] }${cmd} ${chain} ${rule}"
;;
esac
done <<<"${rules}"
}
###
# ACTUAL EXECUTION STARTS HERE
#
# parse command options if any
while getopts nphfo option ; do
case $option in
n) show_chain_heading=true
;;
p) prepend_chain="1"
;;
h) _print_usage ; exit 0
;;
o) one_go=true
;;
f) uniquify=false
;;
'?') exit 1
;;
esac
done
[ -n "${!OPTIND}" ] || { _print_usage ; exit 1 ; }
# preparation step:
# if one_go selected, slurp everything in
if $one_go ; then
# invoke explicit command only when stdin is the terminal
[ -t 0 ] && exec 0< <(iptables -S)
while read -r cmd chain rule ; do
case "${cmd}" in
-N)
all_chains[${chain}]=" " # <<-- whitespace to make provision for empty chains
;;
-A)
# assign rule to its chain in cache
all_chains[${chain}]+=$'\n'"${cmd} ${chain} ${rule}"
;;
esac
done
# otherwise read in chain names only
else
while IFS= read -r chain ; do
all_chains[${chain}]="1"
done < <(iptables -S | sed -ne '/^-N /s///p')
fi
# expand starting chain
_expand_chain ${!OPTIND}
# breadth-first expand queued chains
# as long as queue is not empty
while [ "${#queue_list}" -gt 0 ] ; do
# take next queued chain
subchain="${queue_list%% *}"
# expand it
_expand_chain "${subchain}"
# remove expanded chain from queue
queue_list="${queue_list#${subchain} }"
# queue gets updated by _expand_chain as needed
done
exit 0
もちろん、コメントはあまりありませんが、Bashに慣れていると理解するのは難しくありません。
オプションなしで実行すると、ヘルプの概要が表示されます。
特に、複数回参照されるチェーンの場合でも、基本的に各チェーンを一度だけ拡張することに注意してください。オプションを使用すると、完全にフラット化された出力を要求できます-f
。これは何千もの異なるチェーンが参照する複数のチェーンがあり、それらをすべてマージするのに数時間かかるためです(もちろん、このスクリプトは並列処理を実行しません)。したがって、同様の設定がある場合は、この点に注意してください。
答え2
これはBFS出力シーケンスの説明ですiptables -S
。各ルールを読み、ターゲット(または-P
ポリシー)を見つけます。すべてのルールを読み取ったら、組み込みターゲットから始めて連続レベルのルールを印刷します。
iptables-bfs.pl
#!/usr/bin/perl -w
use strict;
# for now, a chain name has to match regex: [[:alnum:]_-]+
my %jumpsto = ();
while (<>) {
chomp;
next if /^#/;
if (/-[AIR]\s+([[:alnum:]_-]+).*-j\s+([[:alnum:]_-]+)/) {
unless (exists $jumpsto{$1}{$2}) {
$jumpsto{$1}{$2}=$_;
}
} elsif (/-P ([[:alnum:]_-]+)\s+(ACCEPT|DROP)/) {
unless (exists $jumpsto{$1}{$2}) {
$jumpsto{$1}{$2}=$_;
}
}
}
my @queue = ();
push @queue, qw(INPUT OUTPUT FORWARD PREROUTING POSTROUTING);
my @nextqueue = ();
while (@queue) {
my $item = shift @queue;
foreach my $target (keys %{ $jumpsto{$item} }) {
print $jumpsto{$item}{$target} . "\n";
push @nextqueue, $target;
}
if (! @queue && @nextqueue) {
@queue = @nextqueue;
@nextqueue = ();
print "---------------\n";
}
}
質問のサンプル入力に基づいて、出力は次のようになります。
-A INPUT -j UBNT_VPN_IPSEC_FW_HOOK
-P INPUT ACCEPT
-A INPUT -j VYATTA_POST_FW_IN_HOOK
-A INPUT -j VYATTA_FW_LOCAL_HOOK
-P OUTPUT ACCEPT
-A OUTPUT -j VYATTA_POST_FW_OUT_HOOK
-A FORWARD -j VYATTA_FW_IN_HOOK
-A FORWARD -j MINIUPNPD
-P FORWARD ACCEPT
-A FORWARD -j VYATTA_POST_FW_FWD_HOOK
-A FORWARD -j UBNT_VPN_IPSEC_FW_IN_HOOK
-A FORWARD -j UBNT_FW_IN_SUSPEND_HOOK
-A FORWARD -j UBNT_PFOR_FW_HOOK
-A FORWARD -j VYATTA_FW_OUT_HOOK
---------------
-A VYATTA_POST_FW_IN_HOOK -j ACCEPT
-A VYATTA_FW_LOCAL_HOOK -i eth0 -j WAN_LOCAL
-A VYATTA_POST_FW_OUT_HOOK -j ACCEPT
-A VYATTA_FW_IN_HOOK -i eth1 -j LAN1_IN
-A VYATTA_FW_IN_HOOK -i eth0 -j WAN_IN
-A VYATTA_POST_FW_FWD_HOOK -j ACCEPT
-A VYATTA_FW_OUT_HOOK -o eth0 -j WAN_OUT
---------------
-A WAN_LOCAL -m comment --comment WAN_LOCAL-30 -m state --state INVALID -j DROP
-A WAN_LOCAL -m comment --comment WAN_LOCAL-10 -m state --state ESTABLISHED -j RETURN
-A WAN_LOCAL -m comment --comment WAN_LOCAL-30 -m state --state INVALID -j LOG --log-prefix "[WAN_LOCAL-30-D]"
-A LAN1_IN -p udp -m comment --comment LAN1_IN-20 -m udp --dport 53 -m set --match-set dnsaddr dst -j RETURN
-A LAN1_IN -m comment --comment LAN1_IN-10 -m state --state INVALID -j DROP
-A LAN1_IN -m comment --comment LAN1_IN-10 -m state --state INVALID -j LOG --log-prefix "[LAN1_IN-10-D]"
-A WAN_IN -m comment --comment WAN_IN-30 -m state --state INVALID -j DROP
-A WAN_IN -m comment --comment WAN_IN-10 -m state --state ESTABLISHED -j RETURN
-A WAN_IN -m comment --comment WAN_IN-30 -m state --state INVALID -j LOG --log-prefix "[WAN_IN-30-D]"
-A WAN_OUT -m comment --comment WAN_OUT-40 -m state --state INVALID -j DROP
-A WAN_OUT -m comment --comment WAN_OUT-10 -m state --state NEW -j RETURN
-A WAN_OUT -m comment --comment WAN_OUT-40 -m state --state INVALID -j LOG --log-prefix "[WAN_OUT-40-D]"
---------------
私のオリジナル誤解iptables -S
graphviz互換ファイルに変換する次のPerlスクリプトは次のとおりです。ソースチェーンをターゲットチェーンにリンクするグラフを生成します。
iptables-dot.pl
#!/usr/bin/perl -w
use strict;
# for now, a chain name has to match regex: [[:alnum:]_-]+
print "digraph rules {\n";
print "\toverlap=scalexy;\n";
my %jumpsto = ();
while (<>) {
chomp;
next if /^#/;
if (/-[AIR]\s+([[:alnum:]_-]+).*-j\s+([[:alnum:]_-]+)/) {
unless (exists $jumpsto{$1}{$2}) {
print "\"$1\" -> \"$2\";\n";
$jumpsto{$1}{$2}=1;
}
} elsif (/-P ([[:alnum:]_-]+)\s+(ACCEPT|DROP)/) {
unless (exists $jumpsto{$1}{$2}) {
print "\"$1\" -> \"$2\";\n";
$jumpsto{$1}{$2}=1;
}
}
}
print "}\n";
問題にサンプル入力が与えられた場合、結果出力は次のようになります。
digraph rules {
overlap=scalexy;
"INPUT" -> "ACCEPT";
"FORWARD" -> "ACCEPT";
"OUTPUT" -> "ACCEPT";
"INPUT" -> "UBNT_VPN_IPSEC_FW_HOOK";
"INPUT" -> "VYATTA_FW_LOCAL_HOOK";
"INPUT" -> "VYATTA_POST_FW_IN_HOOK";
"FORWARD" -> "MINIUPNPD";
"FORWARD" -> "UBNT_VPN_IPSEC_FW_IN_HOOK";
"FORWARD" -> "UBNT_PFOR_FW_HOOK";
"FORWARD" -> "UBNT_FW_IN_SUSPEND_HOOK";
"FORWARD" -> "VYATTA_FW_IN_HOOK";
"FORWARD" -> "VYATTA_FW_OUT_HOOK";
"FORWARD" -> "VYATTA_POST_FW_FWD_HOOK";
"OUTPUT" -> "VYATTA_POST_FW_OUT_HOOK";
"LAN1_IN" -> "LOG";
"LAN1_IN" -> "DROP";
"LAN1_IN" -> "RETURN";
"VYATTA_FW_IN_HOOK" -> "WAN_IN";
"VYATTA_FW_IN_HOOK" -> "LAN1_IN";
"VYATTA_FW_LOCAL_HOOK" -> "WAN_LOCAL";
"VYATTA_FW_OUT_HOOK" -> "WAN_OUT";
"VYATTA_POST_FW_FWD_HOOK" -> "ACCEPT";
"VYATTA_POST_FW_IN_HOOK" -> "ACCEPT";
"VYATTA_POST_FW_OUT_HOOK" -> "ACCEPT";
"WAN_IN" -> "RETURN";
"WAN_IN" -> "LOG";
"WAN_IN" -> "DROP";
"WAN_LOCAL" -> "RETURN";
"WAN_LOCAL" -> "LOG";
"WAN_LOCAL" -> "DROP";
"WAN_OUT" -> "RETURN";
"WAN_OUT" -> "LOG";
"WAN_OUT" -> "DROP";
}
...結果は以下の通りです。より大きなバージョンを表示するには一度クリックし、ブラウザが自動的にサイズを縮小したらもう一度クリックします。