2019年2月28日木曜日

Cloud Storage へのアクセス元 IP を調べる方法

概要

Google Cloud Storage はデフォルトではアクセスログは記録していません
バケットのロギング機能を有効にすると CSV 形式でアクセスログを特定のバケットに残すことができます
今回はロギングを有効にし生成されたログファイルをパースしてバケットへのアクセス IP を集計してみました

環境

  • macOS 10.14.3
  • Ruby 2.5.1p57
    • google-cloud-storage 1.17.0
  • Google Cloud Storage (2019/02/27 時点)

バケットのロギングを ON にする

コンソールからだとできないようなので gsutil コマンドを使います
ログを格納するバケットを作成し、ロギングするバケットのフラグを ON にするだけです

まずログを格納するバケットを作成しましょう
バケットへのログの書き込みは GCP の内部で管理されている cloud-storage-analytics というメンバになります
バケットに cloud-storage-analytics が書き込み可能な権限を付与しましょう

  • gsutil mb -c regional -l us-central1 gs://log-bucket/
  • gsutil acl ch -g cloud-storage-analytics@google.com:W gs://log-bucket/

あとはロギングしたいバケットのフラグをオンにします
その際に先程作成したログを格納するためのバケットを指定します

  • gsutil logging set on -b gs://log-bucket gs://bucket

設定できているかは get で行います

  • gsutil logging get gs://log-bucket

これでログがバケット内に格納されはじめます

サービスアカウント作成

今回は Ruby スクリプトを使うのでそれ用のサービスアカウントを作成しましょう
すでにサービスアカウントがある場合はそれを流量しても OK です
また今回の場合バケットに対象のサービスアカウントがアクセスできる必要があるので ACL を設定しましょう

  • gsutil acl get gs://log-bucket/
  • gsutil acl ch -u ruby-script-sa@project-123456.iam.gserviceaccount.com:R gs://log-bucket
  • gsutil acl ch -u ruby-script-sa@project-123456.iam.gserviceaccount.com:R gs://log-bucket/*

もしくはバケットポリシーオンリーを使うとバケットの権限を変更するだけでそのバケット配下のオブジェクトも同じ権限にすることができます

Cloud Storage で該当のバケットを選択し権限の設定メニューを開きます
そして「バケットポリシーのみでアクセス制御を簡素化する」を有効にします

追加したサービスアカウントを「ストレージのレガシー バケット読み取り」で追加すれば配下のオブジェクトも読み取ることができるようになります

また IAM 側でも「環境ユーザと Storage Object の閲覧者」の役割を追加してください

なおバケットポリシーオンリーを有効にすると gsutil acl get などは使えなくなるのでご注意ください

Ruby のスクリプトでログを取得する

ログは CSV 形式なので手動でダウンロードして適当に集計しても OK です
ただ、ログファイルが複数に分割されているのでスクリプトで一括ダウンロードして集計します

require 'google/cloud/storage'
require 'csv'

storage = Google::Cloud::Storage.new(
  project_id: "project-123456",
  credentials: "./project-123456.json"
)

b = storage.bucket 'log-bucket'
byte = 0
ret = b.files.each_with_object(Hash.new(0)) { |f, acc|
  f.download "./#{f.name}"
  csv = CSV.read("./#{f.name}", headers: true)
  if f.name.include?('_storage_')
    byte = csv['storage_byte_hours']
  else
    csv.each { |r|
      acc[r['c_ip']] += 1
    }
  end
}
puts byte
pp ret.sort { |(k1, v1), (k2, v2)| v2 <=> v1 }

ちょっと解説

ログファイルは一度ダウンロードしなければ使えません
ファイルは CSV 形式になっていてバケットの使用量をロギングしているファイルが 1 つと複数の分割されたアクセスログがあります

今回ほしい IP アドレスの情報は「c_ip」というカラムで管理されているので CSV から読み込んで取得します
今回は一応使用量をロギングしているファイルからも storage_byte_hours を読み込んでいますが不要であれば削除してください

バケットおよびオブジェクトへの権限がうまく付与されていない場合は Invalid request (Google::Cloud::PermissionDeniedError) になるのでその場合は ACL かバケットポリシーオンリーのどちらかを使ってログファイルにアクセスできるようにしましょう

最後に

バケットへのアクセスログを解析して IP の数をカウントしてみました
ポイントはログファイルが CSV なのと複数のファイルに分割されて保存させる点かなと思います

あとは Cloud Storage を使う上で避けては通れない ACL 周りかなと思います
最近だとバケットポリシーオンリーを使って IAM の機能で管理するのが主流になっているぽいです
ACL はオブジェクト単位で管理する必要があるので確かに面倒なイメージはあります
バケット配下のオブジェクトでそれぞれで権限を設定するケースって確かに少ない気がするのでバケットポリシーオンリーが主流になっているだと思います

参考サイト

0 件のコメント:

コメントを投稿