オンプレミス環境からGCSバケットにファイルのアップロード、最小権限の設定について

## 背景

エンドユーザーはオンプレミス環境から GCS バケットへファイルアップロードしたいという要望がありますが、情報はこれだけで、直接に聞くのも難しかったです。
GCP プロジェクトは組織なしで作られていますが、Google Workspace を利用しているかどうか、Google アカウントを持っているかどうか、オンプレミス環境はどんな状況かなどなど、そういう要素に左右されないように、考えてみました。

## 案

GCP プロジェクトでサービスアカウントを発行し、ロールの「Storage オブジェクト作成者」を付与し、サービスアカウントキーを作成してから、エンドユーザーのオンプレミス環境の Docker コンテナからファイルアップロードすることにしました。

## バケットの作成

ストレージクラスの STANDARD、東京リージョン、均一のバケットを作成します。

```bash
export PROJECT_ID=
export BUCKET_ID=
export DEV_BUCKET_ID=$BUCKET_ID-dev

# create bucket
gsutil mb -p $PROJECT_ID -c STANDARD -l ASIA-NORTHEAST1 -b on gs://$DEV_BUCKET_ID

# check bucket's iam
gsutil iam get gs://$DEV_BUCKET_ID
```

### 継承した権限の確認

センシティブな情報を取り扱うバケットなら、プロジェクトレベルで設定した権限を継承していることはあるから、（`gsutil iam get`のコマンドはプロジェクトレベルから継承した権限確認できないため、）Console 上でバケットの権限を確認します。デフォルトの場合、基本ロール（Owner, Editor, Viewer）もバケットにアクセスできますが、必要なければ、削除しましょう。しかし、オーナーまで削除、且つStorage Adminに相当するロールを持つメンバーがいなければ、操作不能なバケットになります。

![permissions_bucket.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/562445/41d08eb5-275c-923c-6ecd-16fe972f1c0f.png)

## サービスアカウント、キーの発行

オンプレミス環境で使うサービスアカウント、キーを作成し、そして、バケットレベルで Storage Object Creator のロールをサービスアカウントに付与します。

```bash
export PROJECT_ID=
export SER_ACC_ID=
export DEV_SER_ACC_ID=$SER_ACC_ID-dev

# create service account
gcloud iam service-accounts create $DEV_SER_ACC_ID --project $PROJECT_ID \
 --description "" --display-name $DEV_SER_ACC_ID

# create key file for service account
gcloud iam service-accounts keys create {KEY_FILE_PATH} \
 --project $PROJECT_ID --iam-account $DEV_SER_ACC_ID@$PROJECT_ID.iam.gserviceaccount.com

# grant objectCreator role to service account
gsutil iam ch serviceAccount:$DEV_SER_ACC_ID@$PROJECT_ID.iam.gserviceaccount.com:objectCreator \
 gs://$DEV_BUCKET_ID
```

## Dockerfile 作成

[Google Cloud SDK のインストール](https://cloud.google.com/sdk/docs/install?hl=JA#windows)を行ったら、`gsutil`は使えるようになりますが、あえて[Google Cloud SDK Docker](https://hub.docker.com/r/google/cloud-sdk/)を使います。まず、ファイルの配置はこちらです。

```bash
├── Dockerfile
├── certs
│   └── service-account-keyfile.json
├── csv
│   └── testdata.csv
└── docker-entrypoint.sh
```

### Dockerfile

```dockerfile
FROM google/cloud-sdk:slim

COPY ./docker-entrypoint.sh /

ENTRYPOINT ["/docker-entrypoint.sh"]
```

### docker-entrypoint.sh

```bash
#!/bin/bash
set -e

usage() {
  echo "Usage: $0 [-f upload csv file path] [-b gcs bucket name]" 1>&2
  exit 1
}

while getopts f:b:h OPT
do
  case $OPT in
    f)  FILE_PATH=$OPTARG
        ;;
    b)  BUCKET_NAME=$OPTARG
        ;;
    h)  usage
        ;;
  esac
done

if [ ! -f "$FILE_PATH" ]; then
    echo "$FILE_PATH does not exist."
    exit 0
fi

# service account login
gcloud auth activate-service-account --key-file=/certs/service-account-keyfile.json

# upload file to GCS
gsutil cp $FILE_PATH gs://$BUCKET_NAME
```

## アップロード

下記のコマンドを実行すれば、ファイルは GCS バケットにアップロードできます。

```bash
export BUCKET_ID=
export DEV_BUCKET_ID=$BUCKET_ID-dev

# create docker image
docker build -t uploader-gcloud .

# upload testdata.csv
docker run --rm \
 --mount type=bind,source=$PWD/certs,target=/certs \
 --mount type=bind,source=$PWD/csv,target=/csv \
uploader-gcloud -f /csv/testdata.csv -b $DEV_BUCKET_ID
```

## 雑談

そもそも、エンドユーザー環境に Docker を入れさせるのもハードル高いかもしれません。でも、これはチャレンジしないとわからない話と思って、結局、エンドユーザーは Google アカウントを作って、ロールの「Storage Object Creator」+権限の「storage.buckets.list、storage.objects.list」を付与した形で、日々 Console 上で操作することになりました。（人手不足は？）

とはいえ、Docker 初心者の自分にとって、Google Cloud SDK Docker は権限確認などに結構便利なツールだと認識できました。