beta

Node.jsでGoogle Cloud Storageをセキュアに操作する

Node.jsアプリからGoogle Cloud Storageを操作してみます。クラウド周りは権限をしっかりしなと危険なので、権限の設定やクレデンシャルの保持など、なるべくセキュアになるように組んでみました。

公開日:2020年1月15日

やりたいこと

Node.jsアプリから、Google Cloud Storageの操作をします。

具体的には、

  • ファイル一覧の取得
  • ファイルのアップロード
  • ファイルの削除
  • ファイルの更新
  • ファイルの公開

をやってみます。

事前準備

事前にやっておくことは、

  • Google Cloud Storage(GCS)のバケットを作る
  • Google Cloud Platform(GCP)の「IAM」でメンバーを追加
  • クレデンシャルファイルを保存
  • 追加したメンバーにバケットへの権限を付与

という感じです。

以下、ハマりそうなところだけピックアップして解説します。

クレデンシャルファイルを保存

GCPでIAMメンバーを作成すると、jsonファイルのクレデンシャルファイルがダウンロードできます。

このファイルは一度きりしか取得できない上に、GCPでのアクセス権限を与える危険なファイルなので、流出しないように細心の注意が必要です。

後ほど、実際にNode.jsアプリを作成する時に使います。

追加したメンバーにバケットへの権限を適切に付与

ネット上の記事だと、「allUsers」に対して「ストレージの管理者権限」を与えるケースをよくみますが、これだと、何か問題があってクレデンシャルが流出したり、プログラムに穴があった時に、GCSに限ってはなんでもできてしまうため、かなり危険です。

権限は、作成したIAMに対してだけ

  • ストレージオブジェクトの閲覧者
  • ストレージオブジェクトの作成者

までを与えるのがベストです。

ただし、GCSの場合「更新=削除&作成」なので、ファイルを更新する可能性がある場合は、

  • ストレージオブジェクトの管理者

も必要になります。

ただし、ストレージオブジェクトの管理者はGCSに関してはかなりのことができる権限なので、「更新しない」という選択肢も視野に入れると良いでしょう。

実装する

初期化

まずは、Node.jsアプリを初期化します。

npm init

出てくる質問は全てYes(エンター)でOKです。

プラグインのインストール

続いて、プラグインをインストールします。

使うのは、

  • @google-cloud/storage : Goolge公式のライブラリ
  • dotenv : .envを使うためのライブラリ

となります。

npm install @google-cloud/storage dotenv

これでOKです。

.envファイルを用意

続いて.envファイルを用意します。

Node.jsのGCS用ライブラリは、クレデンシャルとプロジェクトIDを環境変数から取得するようになっています。

環境変数はexportコマンドで入れても良いのですが、プロジェクトごとにクレデンシャルが違うこともありますし、なるべく環境変数は本番では使いたくないので、.envファイルで一括管理します。

.envの中身は、

GOOGLE_APPLICATION_CREDENTIALS="jsonファイルへのパス"
GOOGLE_CLOUD_PROJECT="プロジェクトID"

とします。

クレデンシャルファイルは、プログラムとは別のところに保存しておきましょう。そうすることで、Gitなどで間違ってアップロードしてしまうなどの人為的ミスを防げます。

Herokuなど、プラットフォーム側で環境変数が設定できる場合は、クレデンシャルファイルと.envファイルはローカルでのみ使うようにして、Heroku側にはアップロードしないで、管理画面から環境変数登録しましょう。

こうすることで、クレデンシャルの流出を防ぎ、GCPアカウントやバケットをセキュアに保つことができます。

操作するスクリプトを書く

ようやく本番です。

まずは、先ほどの.envを読み込みます。

require('dotenv').config()

これだけで、.envの中身が反映されて、GCSで必要な環境変数が設定されます。

続いて、GCS用のライブラリを読み込みます。

const {Storage} = require('@google-cloud/storage');

読み込んだら、設定内容をロードさせつつ、GCSオブジェクトを作成します。

const storage = new Storage({keyFilename: process.env.GOOGLE_APPLICATION_CREDENTIALS, projectId: process.env.GOOGLE_CLOUD_PROJECT});

引数は、先ほど.envで設定した内容です。

  • keyFilename: クレデンシャルのjsonファイル
  • projectId: プロジェクトID

となります。

これだけで、あとはstorageオブジェクトを操作すればGCSのファイル操作ができます。

アップロード(作成・更新)する

まずはアップロードをしてみます。

ローカルのカレントディレクトリにある「test.json」をアップロードしてみます。

const bucketName = 'バケット名'
await storage.bucket(bucketName).upload('test.json');

エラーが出なければ完了です。簡単ですね。

なお、GCSでは作成も更新も「upload」で実行します。ファイルがなければ作成で、ファイルがあれば「一旦削除して、新規で作成する」ということになります。

ですので、IAMに「ストレージオブジェクトの作成者」権限までしかないと、「delete権限がない」というエラーが返ってくるはずです。

一覧を取得する

一覧は「getFiles」メソッドです。

const [files] = await storage.bucket(bucketName).getFiles();
files.forEach(file => {
    console.log(file.name);
});

ファイルを公開する

アップロードしたファイルは基本的には非公開なので、公開ステータスにします。

await storage.bucket(bucketName).file('test.json').makePublic();

これで外部からアクセスできるようになります。

削除する

削除は、fileメソッドでファイルを絞り込んで、「delete」メソッドを実行します。

await storage.bucket(bucketName).file('test.json').delete();

そのほかの操作・コマンド

GCSライブラリのレポジトリにはサンプルスクリプがたくさんあるので、詳しくはそちらをどうぞ。

googleapis /nodejs-storage

まとめ

最後に一連のスクリプトをまとめてみます。

なお、GCSプラグインはPromiseで動かす必要があるため、全てasync/awaitで書く必要があります。

require('dotenv').config()

const {Storage} = require('@google-cloud/storage');

(async () => {
    try {
      const storage = new Storage({keyFilename: process.env.GOOGLE_APPLICATION_CREDENTIALS, projectId: process.env.GOOGLE_CLOUD_PROJECT});

      const bucketName = 'バケット名'

      // 作成・更新
      await storage.bucket(bucketName).upload('test.json');

      // 一覧をlogに出力
      const [files] = await storage.bucket(bucketName).getFiles();
      files.forEach(file => {
          console.log(file.name);
      });

      // 公開する
      await storage.bucket(bucketName).file('test.json').makePublic();
      
      //削除
      await storage.bucket(bucketName).file('test.json').delete();

      
    } catch (err) {
      console.error('ERROR:', err);
    }
})();

Node.jsアプリから、Google Cloud Storageの操作をする方法をみてきました。

ライブラリのおかげで流れさえわかってしまえば、かなり簡単にNode.jsからGCSの操作ができます。どちらかというと、権限を適切にするところの方が試行錯誤するところかもしれません。