Cloud Asset Inventory で一般公開されたリソースを自動検出してみた。

2022.06.01

こんにちは、みかみです。

人は間違える生き物です。 BigQuery や GCS バケットなど、大切なデータが置いてあるサービスに誰でもアクセスできるような設定がされていたら一大事! リスクはできる限り抑えたいものです。 _(:3」∠)_

やりたいこと

  • 一般公開されている Google Cloud プロジェクト内のリソースを自動検出したい
  • 一般公開リソースがあったらメールで通知したい

Cloud Asset Inventory とは

Google Cloud のプロジェクトやフォルダ、組織に紐づくリソースやポリシー、ランタイム情報などのメタデータを検索、分析できるサービスです。 アセットデータをエクスポートした場合などにはデータ保存先の GCS や BigQuery のストレージ料金がかかりますが、基本的には無料で利用できます。

前提

Google Cloud SDK(gcloud コマンド)の実行環境は準備済みであるものとします。 本エントリでは、Cloud Shell を使用ました。

事前準備

ちゃんと検出できるか確認するために、一般公開された BigQuery データセットを準備しておきます。

データセットの権限設定から、allUsers に「データ閲覧者」ロールを追加しました。

Pub/Sub トピックを作成

Cloud Shell から以下のコマンドを実行して、Pub/Sub トピックを作成します。 Cloud Scheduler からこの Pub/Sub トピックを経由して、Cloud Functions 関数を実行する予定です。

gcloud pubsub topics create check_public_access

管理コンソールから、トピックが作成されたことを確認しました。

Cloud Functions 関数を作成

Cloud Asset Inventory から一般公開されたリソースを取得する、Clooud Functions 関数を作成します。

以下の Python コードを main.py というファイル名で保存しました。

from google.cloud import asset_v1
import os

def check_public_access_resources(event, context):
    GCP_PROJECT = os.getenv('GCP_PROJECT', None)
    scope = f'projects/{GCP_PROJECT}'
    query = 'policy:(allAuthenticatedUsers OR allUsers)'

    client = asset_v1.AssetServiceClient()
    response = client.search_all_iam_policies(
        request={"scope": scope, "query": query}
    )

    for policy in response:
        print(f'Open to the public!! resource: {policy.resource}')
        bindings = policy.policy.bindings
        for item in bindings:
            print(f'{item.role} is attached.')
        break

合わせて、以下の requirements.txt も、同じディレクトリに保存しました。

google-cloud-asset>=3.9.0

以下の gcloud コマンドで、main.pyrequirements.txt を Cloud Functions にデプロイします。

gcloud functions deploy check_public_access_resources \
--region asia-northeast1 \
--runtime python37 \
--trigger-resource check_public_access \
--trigger-event google.pubsub.topic.publish

管理コンソールからも、Cloud Functions 関数が無事デプロイできたことが確認できました。

Cloud Scheduler から Cloud Functions を実行

先ほどデプロイした Cloud Functions をスケジュール実行するために、Cloud Scheduler ジョブを作成します。

Cloud Shell から以下のコマンドを実行します。

gcloud scheduler jobs create pubsub check_public_access \
  --schedule="*/5 * * * *" \
  --topic=check_public_access \
  --message-body="{}" \
  --time-zone="Asia/Tokyo"

5分ごとに Cloud Functions を実行するジョブが作成できました。

そのまま5分待ちます..._(:3」∠)_

5分後、ジョブが正常に実行されたようなので、Cloud Functions のログを確認してみると...

bigquery.googleapis.com/(省略)/datasets/test_publicroles/bigquery.dataViewer ロールで一般公開されてしまっている旨、期待通りログ出力されました。

アラート通知を追加

せっかく Cloud Functions が5分おきにチェックしてくれていても、ログ出力するだけでは人が認識&対応するまでに時間がかかってしまう可能性があります。

一般公開されたリソースが見つかったら、通知してくれる仕組みも作っておきます。

通知用の別の Cloud Functions を作成して検出結果を Pub/Sub 経由で渡して通知、など、通知方法の実装はいろいろ考えられますが、今回はお手軽に Cloud Monitoring のアラートポリシーを作成して、指定したメールアドレス宛にアラートメールを送信してみます。

ログに特定の文字列が出力されていたらアラートを送信する、ログベースのアラートを作成します。 なお、ログベースのアラートは、2022/05/31 現在まだプレビューなので、ご使用の際にはご留意ください。

まずは Cloud Shell から以下のコマンドを実行して、アラート通知先のチャンネルを作成します。

gcloud alpha monitoring channels create \
    --display-name="channel secd mail to me" \
    --description="send mail to me" \
    --type=email \
    --channel-labels=email_address=xxxx@classmethod.jp

※メールアドレスは一部伏字に変更しています。

以下のポリシー定義を、policy_check_public_access.json というファイル名で保存します。

{
  "displayName": "Check public access resources.",
  "documentation": {
    "content": "一般公開されているリソースが見つかりました!至急確認してください!!",
    "mimeType": "text/markdown"
  },
  "conditions": [
    {
      "displayName": "Check public access resources.",
      "conditionMatchedLog": {
        "filter": "resource.type=\"cloud_function\" resource.labels.function_name=\"check_public_access_resources\" resource.labels.region=\"asia-northeast1\" severity\u003e=DEFAULT \"Open to the public!!\""
      }
    }
  ],
  "alertStrategy": {
    "notificationRateLimit": {
      "period": "300s"
    }
  },
  "combiner": "OR",
  "enabled": true,
  "notificationChannels": [
    "projects/cm-da-mikami-yuki-258308/notificationChannels/1663xxxxxxxxxxxx8583"
  ]
}

※一部伏字に変更しています。

以下のコマンドで、保存した定義ファイルを指定して、アラートポリシーを作成しました。

gcloud alpha monitoring policies create \
    --policy-from-file="policy_check_public_access.json"

Cloud Functions が実行されるのを待って、メールを確認してみると...

設定した通り、アラートメールが受信できました。

データセットが一般公開されてるなんて一大事!すぐに対応せねばっ!!(( ;゚Д゚))

まとめ(所感)

Security Command Center などのマネージドサービスや、サードパーティーツールの導入など、脅威検知の仕組みはいろいろ考えられますが、監視対象が少ない場合には、Cloud Asset Inventory を利用したスクラッチも選択肢になるのではないかと思います。

今回は検出のみでしたが、間違って設定したポリシーの自動修正など、要件に合わせて自由に実装できるかと。

リスクとコストのトレードオフは、いつでも辛いところです。。_(:3」∠)_

参考