CentOS7.2 で firewalld、fail2ban、ipset で国内外IPでの火壁を構築

sshポートは日本国内のみ許可したいとか、特定の国のIPは ブラックリスト としてすべてのパケットを破棄するとか

IPアドレスリスト取得スクリプト作成

[root@XXXXXX ~]# vim get_iplist.sh
get_iplist.sh
# http://nami.jp/ipv4bycc/から最新版IPアドレスリストを取得する
wget -q http://nami.jp/ipv4bycc/cidr.txt.gz

# 展開
gunzip cidr.txt.gz

# 取得できなかった場合
if [ ! -f cidr.txt ]; then
    if [ -f /tmp/cidr.txt ]; then
        # バックアップがある場合はその旨をroot宛にメール通知して処理を打ち切る
        echo cidr.txt was read from the backup! | mail -s root
        exit 1
    else
        # バックアップがない場合はその旨をroot宛にメール通知して処理を打ち切る
        echo cidr.txt not found!|mail -s $0 root
        exit 1
    fi
fi

# バックアップがある場合は更新日時を比較して、更新が無かった場合には処理を打ち切る
if [ -f /tmp/cidr.txt ]; then
    TIMEA=`date -r /tmp/cidr.txt +%s`
    TIMEB=`date -r cidr.txt +%s`

    if [ $TIMEA -eq $TIMEB ]; then
        rm -f cidr.txt
        echo TIMEA eq TIMEB!|mail -s $0 root
        exit 1
    fi
fi

# firewalldを停止
systemctl stop firewalld.service

# ipsetのすべてのセットを削除します
ipset destroy 

# ホワイトリストセットを作成
ipset create -exist WHITELIST hash:net

# ブラックリストセットを作成
ipset create -exist BLACKLIST hash:net

# ホワイトリストセットに日本のIPアドレスを登録
sed -n 's/^JP\t//p' cidr.txt | while read ADDRESS; do
    ipset add WHITELIST $ADDRESS
done

# ここでは例としてブラックリストセットに中国のIPアドレスを登録(あくまで例として)
sed -n 's/^CN\t//p' cidr.txt | while read ADDRESS; do
    ipset add BLACKLIST $ADDRESS
done

# 他にもブラックリストに登録したい国があれば以下の[国コード]部分を置換して追加していく
#sed -n 's/^[国コード]\t//p' cidr.txt | while read ADDRESS; do
#    ipset add BLACKLIST $ADDRESS
#done

# firewalldを再開
systemctl start firewalld.service

# 最新版IPアドレスリストをバックアップ
/bin/mv cidr.txt /tmp/cidr.txt

作成したスクリプトに実行権限を付加

[root@centos7 ~]# chmod +x get_iplist.sh

実行してみます

[root@centos7 ~]# ./get_iplist.sh

ipset にIPアドレスのリストがセットされているか確認

[root@centos7 ~]# ipset --list WHITELIST
[root@centos7 ~]# ipset --list BLACKLIST

上記のコマンドの結果にIPアドレスのリストが出力されていればOK!(たぶん)


毎日実行されるように cron.daily ディレクトリに移動

[root@centos7 ~]# mv get_iplist.sh /etc/cron.daily/get_iplist.sh

ここまでの作業で、毎日最新のIPリストで ipsetWHITELIST BLACKLIST が作成されるようになりました

ipset リストア設定

まずは現在のセットの保存先のディレクトリを作成します

[root@centos7 ~]# mkdir /etc/ipset/save

現在のセットを保存

[root@centos7 ~]# ipset save > /etc/ipset/save/iplist

ipset リストアスクリプト作成

以下の手順で firewalld 起動時に ipset がリストアされるように設定します

スクリプト作成

[root@centos7 ~]# vim /etc/ipset/restore.sh
restore.sh
#!/bin/bash

IPSET=/sbin/ipset

$IPSET destroy
$IPSET restore < /etc/ipset/save/iplist

作成したスクリプトに実行権限を付加

[root@centos7 ~]# chmod +x /etc/ipset/restore.sh

firewalld の systemファイルを編集

