Carpe Diem

備忘録

特定のGCSバケットにのみアクセスできるサービスアカウントの作り方

概要

christina04.hatenablog.com

の実践編です。

特定のGCSバケットにのみアクセスできるサービスアカウントを作ってみます。

前提

  • 新規で作るサービスアカウントに対してのアクセス制御を行う
    • 既存のアカウントに対するアクセス制御は行わない
  • 特定のGCSバケットにのみアクセスさせる

作り方

準備

バケットの用意

テスト用のバケットを用意します。

$ gsutil mb -c regional -l us-east1 -b on gs://jun06t-iam-test
Creating gs://jun06t-iam-test/...

作成されました。

f:id:quoll00:20210505023540p:plain

アクセスするオブジェクトも用意しておきます。

$ echo "Hello World" > sample.txt
$ gsutil cp sample.txt gs://jun06t-iam-test

サービスアカウントの用意

次にサービスアカウントを用意します。

$ gcloud iam service-accounts create gcs-sa \
    --description="GCSバケットアクセス用" \
    --display-name="gcs-sa"

作成されました。

f:id:quoll00:20210505024043p:plain

アクセスするためのサービスアカウントキーも作成しておきます。

$ gcloud iam service-accounts keys create ./sa-private-key.json \
    --iam-account=gcs-sa@<project_name>.iam.gserviceaccount.com

実行プログラムで使えるように環境変数に読み込ませておきます。

$ export GOOGLE_APPLICATION_CREDENTIALS=./sa-private-key.json

キーを使ってバケットにアクセスするコード

golangでサクッと書いてみます。

func main() {
    ctx := context.Background()

    data, err := getObject(ctx)
    if err != nil {
        panic(err)
    }
    fmt.Printf("%s\n", data)
}

func getObject(ctx context.Context) ([]byte, error) {
    client, err := storage.NewClient(ctx)
    if err != nil {
        return nil, err
    }

    bucketName := "jun06t-iam-test"
    object := "sample.txt"

    rc, err := client.Bucket(bucketName).Object(object).NewReader(ctx)
    if err != nil {
        return nil, err
    }
    defer rc.Close()
    data, err := ioutil.ReadAll(rc)
    if err != nil {
        return nil, err
    }
    return data, nil
}

動作検証

アクセス権限が無いので当然コケます。

$ go run main.go
panic: googleapi: got HTTP response code 403 with body

アクセス制御は2通りある

IAMを理解すると今回は2通りの方法があることが分かります。

  1. リソースレベル(そのバケットのみ)のIAMポリシーを作る
  2. プロジェクトレベルのIAMポリシーを作る。ただしバインディングの条件で特定バケットに制限する

1. リソースレベル(そのバケットのみ)のIAMポリシーを作る

roles/storage.objectViewerのロールで作成します。

$ gsutil iam ch serviceAccount:gcs-sa@<project_name>.iam.gserviceaccount.com:roles/storage.objectViewer \
  gs://jun06t-iam-test

確認してみます。

{
  "bindings": [
    ... // 基本ロール
    {
      "members": [
        "serviceAccount:gcs-sa@<project_name>.iam.gserviceaccount.com"
      ],
      "role": "roles/storage.objectViewer"
    }
  ],
  "etag": "CAI="
}

アセットの方でもリソースレベルのIAMポリシーが確認できます。

$ gcloud asset search-all-iam-policies \
  --scope=projects/<project_name> \
  --query="policy:gcs-sa"

---
policy:
  bindings:
  - members:
    - serviceAccount:gcs-sa@<project_name>.iam.gserviceaccount.com
    role: roles/storage.objectViewer
project: projects/xxxx
resource: //storage.googleapis.com/jun06t-iam-test

動作検証

権限が付与されたので読み込めるようになりました。

$ go run main.go
Hello World

2. プロジェクトレベルのIAMポリシーを作る

次は別の方法としてプロジェクトレベルのIAMポリシーを条件付きで作成します。

$ gcloud projects add-iam-policy-binding <project_name> \
  --member='serviceAccount:gcs-sa@<project_name>.iam.gserviceaccount.com' \
  --role='roles/storage.objectViewer' \
  --condition='title=becket limit,expression=resource.name.startsWith("projects/_/buckets/jun06t-iam-test")'

バケット名を指定するために
resource.name.startsWith("projects/_/buckets/バケット名")
という条件を入れています。

実行するとIAMポリシー一覧に新しいポリシーが作成され、

f:id:quoll00:20210505032135p:plain

条件も付いています。

f:id:quoll00:20210505032057p:plain

アセットの方でもプロジェクトレベルのIAMポリシーが確認できます。

$ gcloud asset search-all-iam-policies \
  --scope=projects/<project_name> \
  --query="policy:gcs-sa"

---
policy:
  bindings:
  - condition:
      expression: resource.name.startsWith("projects/_/buckets/jun06t-iam-test")
      title: becket limit
    members:
    - serviceAccount:gcs-sa@<project_name>.iam.gserviceaccount.com
    role: roles/storage.objectViewer
project: projects/xxxx
resource: //cloudresourcemanager.googleapis.com/projects/<project_name>

動作検証

こちらも権限が付与されたので読み込めるようになりました。

$ go run main.go
Hello World

まとめ

2通りの方法でサービスアカウントに対して、特定のGCSバケットにのみアクセスできる権限を付与してみました。

参考