国内IPのみからsshを受け付ける設定(Fedora firewalld)

自宅なりレンタルなり、自前でサーバーを立ち上げて公開するとあっという間に世界中からアタックがやってきます。
うちでは会社や出先からちょっと設定をいじったりソースを取り出したりしたいなという時のために自宅鯖のsshを開けていて、
アタックがあったらメールでお知らせみたいな運用をしばらくはしていたのですが、多すぎて辟易してしまいました。
アタック元を見るとほとんど海外、とくにcn,kr,twあたりというのが実情でありまして、
sshを開けている目的からすると365日全世界にオープンにしておく必要はないので今は国内IP以外はfirewallではじいています。

そんなわけで、今回もまたFedoraのfirewalldネタとして書いてみることにします。

(実際は、自分だけしか使わないなら例えばポート変えちゃってもいいんですけどね)

国内IPの判別

世界の国別 IPv4 アドレス割り当てリスト http://nami.jp/ipv4bycc/
で使いやすく整理していただいているものをありがたく使わせて頂きます。
というかここでやっていることが上記サイトの例の応用にすぎません。

firewalldによる特定IPのアクセプト/リジェクト

細かい設定は、firewall-cmd --direct を通じて行います。オプションはiptablesと同様です。
例)

firewall-cmd --direct --add-rule ipv4 filter INPUT 1 -m conntrack --ctstate NEW -m tcp -p tcp --dport 22 -s 192.168.0.0/16 -j ACCEPT

方針

22番ポートに関して、以下の方針でfirewallを組みます。

  • 基本ポリシーは、REJECT
  • ローカルネットワークのIP(192.168.0.0)はACCEPT
  • 国内IPはACCEPT

スクリプト

上記方針を実現するため、 /usr/local/sbin/allow-ssh-from-jp.sh を以下の内容で作成します。

#!/bin/sh

WORK_DIR=/var/firewall

verbose_exec()
{
        echo $*
        $*
}

#
# cd to working dir
#
if [ ! -d $WORK_DIR ]; then
        mkdir -p $WORK_DIR
fi
cd $WORK_DIR

#
# if -dl option is set, download cidr.txt.gz
#
if [ "$1" = "-dl" ]; then
        if [ -f cidr.txt.gz ]; then
                mv cidr.txt.gz cidr-old.txt.gz
        fi
        wget http://nami.jp/ipv4bycc/cidr.txt.gz
        if [ $? -ne 0 ]; then
                rm cidr.txt.gz
        fi
fi

if [ ! -f cidr.txt.gz ]; then
        echo cidr.txt.gz not found.
        exit 1
fi

#
# remove all current rules regarding port 22
#
firewall-cmd --direct --get-rules ipv4 filter INPUT | grep '\--dport 22' | while read rule; do
        verbose_exec firewall-cmd --direct --remove-rule ipv4 filter INPUT $rule
done

#
# accept local network
#
verbose_exec firewall-cmd --direct --add-rule ipv4 filter INPUT 1 -m conntrack --ctstate NEW -m tcp -p tcp --dport 22 -s 192.168.0.0/16 -j ACCEPT

#
# reject all (priority=3)
#
verbose_exec firewall-cmd --direct --add-rule ipv4 filter INPUT 3 -m conntrack --ctstate NEW -m tcp -p tcp --dport 22 -j REJECT

#
# accept from JP (priority=2)
#
zcat cidr.txt.gz | sed -n 's/^JP\t//p' | while read address; do
        verbose_exec firewall-cmd --direct --add-rule ipv4 filter INPUT 2 -m conntrack --ctstate NEW -m tcp -p tcp --dport 22 -s $address -j ACCEPT
done

IPの割り当て状況は変わりますので、このスクリプトを定期的に実行してfirewallを更新していく必要があると思いますが、
リスト提供元のサーバ側に迷惑のかからないように頻度にはご注意ください。
また、スクリプト実行に結構な時間がかかります(手元で数分程度)。起動スクリプトに組み込む場合はバックグラウンドで実行する等の工夫が必要です。