[root@centos7 ~]# vim /usr/lib/systemd/system/firewalld.service

以下の ExecStartPreExecStop の行を追記

[Service]
EnvironmentFile=-/etc/sysconfig/firewalld
# ↓追加
ExecStartPre=/etc/ipset/restore.sh
ExecStart=/usr/sbin/firewalld --nofork --nopid $FIREWALLD_ARGS
ExecReload=/bin/kill -HUP $MAINPID
# ↓追加
ExecStop=/usr/sbin/ipset save > /etc/ipset/save/iplist

反映

[root@centos7 ~]# systemctl daemon-reload

firewalld 設定

ルール追加用にスクリプトを作成します

[root@centos7 ~]# vim firewalld.sh
firewalld.sh
#!/bin/bash

# 設定済みのルールをクリア
# ここ、もっと適切な書き方があると思う・・・
(
IFS=$'\n'; 
RULES=(`firewall-cmd --direct --get-all-rules`)

for rule in ${RULES[@]}; do
    (
    IFS=$' '
    CMD="firewall-cmd --permanent --direct --remove-rule $rule"
    eval $CMD
    )
done
)

# BLACKLISTを破棄
firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 -m set --match-set BLACKLIST src -j DROP

# 自ホストからのアクセスをすべて許可
firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 -i lo -j ACCEPT

# 1秒間に4回を超えるpingはログを記録して破棄
# ※Ping of Death攻撃対策
firewall-cmd --permanent --direct --add-chain ipv4 filter LOG_PINGDEATH
firewall-cmd --permanent --direct --add-rule ipv4 filter LOG_PINGDEATH 1 -m limit --limit 1/s --limit-burst 4 -j ACCEPT
firewall-cmd --permanent --direct --add-rule ipv4 filter LOG_PINGDEATH 1 -j LOG --log-prefix '[PINGDEATH] : ' --log-level 7
firewall-cmd --permanent --direct --add-rule ipv4 filter LOG_PINGDEATH 1 -j DROP
firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 1 -p icmp --icmp-type echo-request -j LOG_PINGDEATH

# Ping Flood攻撃対策(4回以上pingを受信した場合、以降は1秒間に1度だけ許可します。)
firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 -p icmp --icmp-type 8 -m length --length :85 -m limit --limit 1/s --limit-burst 4 -j ACCEPT

# flooding of RST packets, smurf attack Rejection
firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 -p tcp -m tcp --tcp-flags RST RST -m limit --limit 2/second --limit-burst 2 -j ACCEPT

# 全ホスト(ブロードキャストアドレス、マルチキャストアドレス)宛パケットはログを記録せずに破棄
# ※不要ログ記録防止
firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 -d 255.255.255.255 -j DROP
firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 -d 224.0.0.1 -j DROP

# 113番ポート(IDENT)へのアクセスには拒否応答
# ※メールサーバ等のレスポンス低下防止
firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 -p tcp --dport 113 -j REJECT --reject-with tcp-reset

#----------------------------------------------------------
# 各種サービスを公開する場合の設定(ここから)
#----------------------------------------------------------

# 外部からのSSHポートへのアクセスを日本からのみ許可(22番ポート)
firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 1 -p tcp --dport 22 -m set --match-set WHITELIST src -j ACCEPT

# 外部からのTCP80番ポート(HTTP)へのアクセスを許可
firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 1 -p tcp --dport 80 -j ACCEPT

# 外部からのTCP443番ポート(HTTPS)へのアクセスを許可
firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 1 -p tcp --dport 443 -j ACCEPT

#----------------------------------------------------------
# 各種サービスを公開する場合の設定(ここまで)
#----------------------------------------------------------

# デフォルトルール(以降のルールにマッチしなかった場合に適用するルール)設定
firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 2 -m limit --limit 1/s -j LOG --log-prefix '[INPUT] : '
firewall-cmd --permanent --direct --add-rule ipv4 filter FORWARD 1 -m limit --limit 1/s -j LOG --log-prefix '[FORWARD] : '

firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 2 -j DROP     # 受信はすべて破棄
firewall-cmd --permanent --direct --add-rule ipv4 filter OUTPUT 0 -j ACCEPT  # 送信はすべて許可
firewall-cmd --permanent --direct --add-rule ipv4 filter FORWARD 1 -j DROP   # 通過はすべて破棄

# 設定の反映
firewall-cmd --reload

※構築するサーバーの用途によって、開放するポートやマッチさせるセット名などは変わります。
例えば、80番ポート(apache)を国内のみに許可したい場合には WHITELIST をマッチさせる必要があります。

# 80ポートを全公開
firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 1 -p tcp --dport 80 -j ACCEPT
# 80ポートを国内のみに公開
firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 1 -p tcp --dport 80 -m set --match-set WHITELIST src -j ACCEPT

上記のスクリプトは以下のページを参考にさせていただきました。

ファイアウォール構築(iptables)

ファイアウォール設定スクリプトへ実行権限付加

[root@centos7 ~]# chmod +x firewalld.sh

ファイアウォール設定スクリプト実行

[root@centos7 ~]# ./firewalld.sh

上記スクリプトを実行すると /etc/firewalld/direct.xml が作成されます。

direct.xml
<?xml version="1.0" encoding="utf-8"?>
<direct>
  <chain table="filter" ipv="ipv4" chain="LOG_PINGDEATH"/>
  <rule priority="0" table="filter" ipv="ipv4" chain="INPUT">-m set --match-set BLACKLIST src -j DROP</rule>
  <rule priority="0" table="filter" ipv="ipv4" chain="INPUT">-i lo -j ACCEPT</rule>
  <rule priority="0" table="filter" ipv="ipv4" chain="INPUT">-m state --state ESTABLISHED,RELATED -j ACCEPT</rule>
  <rule priority="0" table="filter" ipv="ipv4" chain="INPUT">-p tcp -m tcp --tcp-flags RST RST -m limit --limit 2/second --limit-burst 2 -j ACCEPT</rule>
  <rule priority="0" table="filter" ipv="ipv4" chain="INPUT">-d 255.255.255.255 -j DROP</rule>
  <rule priority="0" table="filter" ipv="ipv4" chain="INPUT">-d 224.0.0.1 -j DROP</rule>
  <rule priority="0" table="filter" ipv="ipv4" chain="INPUT">-p tcp --dport 113 -j REJECT --reject-with tcp-reset</rule>
  <rule priority="1" table="filter" ipv="ipv4" chain="INPUT">-p tcp --dport 22 -m set --match-set WHITELIST src -j ACCEPT</rule>
  <rule priority="1" table="filter" ipv="ipv4" chain="INPUT">-p tcp --dport 80 -j ACCEPT</rule>
  <rule priority="1" table="filter" ipv="ipv4" chain="INPUT">-p tcp --dport 443 -j ACCEPT</rule>
  <rule priority="2" table="filter" ipv="ipv4" chain="INPUT">-m limit --limit 1/s -j LOG --log-prefix '[INPUT] : '</rule>
  <rule priority="2" table="filter" ipv="ipv4" chain="INPUT">-j DROP</rule>
  <rule priority="1" table="filter" ipv="ipv4" chain="LOG_PINGDEATH">-m limit --limit 1/s --limit-burst 4 -j ACCEPT</rule>
  <rule priority="1" table="filter" ipv="ipv4" chain="LOG_PINGDEATH">-j LOG --log-prefix '[PINGDEATH] : ' --log-level 7</rule>
  <rule priority="1" table="filter" ipv="ipv4" chain="LOG_PINGDEATH">-j DROP</rule>
  <rule priority="1" table="filter" ipv="ipv4" chain="FORWARD">-m limit --limit 1/s -j LOG --log-prefix '[FORWARD] : '</rule>
  <rule priority="1" table="filter" ipv="ipv4" chain="FORWARD">-j DROP</rule>
  <rule priority="0" table="filter" ipv="ipv4" chain="OUTPUT">-j ACCEPT</rule>
</direct>

設定確認

先程スクリプトにて設定したルールが適用されているか下記のコマンドで確認します

