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 ルールを適用する対象タグ。 TargetsSpecified target tags を選んだときに使える。
Source filter アクセス元のフィルタ。
Source IP ranges アクセス元の IP 範囲。 Source filterIP 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 または EGRESSINGRESS が外部から内部への、 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 を使ってソフトウェア開発やウェブ制作をしています。詳しくはこちら