Quantcast
Channel: Bashタグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 2883

iptables VPN接続許可アドレスを追加するスクリプト

$
0
0
背景 RaspberryPiにSoftEther VPN https://ja.softether.org/ を入れて、遠隔地からRaspberryPi配下のLANのNasneやブルーレイレコーダをDiXiMソフトで再生したり、RaspberryPiのSSDにローカルアクセスして便利に使っている。iOS/Android標準のVPN設定や、FireHDでOpenVPNconnectアプリなどサクッとVPN接続できるのはありがたい。 しかしVPNのポートを全世界に向け開けっぱなしにすると、SoftEtherのログファイルserver_log/vpn_YYYYMMDD.logに毎日数十万ラインのアクセスを試みる怪しいIPアドレスが出現する。iptablesの豊富な機能で抑え込む方法もあるかもしれないが、ここでは、アクセスを許すホワイトリストのIPアドレスにのみVPNのポートを接続を許可するbashスクリプトを用意することにした。 前提 iptablesコマンドで、WAN側との管理が出来ている環境に、 iptables -A INPUT -p udp -m multiport --dports ポート -s IPアドレス -j ACCEPT を追加して、IPアドレスからのポートを接続を許可させる。 遠隔地のIPアドレスは、https://ipinfo.io/what-is-my-ip からグローバルIPアドレスAA.BB.CC.DD(たいていはDynamicアドレス)を得る。また、このページの Connection > Route の AA.BB.0.0/NN 表示を採用するか、whois AA.BB.CC.DD からCIDR表記のIPアドレス範囲を得る。 使い方例 AA.BB.0.0/NN 行を含む ipt_PROVIDER1.txt を用意する。  但しAA.BB.CC.DD/32 のときは、/32を付けずに、    AA.BB.CC.DD とする。 # cat /usr/local/IPT_DATA/ipt_PROVIDER1.txt AA.BB.0.0/NN # comment # ipt_ALLOW.sh -a ipt_PROVIDER1.txt と実行すると 1: iptables -A INPUT -p udp -m multiport --dports 500,1194,4500 -s AA.BB.0.0/NN -j ACCEPT 1: iptables -A INPUT -p tcp -m multiport --dports 443,992,1194,5555 -s AA.BB.0.0/NN -j ACCEPT ipt_PROVIDER1.txt ipt_PROVIDER2.txt ipt_PROVIDER3.txt とか 個別にポートを接続を許可のテスト確認ができたなら、ipt_addr.txt に集めておく。RaspberryPiが再起動された場合など、 ipt_ALLOW.sh -a と実行して設定の復帰が ipt_addr.txt から行われる。 実行のイメージ # egrep -v '^\s*#|^\s*$' /usr/local/IPT_DATA/ipt_addr.txt AAA.BBB.0.0/16 # PROVIDER1 CC.DD.0.0/16 # PROVIDER2 EEE.FFF.GG.0/19 # PROVIDER3 # ipt_ALLOW.sh -a 1: iptables -A INPUT -p udp -m multiport --dports 500,1194,4500 -s AAA.BBB.0.0/16 -j ACCEPT 1: iptables -A INPUT -p tcp -m multiport --dports 443,992,1194,5555 -s AAA.BBB.0.0/16 -j ACCEPT 2: iptables -A INPUT -p udp -m multiport --dports 500,1194,4500 -s CC.DD.0.0/16 -j ACCEPT 2: iptables -A INPUT -p tcp -m multiport --dports 443,992,1194,5555 -s CC.DD.0.0/16 -j ACCEPT 3: iptables -A INPUT -p udp -m multiport --dports 500,1194,4500 -s EEE.FFF.GG.0/19 -j ACCEPT 3: iptables -A INPUT -p tcp -m multiport --dports 443,992,1194,5555 -s EEE.FFF.GG.0/19 -j ACCEPT # ipt_ALLOW.sh -a -s http 1: iptables -A INPUT -p tcp --dport 80 -s AAA.BBB.0.0/16 -j ACCEPT 2: iptables -A INPUT -p tcp --dport 80 -s CC.DD.0.0/16 -j ACCEPT 3: iptables -A INPUT -p tcp --dport 80 -s EEE.FFF.GG.0/19 -j ACCEPT # ipt_ALLOW.sh -d -s http 1: iptables -D INPUT -p tcp --dport 80 -s AAA.BBB.0.0/16 -j ACCEPT 2: iptables -D INPUT -p tcp --dport 80 -s CC.DD.0.0/16 -j ACCEPT 3: iptables -D INPUT -p tcp --dport 80 -s EEE.FFF.GG.0/19 -j ACCEPT 既知の問題 CIDR IPアドレス表記は、AA.BB.CC.DD/NN のうち数字ピリオドフォーマットしかチェックしておらず、論理的な誤りがある場合は、iptablesに想定の登録ができない。 -c | --check) # ルール反映チェックである程度誤りは検出できるハズ。 bash スクリプトコード /usr/local/bin/ipt_ALLOW.sh #!/bin/bash ### $Id: ipt_ALLOW.sh,v 1.00 2021/06/12 16:50:50 rapiq $ # -*- coding: utf-8 -*- PROGNAME=$(basename $0) # 本ipt_ALLOW.shスクリプトは、CIDR IPアドレスからの # VPNポート接続をiptablesで許可操作(ルール追加削除)する # -A, -a ルール追加 -D, -d ルール削除 usage() { echo "Usage: $PROGNAME [OPTIONS] FILE" echo " This script is 'iptables -A|-D ' From FILE." echo " FILE[ipt_addr.txt] ... CIDR IP address text." echo "Options:" echo " -h, --help" echo " -a, --add_rule" # -A, -a ルール追加 echo " -d, --del_rule" # -D, -d ルール削除 echo " -v, --verbose" # 操作ログ表示 echo " -c, --check" # ルール反映チェック echo " -s, --service" # サービス名 vpn ssh http smtp で開放ポートを選ぶ } # SoftEther VPN Port udp 500,1194,4500 / tcp 443,992,1194,5555 vpn_ADD_DEL() { UDP_CIDR=`grep udp $ACCEPT_Ln|grep 500,1194,4500|grep $CIDR` if [ $? = $JUDGE ]; then # udp での登録状態はどうか if [ $JUDGE = 0 ]; then # 登録があるのに追加しようとしている SKIP_UDP="DUP. `echo "$UDP_CIDR"|awk '{print $1,$4,$8}'`" else # 登録がないのに削除しようとしている SKIP_UDP="REGISTER rule NOTHING $CIDR" fi echo "${CUR}: ${IPARAM}!SKIP udp $SKIP_UDP" | tee -a $LOGF # 操作はスキップ else COM_UDP="iptables $IPARAM INPUT -p udp -m multiport --dports 500,1194,4500 -s $CIDR -j ACCEPT" echo "${CUR}: ${COM_UDP}" | tee -a $LOGF if [ ! -z $ARG_V ]; then # -z 文字列長が 0 なら真 echo "${CUR}: ${COM_UDP}" fi # udp 追加 削除 を実行する $COM_UDP if [ $? != 0 ]; then # コマンドは正常終了か echo "${CUR}: iptables $IPARAM $CIDR ERROR." | tee -a $LOGF else # udp 追加 削除 できているか if [ ! -z $ARG_C ]; then # ルール反映チェック # 期待通りにiptables -A/-D が実行できている確認をする(くどい) UDP_GREP=`iptables -L -n|egrep '^ACCEPT|udp'|grep 500,1194,4500|grep $CIDR` if [ $? != $JUDGE ]; then # udp での登録状態はどうか if [ $JUDGE = 0 ]; then # 追加したが登録できていない SKIP_UDP=" NOT rule ADD. `echo "$UDP_CIDR"|awk '{print $1,$4,$8}'`" else # 削除したが登録が残っている SKIP_UDP=" NOT rule DELETE. `echo "$UDP_CIDR"|awk '{print $1,$4,$8}'`" fi echo "${CUR}: !SKIP udp $SKIP_UDP" | tee -a $LOGF # 操作はスキップ if [ ! -z $ARG_V ]; then echo "${CUR}: !SKIP udp $SKIP_UDP" fi else # ルール反映チェックは成功 if [ $JUDGE = 1 ]; then # 削除できている Check OK echo "${CUR}: Check DELETE rule OK. $CIDR" else # 追加できている Check OK echo "${CUR}: Check ADD rule OK. $CIDR" fi fi fi fi fi TCP_CIDR=`grep tcp $ACCEPT_Ln|grep 443,992,1194,5555|grep $CIDR` if [ $? = $JUDGE ]; then # tcp での登録状態はどうか if [ $JUDGE = 0 ]; then # 登録があるのに追加しようとしている SKIP_TCP="DUP. `echo "$TCP_CIDR"|awk '{print $1,$4,$8}'`" else # 登録がないのに削除しようとしている SKIP_TCP="REGISTER rule NOTHING $CIDR" fi echo "${CUR}: ${IPARAM}!SKIP tcp $SKIP_TCP" | tee -a $LOGF # 操作はスキップ else COM_TCP="iptables $IPARAM INPUT -p tcp -m multiport --dports 443,992,1194,5555 -s $CIDR -j ACCEPT" echo "${CUR}: ${COM_TCP}" | tee -a $LOGF if [ ! -z $ARG_V ]; then # -z 文字列長が 0 なら真 echo "${CUR}: ${COM_TCP}" fi # tcp 追加 削除 を実行する $COM_TCP if [ $? != 0 ]; then echo "${CUR}: iptables $IPARAM $CIDR ERROR." | tee -a $LOGF else # tcp 追加 削除 できているか if [ ! -z $ARG_C ]; then # ルール反映チェック # 期待通りにiptables -A/-D が実行できている確認をする(くどい) TCP_GREP=`iptables -L -n|egrep '^ACCEPT|tcp'|grep 500,1194,4500|grep $CIDR` if [ $? != $JUDGE ]; then # tcp での登録状態はどうか if [ $JUDGE = 0 ]; then # 追加したが登録できていない SKIP_TCP=" NOT rule ADD. `echo "$TCP_CIDR"|awk '{print $1,$4,$8}'`" else # 削除したが登録が残っている SKIP_TCP=" NOT rule DELETE. `echo "$TCP_CIDR"|awk '{print $1,$4,$8}'`" fi echo "${CUR}: !SKIP tcp $SKIP_TCP" | tee -a $LOGF # 操作はスキップ if [ ! -z $ARG_V ]; then echo "${CUR}: !SKIP tcp $SKIP_TCP" fi else # ルール反映チェックは成功 if [ $JUDGE = 1 ]; then # 削除できている Check OK echo "${CUR}: Check DELETE rule OK. $CIDR" else # 追加できている Check OK echo "${CUR}: Check ADD rule OK. $CIDR" fi fi fi fi fi } tcp1_ADD_DEL() { # TCP 1 PORT 操作 TCPPORT1=$1 TCP_CIDR=`grep tcp $ACCEPT_Ln|grep dpt:$TCPPORT1|grep $CIDR` if [ $? = $JUDGE ]; then # tcp での登録状態はどうか if [ $JUDGE = 0 ]; then # 登録があるのに追加しようとしている SKIP_TCP="DUP. `echo "$TCP_CIDR"|awk '{print $1,$4,$8}'`" else # 登録がないのに削除しようとしている SKIP_TCP="REGISTER rule NOTHING $CIDR" fi echo "${CUR}: ${IPARAM}!SKIP tcp $SKIP_TCP" | tee -a $LOGF # 操作はスキップ else COM_TCP="iptables $IPARAM INPUT -p tcp --dport $TCPPORT1 -s $CIDR -j ACCEPT" echo "${CUR}: ${COM_TCP}" | tee -a $LOGF if [ ! -z $ARG_V ]; then # -z 文字列長が 0 なら真 echo "${CUR}: ${COM_TCP}" fi # tcp 追加 削除 を実行する $COM_TCP if [ $? != 0 ]; then echo "${CUR}: iptables $IPARAM $CIDR ERROR." | tee -a $LOGF else # tcp 追加 削除 できているか if [ ! -z $ARG_C ]; then # ルール反映チェック # 期待通りにiptables -A/-D が実行できている確認をする(くどい) TCP_GREP=`iptables -L -n|egrep '^ACCEPT|tcp'|grep dpt:${TCPPORT1}|grep $CIDR` if [ $? != $JUDGE ]; then # tcp での登録状態はどうか if [ $JUDGE = 0 ]; then # 追加したが登録できていない SKIP_TCP=" NOT rule ADD. `echo "$TCP_CIDR"|awk '{print $1,$4,$8}'`" else # 削除したが登録が残っている SKIP_TCP=" NOT rule DELETE. `echo "$TCP_CIDR"|awk '{print $1,$4,$8}'`" fi echo "${CUR}: !SKIP tcp $SKIP_TCP" | tee -a $LOGF # 操作はスキップ if [ ! -z $ARG_V ]; then echo "${CUR}: !SKIP tcp $SKIP_TCP" fi else # ルール反映チェックは成功 if [ $JUDGE = 1 ]; then # 削除できている Check OK echo "${CUR}: Check DELETE rule OK. $CIDR" else # 追加できている Check OK echo "${CUR}: Check ADD rule OK. $CIDR" fi fi fi fi fi } # SSH Port tcp 22 ssh_ADD_DEL() { tcp1_ADD_DEL 22 # SSH } # HTTP Port tcp 80 http_ADD_DEL() { tcp1_ADD_DEL 80 # HTTP } # SMTP Port tcp 25 smtp_ADD_DEL() { tcp1_ADD_DEL 25 # SMTP } # # 引数 オプション ファイル名 の処理 # OPT=`getopt -o hadvcs: -l help,add_rule,del_rule,verbose,check,service: -- "$@"` if [ $? != 0 ] ; then exit 1 fi eval set -- "$OPT" while true do case "$1" in -h | --help) usage; exit 1 ;; -a | --add_rule) # -A, -a ルール追加 ARG_A=1; shift 1 ;; -d | --del_rule) # -D, -d ルール削除 ARG_D=1; shift 1 ;; -v | --verbose) # 操作ログ表示 ARG_V=1; shift 1 ;; -c | --check) # ルール反映チェック ARG_C=1; shift 1 ;; -s | --service) # サービス名 vpn ssh http smtp で開放ポートを選ぶ ARG_S=1; shift 1; VALUE_S="$1"; shift 1 ;; --) shift; param+=( "$@" ); break ;; *) echo "ERR_F0020 Internal error!" 1>&2; exit 1 ;; esac done if [ "$ARG_A" = "" ] && [ "$ARG_D" = "" ]; then usage echo "ERR_F0030 !!! MUST USE -a or -d" exit 1 fi if [ ${EUID} != 0 ]; then # 実行UIDが "0"(root)で管理者権限 echo "ERR_F0010 User not root." exit 1 fi if [ ! -z $ARG_V ]; then echo "START: $PROGNAME at `date`" fi # サービス名 (-s --service) vpn ssh http smtp で開放ポートを選ぶ if [ "$VALUE_S" = "" ]; then SRVNAME="vpn" ### 省略時サービス名 else SRVNAME="$VALUE_S" fi IPTDIR="/usr/local/IPT_DATA" if [ ! -d ${IPTDIR} ]; then echo "ERR_F0040 $IPTDIR NOTEXISTS." exit 1 fi ACCEPT_Ln="${IPTDIR}/ACCEPT_Ln" iptables -L -n|egrep ^ACCEPT|sort -k 4 > $ACCEPT_Ln cd ${IPTDIR} if [ $? != 0 ]; then echo "ERR_F0050 cd $IPTDIR." exit 1 fi LOGDIR="${IPTDIR}/LOG" if [ ! -d ${LOGDIR} ]; then mkdir -p ${LOGDIR} if [ $? != 0 ]; then echo "ERR_F0060 $LOGDIR Cannot mkdir." exit 1 fi fi YMDHM=`date '+%Y%m%d_%H%M'` if [ "$ARG_A" != "" ]; then IPARAM="-A" LOGF="${LOGDIR}/${SRVNAME}_${YMDHM}_A.log" JUDGE=0 # grep exists ERROR 0 if a line is selected fi if [ "$ARG_D" != "" ]; then IPARAM="-D" LOGF="${LOGDIR}/${SRVNAME}_${YMDHM}_D.log" JUDGE=1 # grep NOT exists ERROR 1 if no lines were selected fi if [ "$param" = "" ]; then IPTFILE="${IPTDIR}/ipt_addr.txt" else IPTFILE="${IPTDIR}/${param}" fi if [ ! -f "$IPTFILE" ]; then echo "ERR_F0070 $IPTFILE NOT FOUND." exit 1 fi LSFILE=`ls -l ${IPTFILE}|awk '{print $5,$6,$7,$8,$9}'` echo "" >> $LOGF echo "${YMDHM} IPARAM=${IPARAM} $LSFILE" >> $LOGF CUR=0 ######################################################### while read pline # $IPTFILE を一行読み込む # do ######################################################### # egrep で#で始まるコメント行および空白行を削除する echo $pline | egrep -q '^\s*#|^\s*$' if [ $? = 0 ]; then continue fi if [ "$pline" = "EOF" ]; then if [ ! -z $ARG_V ]; then echo "${IPTFILE} EOF Detected." fi break fi CUR=`expr ${CUR} + 1` if [ ! -z $ARG_V ]; then echo "${CUR}: ${pline}" fi echo "${CUR}: ${pline}" >> $LOGF CIDR=`echo ${pline}|awk '{print $1}'` echo $CIDR | perl -ne '/^\d+\.\d+\.\d+\.\d+\/\d+$/ and exit 1';RET=$? # Classless Inter-Domain Routing サイダー クラスレスアドレッシング if [ $RET != 1 ]; then ### 数字のみで整合性のチェックではない echo $CIDR | perl -ne '/^\d+\.\d+\.\d+\.\d+/ and exit 1';RET2=$? if [ $RET2 != 1 ]; then ### /NN がない場合もある echo "${CUR}: $CIDR ADDRESS Missing." | tee -a $LOGF exit 0 fi fi case "$SRVNAME" in vpn) # SoftEther VPN Port udp 500,1194,4500 / tcp 443,992,1194,5555 vpn_ADD_DEL ;; ssh) # SSH Port tcp 22 ssh_ADD_DEL ;; http) # HTTP Port tcp 80 http_ADD_DEL ;; smtp) # SMTP Port tcp 25 smtp_ADD_DEL ;; *) echo "ERR_F0080 $SRVNAME service NOT FOUND." exit 1 ;; esac done < $IPTFILE SEQ=`printf "%03d" ${CUR}` ENDM1="${IPTFILE} iptables $IPARAM COUNT=${SEQ} END." >> $LOGF ENDM2="LOGFILE: $LOGF" >> $LOGF ENDM3="END: $PROGNAME at `date`" >> $LOGF if [ ! -z $ARG_V ]; then echo $ENDM1 echo $ENDM2 echo $ENDM3 fi # end of ipt_ALLOW.sh

Viewing all articles
Browse latest Browse all 2883

Trending Articles