[root@centos7 ~]# iptables -nL INPUT_direct
Chain INPUT_direct (1 references)
target     prot opt source               destination
DROP       all  --  0.0.0.0/0            0.0.0.0/0            match-set BLACKLIST src
ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0
ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0            state RELATED,ESTABLISHED
ACCEPT     icmp --  0.0.0.0/0            0.0.0.0/0            icmptype 8 length 0:85 limit: avg 1/sec burst 4
ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0            tcp flags:0x04/0x04 limit: avg 2/sec burst 2
DROP       all  --  0.0.0.0/0            255.255.255.255
DROP       all  --  0.0.0.0/0            224.0.0.1
REJECT     tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:113 reject-with tcp-reset
LOG_PINGDEATH  icmp --  0.0.0.0/0            0.0.0.0/0            icmptype 8
ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:22 match-set WHITELIST src
ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:80
ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:443
LOG        all  --  0.0.0.0/0            0.0.0.0/0            limit: avg 1/sec burst 5 LOG flags 0 level 4 prefix "[INPUT] : "
DROP       all  --  0.0.0.0/0            0.0.0.0/0

新たにルールを追加したい場合は

  • /etc/firewalld/direct.xml を直接編集する
  • firewall-cmd --add-rule する
  • 先程作成したスクリプト firewalld.sh にルールを追記して実行する

のいずれかでOK!(※上2つは firewall-cmd --reload を忘れずに)

fail2ban

続いて fail2ban の設定をしていきます!

epelレポジトリ追加

まずは epelレポジトリ をインストールします。

ダウンロード

[root@centos7 ~]# wget http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm

※上記ファイルが存在しない場合は、下記URLからepel-releaseで始まるファイルを探してダウンロードしてください。
http://dl.fedoraproject.org/pub/epel/6/x86_64/

インストール

[root@centos7 ~]# rpm --upgrade --verbose --hash epel-release-6-8.noarch.rpm

デフォルトで無効(enabled=0)にしておきます

[root@centos7 ~]# vim /etc/yum.repos.d/epel.repo
epel.repo
enabled=1 → enabled=0

jwhoisをインストール

BANしたIPアドレスのWHOIS情報も通知で受け取りたい場合には jwhois をインストールしておく

[root@centos7 ~]# yum install --enablerepo=epel jwhois

fail2banをインストール

[root@centos7 ~]# yum install --enablerepo=epel fail2ban

fail2banを起動 & 有効化(自動起動設定)

[root@centos7 ~]# systemctl start fail2ban.service
[root@centos7 ~]# systemctl enable fail2ban.service

fail2ban 設定

fail2ban を設定していきます

jail設定ファイルをコピー

[root@centos7 ~]# cp -p /etc/fail2ban/jail.conf /etc/fail2ban/jail.d/jail.local

設定ファイルを編集

コピーしたjail設定ファイルを編集していきます

[root@centos7 ~]# vim /etc/fail2ban/jail.d/jail.local
jail.local
destemail = root@localhost
↓変更
destemail = root

sender = root@localhost
↓変更
sender = root

# "bantime" is the number of seconds that a host is banned.
bantime  = 600
↓変更
bantime  = 86400

# A host is banned if it has generated "maxretry" during the last "findtime"
# seconds.
findtime  = 600
↓変更
findtime  = 3600

# ↓banaction設定をfirewalldに変更
banaction = iptables-multiport
banaction_allports = iptables-allports
↓
banaction = firewallcmd-ipset
banaction_allports = firewallcmd-allports

[sshd]
# ↓追加
enabled = true
port = ssh
logpath = %(sshd_log)s
backend = systemd
# ※sshdでBANしたIPアドレス情報をメール通知する場合追記(BANしたIPアドレスのみ通知の場合は不要)
# ↓BANしたIPアドレス+そのIPアドレスのWHOIS情報も通知
action = %(action_mw)s
# ↓上記+そのIPアドレスが記録されたログの行も転記して通知
action = %(action_mwl)s

上記により ssh の監視が有効化されます。

action ファイルの編集

