Google Cloud Platform で特定の国からのアクセスをブロックする方法

Google Cloud Platform

Google Cloud Platform で特定の国からのアクセスをブロックする方法についてです。

直接国を指定してブロックする方法は用意されていないので、 まずブロックしたい国の IP アドレスの範囲を特定してから、それらをファイアウォール機能で全件ブロックする というアプローチを採ることになります。

Google Cloud Platform (以下 GCP )のファイアウォール機能は 1 つのルールにつき最大 256 件の IP アドレスの範囲を指定できるので、およそ ブロックすべき IP アドレスの範囲の数 ÷ 256 [個] のルールを作成すれば OK です。 たとえば 1,000 個の IP 範囲のある国をブロックしたい場合は 1,000 ÷ 4 ≒ 3.9 なので、ルールを 4 つ作ればすべてカバーできます。

手作業で行う場合は、 GCP のコンソールから VPC network → Firewall に進み「 Create Firewall Rule 」をクリックしてファイアウォールルールを 1 件ずつ作成していきます。

本記事作成時点でそのルール作成画面に設けられている主なフィールドは次のとおりです(インタフェースを日本語にしている方は日本語に読み替えてください)。

  • Name
  • Description
  • Logs
  • Network
  • Priority
  • Direction of traffic
  • Action on match
  • Targets
  • Target tags
  • Source filter
  • Source IP ranges
  • Second source filter
  • Protocols and ports
  • Enforcement

それぞれの意味合いはおおよそ以下のとおりです。

名前 説明
Name ルール名。アルファベットの小文字・数字・ハイフンが使えます。
Description 説明。機能には影響がありません。
Logs ログを有効にするかどうか。デフォルトは Off 。
Network 対象のネットワーク。
Priority 適用優先度。数字が低いものが優先される。デフォルトは 1000 。
Direction of traffic アクセスの方向。 Ingress は外からのアクセス、 Egress は内から外へのアクセス。デフォルトは Ingress 。
Action on match ルールにマッチしたときに許可するかブロックするか。デフォルトは Allow 。
Targets ルールを適用する対象インスタンス。
Target tags ルールを適用する対象タグ。 Targets で Specified target tags を選んだときに使える。
Source filter アクセス元のフィルタ。
Source IP ranges アクセス元の IP 範囲。 Source filter で IP ranges を選んだときに使える。
Second source filter 2 つめのアクセス元フィルタ。
Protocols and ports ルールを適用するプロトコルとポート。
Enforcement ルールを有効にするかどうか。デフォルトは Enabled 。

細かな設定方法は、公式のドキュメントを読むか、ブログで紹介している人がいるのでそれらを参考にしてください。

件数が少ない場合は手作業でもよいですが、数が多くなる場合は gcloud コマンドでまとめて追加した方がかんたんで確実です。 今回は試しに中国をブロックしてみましょう。

最初にその国の IP アドレスの範囲を特定する必要があります。 これは ipv4.fetus.jp で公開されているものをお借りするのがかんたんです。

# 中国の IP アドレスの範囲の一覧をダウンロードする
curl -O https://ipv4.fetus.jp/cn.txt

ファイル cn.txt の中身は次のようになっています。

#
# [cn] 中華人民共和国 (China)
#  https://ipv4.fetus.jp/cn.txt
#  出力日時: ...
#

1.0.1.0/24
1.0.2.0/23
1.0.8.0/21
1.0.32.0/19
1.1.0.0/24
...

このデータとファイアウォールルールを作成する gcloud のサブコマンド gloud compute firewall-rules create を使ってルールを作成していきます。

gcloud compute firewall-rules create コマンドは次のような形で使用します:

gcloud compute firewall-rules create [NAME] \
  --action=DENY \
  --rules=ALL \
  --direction=INGRESS \
  --priority=10 \
  --no-enable-logging \
  --source-ranges=[RANGES]

これを実行し無事に処理が成功すると、 [RANGES] で指定した IP アドレスからのアクセスをブロックするファイアウォールルールが作成されます。 名前は [NAME] で指定したものになります。

その他のオプションの意味合いは次のとおりです。

