本記事は KDDIアジャイル開発センター(KAG) Advent Calendar 2024 の23日目です。
パスワード管理サービスである 1Password には、トークンを用いて保管庫の情報にアクセスできる Service Accoutns という仕組みがあります。
これを用いることで 1Password に保存した秘密情報を Lambda から取得できます。
この機能を試してみました。
Service Account を作成する
公式ドキュメントに従って進めます。
まずは Lambda が読み取りたいアイテムを保管する保管庫を作成します。
次に 1Password.com にサインインし、デベロッパーツールを選択して、アクセストークンのサービスアカウントを選択します。
すでに Service Account を作成している場合などは、上部のディレクトリを選択します
Service Account 名を決めます。
Service Account のパーミッションを決めます。ここで決めたパーミッションは後で変更できません。
- 保管庫を作成できるか
- 選択した保管庫について
- 読み取り
- 書き込み
- 共有
変更するには Service Account を作り直す必要があります
Service Account トークンが発行されるので保存します。(上記のトークンは実際のトークンの一部分です)
このトークンはこの場でしか閲覧できません。
1Password に保存 を押すとトークンを 1Password のアイテムとして保存することができます。
これで Service Account のトークンを発行できました。
1Password SDK を使って Lambda から保管庫のアイテムを読み取る
こちらも基本的には公式ドキュメントに沿って進めます。
Service Account トークンを Secrets Manager に保存する
公式ドキュメントでは先程発行した Service Account トークンを環境変数から取得していますが、Lambda の場合は環境変数にトークンなどの秘密情報を保存するのは推奨されていないので、代わりに AWS Secrets Manager にトークンを保存します。
Secrets Manager の画面から新しいシークレットを保存するを押します。
シークレットのタイプはその他のシークレットのタイプを選び、キー/値のペアでプレーンテキストを選んで Service Account トークンを貼り付けます。
暗号化キーはデフォルトでOKです。
次にシークレットの名前を入力します。
その他は必要に応じて追加します。(何も入力しなくてもOKです)
ローテーションの設定は、必要に応じて編集します。(何も入力しなくてもOKです)
次の画面へ移り、内容を確認した後保存をすることで Secrets Manager にトークンが保存されます。
秘密情報を取得する Lambda を作成する
npm パッケージを作成し、package.json
に type: "module"
を追加します。
{
"name": "Tutorial",
"version": "1.0.0",
"description": "",
"main": "index.js",
+ "type": "module",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
1Password SDK と AWS SDK for JavaScript をインストールします。
npm install @1password/sdk @aws-sdk/client-secrets-manager
次に index.js
を下記のようにします。
import {
SecretsManagerClient,
GetSecretValueCommand,
} from "@aws-sdk/client-secrets-manager";
import { createClient } from "@1password/sdk";
// Secrets Manager から 1Password Service Account トークンを取得
const secret_name = "<your secret name>";
const awsSecretsManagerClient = new SecretsManagerClient({
region: "ap-northeast-1",
});
let response;
try {
response = await awsSecretsManagerClient.send(
new GetSecretValueCommand({
SecretId: secret_name,
})
);
} catch (error) {
throw error;
}
const opServiceAccountToken = response.SecretString;
// 認証済みの 1Password クライアントを作成する
const client = await createClient({
auth: opServiceAccountToken,
// 下記は自由に設定
integrationName: "My 1Password Integration",
integrationVersion: "v1.0.0",
});
// 1Password に保存した秘密情報を取得
// "op://~" は秘密情報への秘密参照
const secret = await client.secrets.resolve("op://vault/item/field");
ここで client.secrets.resolve()
に渡す秘密参照とは下記のように取得できます。
最後に、Lambda が Secrets Manager にアクセスできるようにするため、Lambda にアタッチする IAM ロールに下記を追加します。
{
"Version": "2012-10-17",
"Statement": [
...,
+ {
+ "Action": [
+ "secretsmanager:GetSecretValue"
+ ],
+ "Resource": "<Secrets Manager の ARN>",
+ "Effect": "Allow"
+ }
]
}
以上で Lambda から 1Password に保存した秘密情報にアクセスできるようになります。
まとめ
1Password Service Accounts を使って AWS Lambda から 1Password に保存した秘密情報を取得する方法を試しました。
一見、秘密情報を AWS Secrets Manger に保存するだけで良いのでは?と思いますが、秘密情報が 1Password に一元化されることで下記のメリットがあると考えられます。
- 秘密情報が複数の場所から参照される場合、秘密情報の更新が1箇所のみの変更で済む
- 秘密情報が 1Password にアイテムとして保存されていると、開発時に 1Password CLI と組み合わせた開発ができる
- 秘密情報へのアクセス権を誰が持っているかを 1Password で一元管理できる
用途に合わせて、活用していきたいです。