[root@centos7 ~]# vim /etc/fail2ban/action.d/firewallcmd-ipset.conf
firewallcmd-ipset.conf
actionstart = ipset create fail2ban-<name> hash:ip timeout <bantime>
              firewall-cmd --direct --add-rule ipv4 filter <chain> 0 -p <protocol> -m multiport --dports <port> -m set --match-set fail2ban-<name> src -j <blocktype>

↓

actionstart = ipset create fail2ban-<name> hash:ip timeout <bantime>
              firewall-cmd --permanent --direct --add-rule ipv4 filter <chain> 0 -p tcp -m multiport --dports <port> -m set --match-set fail2ban-<name> src -j <blocktype>
              firewall-cmd --reload
firewallcmd-ipset.conf
actionstop = firewall-cmd --direct --remove-rule ipv4 filter <chain> 0 -p <protocol> -m multiport --dports <port> -m set --match-set fail2ban-<name> src -j <blocktype>
             ipset flush fail2ban-<name>
             ipset destroy fail2ban-<name>

↓

actionstop = firewall-cmd --permanent --direct --remove-rule ipv4 filter <chain> 0 -p tcp -m multiport --dports <port> -m set --match-set fail2ban-<name> src -j <blocktype>
             firewall-cmd --reload
             ipset flush fail2ban-<name>
             ipset destroy fail2ban-<name>

[Init] 枠内に以下の一行を追加

firewallcmd-ipset.conf
blocktype = DROP

ログレベルの変更 (/var/log/fail2ban.logにINFOが大量に出るのがうるさい場合)

[root@centos7 ~]# vim /etc/fail2ban/fail2ban.conf
fail2ban.conf
loglevel = INFO
↓
loglevel = NOTICE

設定反映

[root@centos7 ~]# systemctl reload fail2ban.service

ブロックしたIPアドレスのリストを表示

[root@XXXXXX ~]# ipset --list fail2ban-sshd

firewalld 停止時の対応

firewalld が停止されると一緒に fail2ban も停止します。(なんで?)
なので、 firewalld 起動時に fail2ban が起動するよう設定します。

[root@centos7 ~]# vim /usr/lib/systemd/system/firewalld.service

ExecStartPostfail2ban 起動コマンドを追記します

firewalld.service
[Service]
EnvironmentFile=-/etc/sysconfig/firewalld
ExecStart=/usr/sbin/firewalld --nofork --nopid $FIREWALLD_ARGS
# ↓追加
ExecStartPost=/usr/bin/systemctl --no-block start fail2ban.service

設定を反映

[root@centos7 ~]# systemctl daemon-reload
確認

firewalld を停止させます

[root@centos7 ~]# systemctl stop firewalld.service

firewalld の停止を確認

[root@centos7 ~]# systemctl status firewalld.service

● firewalld.service - firewalld - dynamic firewall daemon
   Loaded: loaded (/usr/lib/systemd/system/firewalld.service; enabled; vendor preset: enabled)
   Active: inactive (dead) since Fri 2018-03-09 15:50:31 JST; 5s ago

fail2ban の停止を確認

[root@centos7 ~]# systemctl status fail2ban.service

● fail2ban.service - Fail2Ban Service
   Loaded: loaded (/usr/lib/systemd/system/fail2ban.service; enabled; vendor preset: disabled)
   Active: inactive (dead) since Fri 2018-03-09 15:50:30 JST; 1min 17s ago

firewalld を起動

[root@centos7 ~]# systemctl start firewalld.service

firewalld の起動を確認

[root@centos7 ~]# systemctl status firewalld.service

● firewalld.service - firewalld - dynamic firewall daemon
   Loaded: loaded (/usr/lib/systemd/system/firewalld.service; enabled; vendor preset: enabled)
   Active: active (running) since Fri 2018-03-09 15:52:29 JST; 28s ago

fail2ban の起動を確認

[root@centos7 ~]# systemctl status fail2ban.service

● fail2ban.service - Fail2Ban Service
   Loaded: loaded (/usr/lib/systemd/system/fail2ban.service; enabled; vendor preset: disabled)
   Active: active (running) since Fri 2018-03-09 15:52:30 JST; 48s ago

お疲れ様でした

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.