【さくらのVPS】iptablesでファイアーウォールを構築してみた

 iTunes Store(Japan)
この記事の所要時間: 2528

【さくらのVPS】初期設定をしてみたからかなり間があいてしまいましたが続きです ^^;

さて、今回はさくらのVPS上にiptablesでファイアーウォール(パケットフィルタリング)を構築します。
ファイアーウォールについてはこちら(小悪魔女子大生のサーバエンジニア日記 ファイアーウォールって何だろう?)を御覧ください。

ポリシー

ファイアーウォールは以下のポリシーで構築します。

  • 受信(入力)するパケットは原則破棄
  • 送信(出力)するパケットは原則通過
  • フォワードするパケットは原則破棄
  • 自ホスト(ループバックデバイス)からのパケットは全て許可
  • 内部ネットワーク(以降、内部)からのアクセスを全て許可
  • 内部から行ったアクセスに対する外部ネットワーク(以降、外部)からの返答アクセスを許可
  • SYN Cookiesを有効(TCP SYN Flood攻撃対策)
  • ブロードキャストアドレス宛pingへは返答しない(Smurf攻撃対策)
  • ICMP Redirectパケットは拒否
  • Source Routedパケットは拒否
  • フラグメント化されたパケットはログを記録して破棄
  • 外部とのNetBIOS関連のアクセスはログを記録せずに破棄(不要ログ記録防止のため)
  • 1秒間に4回を超えるpingはログを記録して破棄
  • 全ホスト(ブロードキャストアドレス、マルチキャストアドレス)宛パケットはログを記録せずに破棄
  • 133番ポート(IDENT)へのアクセスには拒否応答
  • 指定された国のIPアドレスからのアクセスを許可、拒否する(許可:日本 拒否:中国、韓国、台湾)
  • 公開するサービスだけを許可(SSH, DNS, HTTP, HTTPS, FTP, FTP-DATA, SMTP, SMTPS, POP3, POP3S, IMAP, IMAPS等)

