GCEで自前のサービスアカウントを使用してGoogle Cloud Storageにアクセスしてみた


GCP コンソールから Compute Engine を初めて選択したとき、Compute Engine API を有効にするか聞かれます。有効を選択すると、Compute Engine のデフォルトのサービスアカウントが作成されます。GCE でインスタンスを作成するとサービスアカウントとしてこのデフォルトのサービスアカウントが指定されています。

デフォルトのサービスアカウントには基本ロールの``編集者``が割り当てられていますが、基本ロールは強い権限を持っており本番環境で使用するのは非推奨になっています。

>注意: 基本ロールには、すべての Google Cloud サービスにかかわる何千もの権限が含まれます。本番環境では、他に選択肢がない限り、基本ロールを付与しないでください。代わりに、ニーズに合わせて最も制限された事前定義ロールまたはカスタムロールを付与します。

https://cloud.google.com/compute/docs/access/service-accounts?hl=ja

https://cloud.google.com/iam/docs/understanding-roles?hl=ja#basic

今回は、インスタンスのサービスアカウントにデフォルトのサービスアカウントではなく自前で用意したサービスアカウントを指定して PHP で Cloud Storage にアクセスしてみます。

### サービスアカウントを作成

まずは今回使用するサービスアカウントを作成します。

1. [**IAMと管理**]から[**IAM**]を選択。[**サービスアカウント**]を選択し、[**\+サービスアカウントを選択**]を選択。「1. サービスアカウントの詳細」の各項目に任意の名前を入力し[**作成**]を選択。
1. 「2. このサービス アカウントにプロジェクトへのアクセスを許可する (省略可)」でサービスアカウントにロールを割り当てます。今回は Cloud Storage にアクセスしたいので、事前定義ロールの[**ストレージ管理者**]を割り当てます。
 1. [**ロールを選択**]を選択。
 1. [**Type to filter**]に「ストレージ」と入力。
 1. [**ストレージ管理者**]を選択
 1. ロールを割り当てたら[**続行**]を選択。
1. [**完了**]を選択。

### GCEインスタンスに割り当て

作成したサービスアカウントをインスタンスに割り当てます。

1. 画面左側ペインの[**Compute Engine**]から[**VMインスタンス**]を選択。
1. 画面上部にある[**インスタンスを作成**]を選択。
1. 画面下部に[**IDとAPIへのアクセス**]という項目がありサービスアカウントを選択できるセレクトボックスがあるので、先ほど作成したサービスアカウントを選択します。サービスアカウントを選択したらその他の項目には任意の値を入力し[**作成**]を選択。

数分待つとインスタンスが作成されます。

## ライブラリをインストール

インスタンスが作成できたら、動作検証のため``php``と``composer``をインストールします。

``composer``を使ってライブラリをインストールするので、まずは``php``をインストールしてその後``composer``をインストールします。
https://getcomposer.org/download/

```bash
$ sudo apt-get update && sudo apt-get install -y php
$ php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
$ php -r "if (hash_file('sha384', 'composer-setup.php') === '756890a4488ce9024fc62c56153228907f1545c228516cbf63f885e036d37e9a59d27d63f46af1d4d07ee0f76181c7d3') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
$ php composer-setup.php
$ php -r "unlink('composer-setup.php');"
$ sudo mv composer.phar /usr/local/bin/composer
$ which composer
/usr/local/bin/composer
```

### google-cloud-phpをインストール

``composer``を使って``google/cloud-storage``をインストールします。
https://github.com/googleapis/google-cloud-php/tree/master/Storage

``composer``実行時に下記のエラーが発生したので``git``を先にインストールしておきます。

```
[RuntimeException]                                        
  git was not found in your PATH, skipping source download
```

```
$ sudo apt-get install -y git
$ sudo composer require google/cloud-storage
```

## 検証

Cloud Storageにファイルをアップロードするコードを書いて、コードと環境変数にクレデンシャル情報を設定しないで動作するか試してみます。``Google Cloud PHP``ライブラリはクレデンシャルを``コード →  環境変数 → GCEインスタンス``の順に探しにいきます。
https://github.com/googleapis/google-cloud-php/blob/master/AUTHENTICATION.md#project-and-credential-lookup

下記のコードを用意してCloud Storageにアクセスできるか試してみます。

```php
<?php
require_once 'vendor/autoroad.php';

use Google\Cloud\Storage\StorageClient;

$storage = new StorageClient();
$bucket = $storage->bucket('bucket-name');
$bucket->upload(fopen('file.txt', 'r'));
```

実行してみます。

```
$ php index.php
```

Cloud Storage内のバケットに``file.txt``があれば成功です。サービスアカウントのロールを「Storage オブジェクト閲覧者」に変更したらどうなるかも確認してみます。ロールの変更は[**IAMと管理**]の[**IAM**]から行います。ロールの変更をインスタンスに反映するために、インスタンスをリセットします。インスタンスがリセットされたら再度 PHP を実行してみると、

```bash
PHP Fatal error:  Uncaught Google\Cloud\Core\Exception\ServiceException: {"error":{"code":403,"message":"gcs-access@xxx.iam.gserviceaccount.com does not have storage.buckets.get access to the Google Cloud Storage bucket.","errors":[{"message":"gcs-access@xxxx.iam.gserviceaccount.com does not have storage.buckets.get access to the Google Cloud Storage bucket.","domain":"global","reason":"forbidden"}]}} in /var/www/html/vendor/google/cloud-core/src/RequestWrapper.php:368
```

``storage.buckets.get``の権限がなくてアクセスできなくなりました。ストレージオブジェクト閲覧者の権限は、

- resourcemanager.projects.get
- resourcemanager.projects.list
- storage.objects.get
- storage.objects.list

なので、``storage.buckets.get``に対しては権限がないので正しく動いていることが確認できました。

## 参考

https://qiita.com/atsumjp/items/152197b5039fe78c98ba

http://googleapis.github.io/google-cloud-php/#/docs/cloud-storage/v1.23.1/storage/readme