はじめに
サーバー構築・運用において、固定IPアドレスを持っていないユーザーからのSSH接続を許可する必要があるために、SSHのアクセス元IPアドレスを限定できないケースはわりとよくあると思います。
公開鍵認証を必須とする、ポート番号を変更する、ブルートフォース攻撃対策としてFail2BanやDenyHostsでBanする、などの方法でセキュリティ強化はできますが、そもそも不正アクセスのチャレンジ自体をできるだけ少なくしたいですね。
現実の世界で例えるならば、自宅の鍵穴を知らない人にガチャガチャされるのは、安全だとわかっていても気持ち悪いものです。
以前、国別のIPアドレスリストを使って、特定の国からのWebアクセスをブロックする対処を行ったことがありました。
・Nginxで海外からのアクセスを拒否する
https://inaba-serverdesign.jp/blog/20140616/nginx_deny_ipaddress.html
そこで、国別のIPアドレスリストを利用して、iptablesでSSHを日本国内からの接続に限定すればよいのでは、と考えて試してみました。
ところが、単純にIPアドレスの分だけフィルタリングルールを追加すると、、、
iptables -A OUTPUT -m state --state NEW -s <IPアドレス> -p tcp --dport 22 -j ACCEPT
それだけで2,000件以上のフィルタリングルール追加処理が発生します。
iptablesのフィルタリングルールを追加する処理は少し重いようで、すべてのiptablesエントリーを登録するまでに数十秒かかるようになってしまいました。
iptablesルールを適用する間、ルールの順序にもよりますが、一時的にサーバーへアクセスできなくなってしまいます。
また、iptablesのフィルタリングルールには上限があるようで、「いくつかの国からのSSHアクセスを拒否する」というような設定を試してみたところ、ルール数が多すぎて、iptalbesのフィルタリングルール追加が途中で止まってしまいました。
ということで困っていたのですが、こういう場合は
「ipsetを利用してIPアドレスの集合として管理し、iptablesで参照する」
という方法があることがわかりました。
前置きが長くなりましたが、以下、
「CentOS 6でipsetとiptablesでSSHを日本国内からの接続に限定する」
方法をまとめます。
ipsetの詳しい説明は、以下の記事がとてもわかりやすいのでそちらを参照してください。
・ipsetを使ってスマートにiptablesを設定する
http://equj65.net/tech/ipset/
※CentOS 7での設定方法は少し違うので、別の記事にまとめました。
・ipsetとiptablesでSSHを日本国内からの接続に限定する (CentOS 7)
https://inaba-serverdesign.jp/blog/20150209/ipset_iptables_country_centos7.html
ipsetの設定
・ipsetのインストール
# yum install ipset インストール: ipset.x86_64 0:6.11-3.el6 依存性関連をインストールしました:
・許可リストセットの作成
接続を許可したいネットワークのアドレスを保持するセット「ALLOWLIST」を作成し、国別コードリストからALLOWLISTのエントリーを登録します。
定期的に最新の国別コードリストの情報を反映できるよう、スクリプトを用意しました。
# vi mk_ipallowlist.sh
#!/bin/bash
# 国別コードリストをダウンロード
cd /root/bin/
if [ -s cidr.txt ]; then
mv cidr.txt cidr.txt.old
fi
wget http://nami.jp/ipv4bycc/cidr.txt.gz
gunzip cidr.txt.gz
# 許可リストセットを作成
ipset create -exist ALLOWLIST hash:net
# 許可リストセットに日本のIPアドレスを登録
sed -n 's/^JP\t//p' cidr.txt | while read ADDRESS; do
ipset add ALLOWLIST $ADDRESS
done
スクリプトを実行します。
# chmod 700 ./mk_ipallowlist.sh # ./mk_ipallowlist.sh
ALLOWLISTを確認します。
# ipset list ALLOWLIST | less Name: ALLOWLIST Type: hash:net Header: family inet hashsize 1024 maxelem 65536 Size in memory: 48176 References: 0 Members: 112.138.0.0/15 138.212.0.0/16 192.42.104.0/22 202.177.48.0/20 ...
・ipsetセットの保存
ipsetはサービスを再起動すると削除されるため、/etc/sysconfig/ipset に保存し、次回ipsetが起動するときに自動的にALLOWLISTセットをロードするようにします。
# /etc/init.d/ipset save
確認します。
# less /etc/sysconfig/ipset create ALLOWLIST hash:net family inet hashsize 1024 maxelem 65536 add ALLOWLIST 112.138.0.0/15 add ALLOWLIST 138.212.0.0/16 add ALLOWLIST 192.42.104.0/22 add ALLOWLIST 202.177.48.0/20 ...
・自動起動設定
OS起動時にipsetセットをロードするよう自動起動設定を行います。
念のため、ipset起動スクリプトを確認します。
→ chkconfig: 2345 07 93 となっているので、iptablesより前に起動し、iptablesの後で終了することがわかります。
# view /etc/init.d/ipset ... # chkconfig: 2345 07 93 # description: Starts, stops and saves IP sets ... # view /etc/init.d/iptables ... # chkconfig: 2345 08 92 # description: Starts, stops and saves iptables firewall ...
自動起動設定を行います。
# chkconfig ipset on # chkconfig --list ipset ipset 0:off 1:off 2:on 3:on 4:on 5:on 6:off
iptablesの設定
iptablesコマンドでオプション -m set –match-set <セット名=ALLOWLIST> src を指定して、許可リストのIPアドレスからのみSSH接続を許可します。
(デフォルトポリシーがDROP、SSHポートはTCP/22を想定。)
# iptables -I INPUT -m state --state NEW -p tcp --dport 22 -m set --match-set ALLOWLIST src -j ACCEPT
これで、iptables 1行のエントリーで、大量のIPアドレスからのアクセス許可ルールを表現できました。
iptablesコマンドでは source 0.0.0.0/0 と出力されますが、許可リストにマッチしないIPアドレスからはSSHポートにアクセスできません。
# iptables -L -n Chain INPUT (policy DROP) target prot opt source destination ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 state NEW tcp dpt:22 match-set ALLOWLIST src
ルールを追加したので、iptablesルールを保存するのをお忘れなく。
# /etc/init.d/iptables save # less /etc/sysconfig/iptables *filter :INPUT DROP [0:0] :FORWARD DROP [0:0] :OUTPUT DROP [16000:1808000] -A INPUT -p tcp -m state --state NEW -m tcp --dport 10022 -m set --match-set ALLOWLIST src -j ACCEPT ...
動作確認
以下を確認します。
- 自分のPCやサーバーからSSHログインできる。
- 海外のサーバー環境からSSHログインできない。
また、可能であれば、OSを再起動してipsetルールやiptablesルールが正しくロードされることも確認するとよいでしょう。
「海外のサーバー環境」は、例えばAWS EC2のサーバーを使うとよいと思います。
なお、EC2サーバーのIPアドレスは、東京リージョンのサーバーでも国名コードとしては’US’です。
つまり、この国名コードは、必ずしも該当のIPアドレスを使用するサーバーの地理的な位置を表すとは限らないことに注意しましょう。
まとめ
国別のIPアドレスリストを利用して、ipsetとiptablesでSSHを日本国内からの接続に限定する設定方法をまとめました。
この方法で、FTP等ほかのポートのアクセス制限を行うこともできますし、ipsetでブロックリストのセットを作成しDROPルールを登録することで、特定の国からのアクセスを防ぐこともできます。
# iptables -I INPUT -m state --state NEW -p tcp --dport 22 -m set --match-set BLOCKLIST src -j DROP
iptablesで取り扱うIPアドレスが多い場合は、ipsetを使いましょう。