オプション 意味合い
--action=DENY ルールにマッチしたときに行う処理。 ALLOW または DENY 。
--rules=ALL ルールを適用するプロトコルとポート。 ALL とするとすべてが対象になる。
--direction=INGRESS アクセスの方向。 INGRESS または EGRESS 。 INGRESS が外部から内部への、 EGRESS が内部から外部へのアクセスを表す。別名として IN OUT も用意されている。
--priority=10 優先度。 0 から 65535 までの整数。数字が低いほど優先度が高い。デフォルトは 1000 。
--no-enable-logging ロギングを無効にする。
--source-ranges=[RANGES] 対象の IP アドレスのリスト。 , 区切りで複数指定可。

[NAME] と [RANGES] には実際の名前と IP アドレスの範囲を入れます。

ひとつのルールで指定できる IP アドレスの範囲は最大 256 件なので、たとえば範囲の数が 6,000 個ある場合は 20 個強のルールを作成する必要があります。 これを手作業でやるのは大変なので、次のようなスクリプトを書きます。

gcp_block_country.py:

"""
Google Cloud Platform で特定の国からのアクセスをブロックする
ルールを作成するためのスクリプト
"""

import argparse
import subprocess

CHUNK_SIZE = 256
# サポート対象の国の一覧
# キー: 国コード / バリュー: ルールのプリフィックス
COUNTRIES = {
    'cn': 'block-china-',
    'ru': 'block-russia-',
}


def main():
    """メイン関数"""
    args = get_args()
    dry_run = args.dry_run
    country_code = args.country_code

    addresses = get_addresses(country_code)

    name_prefix = COUNTRIES[country_code]
    create_rules(name_prefix, addresses, dry_run=dry_run)


def get_args():
    """コマンドライン引数を取得する"""
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument('--dry-run', action='store_true', help='ドライラン')
    parser.add_argument('--country-code', required=True, help='国コード')

    return parser.parse_args()


def create_rules(name_prefix, addresses, *, dry_run):
    """ファイヤウォールルールを複数件まとめて作成する"""
    n = 0
    while True:
        start = n * CHUNK_SIZE
        stop = start + CHUNK_SIZE

        chunk_addresses = addresses[start:stop]
        if not chunk_addresses:
            break

        name = '{}{}'.format(name_prefix, n)
        create_rule(name, chunk_addresses, dry_run=dry_run)
        n += 1


def create_rule(name, addresses, *, dry_run):
    """ファイヤウォールルールを 1 件作成する"""
    args = [
        'gcloud',
        'compute',
        'firewall-rules',
        'create',
        name,
        '--action=DENY',
        '--rules=ALL',
        '--direction=INGRESS',
        '--priority=10',
        '--no-enable-logging',
        '--source-ranges={}'.format(','.join(addresses)),
    ]
    if dry_run:
        print('Run:', ' '.join(args))
        return

    return subprocess.run(args, check=True)


def get_addresses(country_code):
    """アドレス一覧を取得する"""

    def is_valid(line):
        return line.strip() and not line.startswith('#')

    with open('./{}.txt'.format(country_code)) as f:
        addresses = [l.strip() for l in f.readlines() if is_valid(l)]

    return addresses


if __name__ == '__main__':
    main()

実行します。 最初は --dry-run オプションを付けてどのようなコマンドが走るのかを確認します。

python gcp_block_country.py --country-code cn --dry-run

問題がなさそうであれば --dry-run を外して実行します。

python gcp_block_country.py --country-code cn

コマンドを実行して少し待つと block-china-0 block-china-1 ……というルールが作成されることが確認できます。 確認はブラウザで GCP の console の VPC network → Firewall を開くか gcloud コマンドを使うとよいでしょう。 次のコマンドを使用すればどんなファイアウォールルールが登録されているかを確認できます。

gcloud compute firewall-rules list

以上です。

今回は中国を対象にしましたが、他の国でも同様のやり方で制限することができます。 コードを GitHub に置いたので興味のある方は参考にしてみてください。

参考


アバター
後藤隼人 ( ごとうはやと )

Python や PHP を使ってソフトウェア開発やウェブ制作をしています。詳しくはこちら。