"iptables -S" 출력을 너비 우선 목록으로 변환하는 방법

"iptables -S" 출력을 너비 우선 목록으로 변환하는 방법

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

얼마 전에는 너비 우선 방식으로 사용자 정의 방화벽의 규칙 트리를 평면화하고 모두 하나의 파일에 넣어야 했습니다. 어쨌든 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 -Sgraphviz 호환 파일로 변환할 다음 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";
}

...결과는 아래와 같습니다. 더 큰 버전을 보려면 한 번 클릭하고, 브라우저가 자동으로 크기를 줄이면 다시 클릭하세요.

iptables 체인 대상

관련 정보