設定する

  1. 上記のポリシーをそれぞれ書いていたら大変なのでスクリプトにします。
  2. iptables.sh(UTF-8のテキストファイル)
    このスクリプトを適当な場所に置きます。ボクは /root/script/ に置いてます。

    #!/bin/bash
    
    #---------------------------------------#
    # 設定開始                              #
    #---------------------------------------#
    
    # インタフェース名定義
    LAN=eth0
    
    #---------------------------------------#
    # 設定終了                              #
    #---------------------------------------#
    
    # 内部ネットワークのネットマスク取得
    LOCALNET_MASK=ifconfig $LAN|sed -e 's/^.*Mask:\([^ ]*\)$/\1/p' -e d
    
    # 内部ネットワークアドレス取得
    LOCALNET_ADDR=netstat -rn|grep $LAN|grep $LOCALNET_MASK|cut -f1 -d' '
    LOCALNET=$LOCALNET_ADDR/$LOCALNET_MASK
    
    
    # ファイアウォール停止(すべてのルールをクリア)
    /etc/rc.d/init.d/iptables stop
    
    # デフォルトルール(以降のルールにマッチしなかった場合に適用するルール)設定
    iptables -P INPUT   DROP   # 受信はすべて破棄
    iptables -P OUTPUT  ACCEPT # 送信はすべて許可
    iptables -P FORWARD DROP   # 通過はすべて破棄
    
    # 自ホストからのアクセスをすべて許可
    iptables -A INPUT -i lo -j ACCEPT
    
    # 内部からのアクセスをすべて許可
    iptables -A INPUT -s $LOCALNET -j ACCEPT
    
    # 内部から行ったアクセスに対する外部からの返答アクセスを許可
    iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
    
    # SYN Cookiesを有効にする
    # ※TCP SYN Flood攻撃対策
    sysctl -w net.ipv4.tcp_syncookies=1 > /dev/null
    sed -i '/net.ipv4.tcp_syncookies/d' /etc/sysctl.conf
    echo "net.ipv4.tcp_syncookies=1" >> /etc/sysctl.conf
    
    # ブロードキャストアドレス宛pingには応答しない
    # ※Smurf攻撃対策
    sysctl -w net.ipv4.icmp_echo_ignore_broadcasts=1 > /dev/null
    sed -i '/net.ipv4.icmp_echo_ignore_broadcasts/d' /etc/sysctl.conf
    echo "net.ipv4.icmp_echo_ignore_broadcasts=1" >> /etc/sysctl.conf
    
    # ICMP Redirectパケットは拒否
    sed -i '/net.ipv4.conf.*.accept_redirects/d' /etc/sysctl.conf
    for dev in ls /proc/sys/net/ipv4/conf/
    do
        sysctl -w net.ipv4.conf.$dev.accept_redirects=0 > /dev/null
        echo "net.ipv4.conf.$dev.accept_redirects=0" >> /etc/sysctl.conf
    done
    
    # Source Routedパケットは拒否
    sed -i '/net.ipv4.conf.*.accept_source_route/d' /etc/sysctl.conf
    for dev in ls /proc/sys/net/ipv4/conf/
    do
        sysctl -w net.ipv4.conf.$dev.accept_source_route=0 > /dev/null
        echo "net.ipv4.conf.$dev.accept_source_route=0" >> /etc/sysctl.conf
    done
    
    # フラグメント化されたパケットはログを記録して破棄
    iptables -A INPUT -f -j LOG --log-prefix '[IPTABLES FRAGMENT] : '
    iptables -A INPUT -f -j DROP
    
    # 外部とのNetBIOS関連のアクセスはログを記録せずに破棄
    # ※不要ログ記録防止
    iptables -A INPUT -s ! $LOCALNET -p tcp -m multiport --dports 135,137,138,139,445 -j DROP
    iptables -A INPUT -s ! $LOCALNET -p udp -m multiport --dports 135,137,138,139,445 -j DROP
    iptables -A OUTPUT -d ! $LOCALNET -p tcp -m multiport --sports 135,137,138,139,445 -j DROP
    iptables -A OUTPUT -d ! $LOCALNET -p udp -m multiport --sports 135,137,138,139,445 -j DROP
    
    # 1秒間に4回を超えるpingはログを記録して破棄
    # ※Ping of Death攻撃対策
    iptables -N LOG_PINGDEATH
    iptables -A LOG_PINGDEATH -m limit --limit 1/s --limit-burst 4 -j ACCEPT
    iptables -A LOG_PINGDEATH -j LOG --log-prefix '[IPTABLES PINGDEATH] : '
    iptables -A LOG_PINGDEATH -j DROP
    iptables -A INPUT -p icmp --icmp-type echo-request -j LOG_PINGDEATH
    
    # 全ホスト(ブロードキャストアドレス、マルチキャストアドレス)宛パケットはログを記録せずに破棄
    # ※不要ログ記録防止
    iptables -A INPUT -d 255.255.255.255 -j DROP
    iptables -A INPUT -d 224.0.0.1 -j DROP
    
    # 113番ポート(IDENT)へのアクセスには拒否応答
    # ※メールサーバ等のレスポンス低下防止
    iptables -A INPUT -p tcp --dport 113 -j REJECT --reject-with tcp-reset
    
    # ACCEPT_COUNTRY_MAKE関数定義
    # 指定された国のIPアドレスからのアクセスを許可するユーザ定義チェイン作成
    ACCEPT_COUNTRY_MAKE(){
        for addr in cat /tmp/cidr.txt|grep ^$1|awk '{print $2}'
        do
            iptables -A ACCEPT_COUNTRY -s $addr -j ACCEPT
        done
    }
    
    # DROP_COUNTRY_MAKE関数定義
    # 指定された国のIPアドレスからのアクセスを破棄するユーザ定義チェイン作成
    DROP_COUNTRY_MAKE(){
        for addr in cat /tmp/cidr.txt|grep ^$1|awk '{print $2}'
        do
            iptables -A DROP_COUNTRY -s $addr -m limit --limit 1/s -j LOG --log-prefix '[IPTABLES DENY_COUNTRY] : '
            iptables -A DROP_COUNTRY -s $addr -j DROP
        done
    }
    
    # IPアドレスリスト取得
    . /root/script/iptables_functions
    IPLISTGET
    
    # 日本からのアクセスを許可するユーザ定義チェインACCEPT_COUNTRY作成
    iptables -N ACCEPT_COUNTRY
    ACCEPT_COUNTRY_MAKE JP
    # 以降,日本からのみアクセスを許可したい場合はACCEPTのかわりにACCEPT_COUNTRYを指定する
    
    # 中国・韓国・台湾※からのアクセスをログを記録して破棄
    # ※全国警察施設への攻撃元上位3カ国(日本・アメリカを除く)
    # http://www.cyberpolice.go.jp/detect/observation.htmlより
    iptables -N DROP_COUNTRY
    DROP_COUNTRY_MAKE CN
    DROP_COUNTRY_MAKE KR
    DROP_COUNTRY_MAKE TW
    iptables -A INPUT -j DROP_COUNTRY
    
    #----------------------------------------------------------#
    # 各種サービスを公開する場合の設定(ここから)               #
    #----------------------------------------------------------#
    
    # 外部からのTCP22番ポート(SSH)へのアクセスを日本からのみ許可
    # ※SSHサーバーを公開する場合のみ
    iptables -A INPUT -p tcp --dport 22 -j ACCEPT_COUNTRY
    
    # 外部からのTCP/UDP53番ポート(DNS)へのアクセスを許可
    # ※外部向けDNSサーバーを運用する場合のみ
    iptables -A INPUT -p tcp --dport 53 -j ACCEPT
    iptables -A INPUT -p udp --dport 53 -j ACCEPT
    
    # 外部からのTCP80番ポート(HTTP)へのアクセスを許可
    # ※Webサーバーを公開する場合のみ
    iptables -A INPUT -p tcp --dport 80 -j ACCEPT
    
    # 外部からのTCP443番ポート(HTTPS)へのアクセスを許可
    # ※Webサーバーを公開する場合のみ
    iptables -A INPUT -p tcp --dport 443 -j ACCEPT
    
    # 外部からのTCP21番ポート(FTP)へのアクセスを日本からのみ許可
    # ※FTPサーバーを公開する場合のみ
    iptables -A INPUT -p tcp --dport 21 -j ACCEPT_COUNTRY
    
    # 外部からのPASV用ポート(FTP-DATA)へのアクセスを日本からのみ許可
    # ※FTPサーバーを公開する場合のみ
    # ※PASV用ポート60000:60030は当サイトの設定例
    iptables -A INPUT -p tcp --dport 60000:60030 -j ACCEPT_COUNTRY
    
    # 外部からのTCP25番ポート(SMTP)へのアクセスを許可
    # ※SMTPサーバーを公開する場合のみ
    #iptables -A INPUT -p tcp --dport 25 -j ACCEPT
    
    # 外部からのTCP465番ポート(SMTPS)へのアクセスを日本からのみ許可
    # ※SMTPSサーバーを公開する場合のみ
    #iptables -A INPUT -p tcp --dport 465 -j ACCEPT_COUNTRY
    
    # 外部からのTCP110番ポート(POP3)へのアクセスを日本からのみ許可
    # ※POP3サーバーを公開する場合のみ
    #iptables -A INPUT -p tcp --dport 110 -j ACCEPT_COUNTRY
    
    # 外部からのTCP995番ポート(POP3S)へのアクセスを日本からのみ許可
    # ※POP3Sサーバーを公開する場合のみ
    #iptables -A INPUT -p tcp --dport 995 -j ACCEPT_COUNTRY
    
    # 外部からのTCP143番ポート(IMAP)へのアクセスを日本からのみ許可
    # ※IMAPサーバーを公開する場合のみ
    #iptables -A INPUT -p tcp --dport 143 -j ACCEPT_COUNTRY
    
    # 外部からのTCP993番ポート(IMAPS)へのアクセスを日本からのみ許可
    # ※IMAPSサーバーを公開する場合のみ
    #iptables -A INPUT -p tcp --dport 993 -j ACCEPT_COUNTRY
    
    # 外部からのUDP1194番ポート(OpenVPN)へのアクセスを日本からのみ許可
    # ※OpenVPNサーバーを公開する場合のみ
    # iptables -A INPUT -p udp --dport 1194 -j ACCEPT_COUNTRY
    
    # VPNインタフェース用ファイアウォール設定
    # ※OpenVPNサーバーを公開する場合のみ
    # [ -f /etc/openvpn/openvpn-startup ] && /etc/openvpn/openvpn-startup
    
    
    #----------------------------------------------------------#
    # 各種サービスを公開する場合の設定(ここまで)               #
    #----------------------------------------------------------#
    
    # 拒否IPアドレスからのアクセスはログを記録せずに破棄
    # ※拒否IPアドレスは/root/script/deny_ipに1行ごとに記述しておくこと
    # (/root/script/deny_ipがなければなにもしない)
    if [ -s /root/script/deny_ip ]; then
        for ip in cat /root/script/deny_ip
        do
            iptables -I INPUT -s $ip -j DROP
        done
    fi
    
    # 上記のルールにマッチしなかったアクセスはログを記録して破棄
    iptables -A INPUT -m limit --limit 1/s -j LOG --log-prefix '[IPTABLES INPUT] : '
    iptables -A INPUT -j DROP
    iptables -A FORWARD -m limit --limit 1/s -j LOG --log-prefix '[IPTABLES FORWARD] : '
    iptables -A FORWARD -j DROP
    
    # サーバー再起動時にも上記設定が有効となるようにルールを保存
    /etc/rc.d/init.d/iptables save
    
    # ファイアウォール起動
    /etc/rc.d/init.d/iptables start
    

    上のiptables.sh内で呼び出している外部関数iptables_functionsです。
    iptables_functions
    これも上のスクリプトと同じディレクトリに置きます。

    # IPアドレスリスト取得関数定義
    IPLISTGET(){
        # http://nami.jp/ipv4bycc/から最新版IPアドレスリストを取得する
        wget -q http://nami.jp/ipv4bycc/cidr.txt.gz
        gunzip cidr.txt.gz
        # 最新版IPアドレスリストが取得できなかった場合
        if [ ! -f cidr.txt ]; then
            if [ -f /tmp/cidr.txt ]; then
                # バックアップがある場合はその旨をroot宛にメール通知して処理を打ち切る
                echo cidr.txt was read from the backup! | mail -s $0 root
                exit 1
            else
                # バックアップがない場合はその旨をroot宛にメール通知して処理を打ち切る
                echo cidr.txt not found!|mail -s $0 root
                exit 1
            fi
        fi
        # 最新版IPアドレスリストを /tmpへバックアップする
        /bin/mv cidr.txt /tmp/cidr.txt
    }
    
  3. IPアドレスリストの更新チェックスクリプトを用意します
  4. IPアドレスリストは頻繁に更新されるため、毎日cronで更新有無をチェックし、更新がある場合はファイアーウォール設定スクリプトを再起動するようにします。
    iplist_check.sh
    このファイルはcron.dailyで実行させるので /etc/cron.daily/ に置きます。

    #!/bin/bash
    
    PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
    
    # 新旧IPLIST差分チェック件数(0を指定するとチェックしない)
    # ※新旧IPLIST差分がSABUN_CHKで指定した件数を越える場合はiptables設定スクリプトを実行しない
    # ※新旧IPLIST差分チェック理由はhttp://centossrv.com/bbshtml/webpatio/1592.shtmlを参照
    SABUN_CHK=10
    [ $# -ne 0 ] && SABUN_CHK=${1}
    
    # チェック国コード
    COUNTRY_CODE='JP CN KR TW'
    
    # iptables設定スクリプトパス
    IPTABLES=/root/script/iptables.sh
    
    # iptables設定スクリプト外部関数取り込み
    . /root/script/iptables_functions
    
    # IPアドレスリスト最新化
    rm -f IPLIST.new
    IPLISTGET
    for country in $COUNTRY_CODE
    do
        if [ -f /tmp/cidr.txt ]; then
            grep ^$country /tmp/cidr.txt >> IPLIST.new
        else
            grep ^$country /tmp/IPLIST >> IPLIST.new
        fi
    done
    [ ! -f /tmp/IPLIST ] && cp IPLIST.new /tmp/IPLIST
    
    # IPアドレスリスト更新チェック
    diff -q /tmp/IPLIST IPLIST.new > /dev/null 2>&1
    if [ $? -ne 0 ]; then
        if [ ${SABUN_CHK} -ne 0 ]; then
            if [ $(diff /tmp/IPLIST IPLIST.new | egrep '<|>' | wc -l) -gt ${SABUN_CHK} ]; then
                (
                 diff /tmp/IPLIST IPLIST.new
                 echo
                 echo "$IPTABLES not executed."
                ) | mail -s 'IPLIST UPDATE' root
                rm -f IPLIST.new
                exit
            fi
        fi
        /bin/mv IPLIST.new /tmp/IPLIST
        $IPTABLES > /dev/null
    else
        rm -f IPLIST.new
    fi
    
  5. rootユーザでiptablesを実行します。
  6. $ su -
    パスワード:
    # cd script/
    # sh iptables.sh 
    ファイアウォールルールを適用中:                            [  OK  ]
    チェインポリシーを ACCEPT に設定中filter                   [  OK  ]
    iptables モジュールを取り外し中                            [  OK  ]
    ファイアウォールのルールを /etc/sysconfig/iptables に保存中[  OK  ]
    ファイアウォールルールを適用中:                            [  OK  ]
    チェインポリシーを ACCEPT に設定中filter                   [  OK  ]
    iptables モジュールを取り外し中                            [  OK  ]
    iptables ファイアウォールルールを適用中:                   [  OK  ]
    

    それなりに時間がかかるので気長に待ちましょうw

  7. iptablesを自動起動するように設定します。
  8. # chkconfig iptables on
    

以上でiptablesの設定になります。
今回は、中国、韓国、台湾からアクセスを拒否するようにしましたが、拒否する必要がないと思う場合は、その箇所をコメントアウト等することでオープンになります。しかし、毎日数件変なポートにアクセスが来ますので注意が必要です。

その前に、さくら内部からくるアレをどうにかしてくれw
今日の午前4時のログより

 ################### Logwatch 7.3 (03/24/06) #################### 
        Processing Initiated: Tue Apr 12 03:59:03 2011
        Date Range Processed: yesterday
                              ( 2011-Apr-11 )
                              Period is day.
      Detail Level of Output: 0
              Type of Output: unformatted
           Logfiles for Host: vps.iex3.info
  ################################################################## 
 
(中略)
 --------------------- pam_unix Begin ------------------------ 

 sshd:
    Authentication Failures:
       unknown (www19177u.sakura.ne.jp): 1983 Time(s)
       root (www19177u.sakura.ne.jp): 33 Time(s)
       mail (www19177u.sakura.ne.jp): 21 Time(s)
       ftp (www19177u.sakura.ne.jp): 4 Time(s)
       operator (www19177u.sakura.ne.jp): 3 Time(s)
       adm (www19177u.sakura.ne.jp): 2 Time(s)
       mailnull (www19177u.sakura.ne.jp): 2 Time(s)
       bin (www19177u.sakura.ne.jp): 1 Time(s)
       daemon (www19177u.sakura.ne.jp): 1 Time(s)
       games (www19177u.sakura.ne.jp): 1 Time(s)
       gopher (www19177u.sakura.ne.jp): 1 Time(s)
       halt (www19177u.sakura.ne.jp): 1 Time(s)
       lp (www19177u.sakura.ne.jp): 1 Time(s)
       news (www19177u.sakura.ne.jp): 1 Time(s)
       nfsnobody (www19177u.sakura.ne.jp): 1 Time(s)
       nobody (www19177u.sakura.ne.jp): 1 Time(s)
       rpc (www19177u.sakura.ne.jp): 1 Time(s)
       rpcuser (www19177u.sakura.ne.jp): 1 Time(s)
       shutdown (www19177u.sakura.ne.jp): 1 Time(s)
       smmsp (www19177u.sakura.ne.jp): 1 Time(s)
       sshd (www19177u.sakura.ne.jp): 1 Time(s)
       sync (www19177u.sakura.ne.jp): 1 Time(s)
       uucp (www19177u.sakura.ne.jp): 1 Time(s)
       xfs (www19177u.sakura.ne.jp): 1 Time(s)
    Invalid Users:
       Unknown Account: 1985 Time(s)

(中略) 
 ---------------------- pam_unix End ------------------------- 

(中略)

 ###################### Logwatch End #########################