GeoIPを利用したsshdの国別アクセス制御

シェアする

クラウド化が進む中、サーバをインターネットに公開しつつ、sshdにてリモートアクセスにて制御する対応が増えてきているかと思います。そんな中、減らないのが不正アクセスによる情報漏洩。情報漏洩を起こすと会社としての信頼問題につながり、その対策に数億・数十億の金が必要となる場合があります。
そういった不正アクセスをしてくるのは、国別批判はよろしくないのですが中国やインドやら、サマライズすると大多数はこういった特定の国からと考えています。実際、私が検証しているサーバでも国別IPで評価するとCN・IN(GeoIP評価結果)からのSSHの不正アクセスが繰り返されています。
なんで、GeoIPの評価結果にてJP以外からのSSHを切断できればと思い、ネットを検索すると、Ubuntuとかなら、tcp_wrappers と、その機能であるaclexec を利用すれば簡単に実現しそうでしたが、私はCentOSを利用しているので、CentOSで何とかできないかと考えました。

必要パッケージ

CentOS7 にて必要となるパッケージは以下の通りです。(バージョンは、私が利用しているサーバのバージョンを記載。特に制限はありません。)

  • GeoIP-1.5.0-11.el7.x86_64
  • tcp_wrappers-7.6-77.el7.x86_64

ま、結局のところ、tcp_wrappers を利用しましたw

実装

以下にスクリプトを設置しました。

# vi /usr/local/scripts/country_filter.sh
#!/bin/bash

CMD_LOGGER_ERR="/bin/logger -p auth.err -t country_filter"
CMD_LOGGER_INFO="/bin/logger -p auth.info -t country_filter"
ALLOW_COUNTRIES="JP"

if [ $# -ne 2 ]; then
  echo "Usage: $0 <IP> <PID>"
  exit 0
fi

COUNTRY=`/usr/bin/geoiplookup $1 | awk -F ": " '{ print $2 }' | awk -F "," '{ print $1 }' | head -n 1`
PID=$2

if [ "${COUNTRY}" = "IP Address not found" ]
then
  ${CMD_LOGGER_INFO} "ALLOW sshd(${PID}) connection from $1 ($COUNTRY)"
  exit 0
fi

if [[ ${COUNTRY} =~ ${ALLOW_COUNTRIES} ]]
then
  ${CMD_LOGGER_INFO} "ALLOW sshd(${PID}) connection from $1 ($COUNTRY)"
else
  ${CMD_LOGGER_ERR} "DENY sshd(${PID}) connection from $1 ($COUNTRY). sshd session kill."
  kill ${PID}
fi

実行権限とかは、忘れずに。ちなみに、GeoIPにて国が判定できないIPは許可としています。
つづけて、tcp_wrappers の hosts.allow と hosts.deny を設置します。

# vi /etc/hosts.allow
sshd: ALL: spawn /usr/local/scripts/country_filter.sh %a %p

%a: 接続元IP
%p: 接続時に起動したデーモン(sshd)のプロセスID
が引数で、渡されます。

# vi /etc/hosts.deny
sshd: ALL

実装は、これで終わりです。
実際に接続されてくると、以下ログが /var/log/messages に出力されます。

Feb 28 01:06:23 sabao-srv auth.err: country_filter: DENY sshd(8662) connection from 103.235.247.242 (CN). sshd session kill.
Feb 28 01:06:49 sabao-srv auth.err: country_filter: DENY sshd(8681) connection from 112.65.170.186 (CN). sshd session kill.
Feb 28 01:07:25 sabao-srv auth.err: country_filter: DENY sshd(8712) connection from 115.254.104.201 (IN). sshd session kill.

仕組み

tcp_wrappersにて、aclexec の場合は、呼び出された実行結果にて Returnコード1の場合、そのセッションをRefuseする仕組みなのでしょうが、spawnを利用すると、そのスクリプトは子プロセスとして呼び出されるため、それがReturnコード1で終わろうが関係なく、そのセッションは成立します。そのため、tcp_wrappers にてaclexecを実現するためには。。と考え、以下の図的な感じで仕上げました。

(うーん、フロー図なのか、よくわからん図だw)

なので、tcp_wrappers の refuseログは出ません。その代わりにDENYログを出しています。また、ログイン認証が通った後にプロセスKILLが走りますので、パスワードは漏洩してますがなwという代物です。

最後にGeoIPの更新

GeoIPは、都度更新されていますのでcronで以下スクリプトを動作させ、GeoIPのDBを更新しています。参考までに記載しますが、IPv6には対応させていません。

# vi /usr/local/scripts/geoip_get.sh
#! /bin/bash

cd /usr/share/GeoIP/

test -f GeoIP.dat.gz && rm -rf GeoIP.dat.gz

wget http://geolite.maxmind.com/download/geoip/database/GeoLiteCountry/GeoIP.dat.gz

test -f GeoIP.dat.gz && gzip -f -d GeoIP.dat.gz

最近も、ポルシェジャパンが28,000件の個人情報漏洩とか。不正アクセスされる前提で、Webサーバを乗っ取られてもデータベースが参照されないとかWebサーバとデータベースサーバの間のインターフェースの仕組みを再検討したほうがいいのかもしれないですね。

GeoIPの簡単な活用方法でした。

ではでは

トップへ戻る