VercelにデプロイしたアプリからSecret Managerを利用する
こんにちは。
自作アプリを作っている際に、NotionのAPIKeyやDBIDをGoogle CloudのSecret Managerに保存したいけど、Vercelにデプロイしているアプリからだとどうすればいいのだろう?となったのでまとめます。
ベストプラクティスは、おそらくWorkload Identity連携を用いるのがベターのようですが、個人開発のレベルだったのでサービスアカウントを利用するやり方を採用しています。
この記事のゴール
VercelにデプロイしているNext.jsのアプリのAPI RoutesからGoogle CloudのSecret Managerにアクセス行い、APIKeyなどを保存したり取得したりできるようにする。
手順
(この記事に辿り着いている時点である程度、今回やりたいことに関して理解されているだろうと思い色々省いて説明しています)
- Secret Managerにアクセス権があるGoogle Cloudのサービスアカウントを用意する
- Secret ManagerのSDKをインストール
- 必要な環境変数をVercelに保存しておく
- 環境変数とSDKを利用してSecret Managerを叩く
1. サービスアカウントの用意
公式ドキュメントなどを参考にしてください🙇
2. SDKをインストール
公式を参考にして入れます。私はyarnを利用しているので以下のようなコマンドで入れました。
yarn add @google-cloud/secret-manager
3. 必要な環境変数をVercelに保存しておく
今回は以下の3つを利用します。
全てサービスアカウントのJSONキーから取得しています。
- PRIVATE_KEY
- CLIENT_EMAIL
- PROJECT_ID
私はそのままの名前で3つを保存しています。
今回のコードはクライアント側で実行しないことを前提にしています。そのため、サービスアカウントのJSONからPRIVATE_KEYを取得しそのまま環境変数としてVercelに保存しています。
4. 環境変数を利用してSecret Managerを叩く
以下のコードはAPI Routes上で実行しています。
以下のようなコードを用意して使いまわしています。
ポイントは2つです。
- コードを見てもらうとわかると思いますが、PRIVATE_KEYをbase64エンコードしています
- SecretManagerServiceClientを初期化する際にauth変数を渡しており、そこであらかじめ作成したauth変数を利用することでデフォルトのサービスアカウントなどを探しに行かないようにしています。
let client: SecretManagerServiceClient | null = null
export const getClient = () => {
if (client === null) {
const privateKey = process.env.PRIVATE_KEY ?? ''
const private_key = Buffer.from(privateKey, 'base64').toString('utf8')
// 一回GoogleAuth型のauthを作るとサジェストが効いて楽
const auth = new GoogleAuth({
credentials: {
private_key: private_key.replace(/\\n/g, '\n'),
client_email: process.env.CLIENT_EMAIL,
},
projectId: process.env.PROJECT_ID,
})
// authを渡すことでデフォルトのサービスアカウントを探しに行かなくなる
client = new SecretManagerServiceClient({ auth })
}
return client
}
まとめ
以上の4ステップでおそらくVercelにデプロイしたアプリからでもSecret Managerを利用できるようになっていると思います。
今回の記事はサービスアカウントを利用してGoogle Cloudのサービスを利用する際には応用が効くと思う(SDKのクライアントの初期化がポイント)のでよければ他のサービスを利用する際にも参考にしてみてください。
個人的にはGoogleAuth型
でauth変数を初期化するところがポイントで、うまく行っていない頃は間違えてprivate_key
, client_email
, project_id
を全て同じレベルにしてobjectを渡していたりしたので、その辺で詰まっている人に届くと嬉しいです。
NGコード(参考)
const client = new SecretManagerServiceClient({
private_key: privateKey.replace(/\\n/g, '\n'),
client_email: process.env.CLIENT_EMAIL,
projectId: process.env.PROJECT_ID,
})
Discussion