1Password Service Accountsを利用し、AWS Lambda上で1Password CLIを実行してみる

2024.06.04

どうも!オペレーション部の西村祐二です。

1Password CLIをAWS Lambdaから実行できるかの検証する機会がありましたので、備忘録兼ねてブログにまとめておきたいと思います。

なぜ1Password CLIをAWS Lambdaで実行したいのか

パスワードを1Passwordに保存するであったり、共有や削除など関連作業を自動化、作業の省略化したかったためです。

下記のブログを自動化できたら作業効率化できるのではと思ったのがきっかけです。

1Passwordの非ユーザーとも安全にパスワードを共有できる機能「Psst!」でログイン情報をシェアしてみた | DevelopersIO

なぜCLIか

1PasswordにはSDKが提供されていますが、まだBetaでソースをみるに基本的なアイテムの作成、取得、更新、削除のみしかまだサポートされていないように見受けられたためです。

onepassword-sdk-python/src/onepassword/items.py at main · 1Password/onepassword-sdk-python

本来、やりたかったアイテムをシェアするリンクの作成などはCLIでしか利用できなさそうだったのでCLIを利用する流れになりました。SDKで利用できるようになればそれがベストだと思います。

1Password SDKs (beta) | 1Password Developer

AWS Lambdaで実行する際の認証まわりはどうするか

1Password Service Accountsを利用します。

1Password Service Accounts | 1Password Developer

所謂、事前にトークンを発行し、環境変数に設定することで1Passowrd CLIが利用できるようになるというものです。

1Password Service Accountsのいいなと思った点としては

  • 1Password Service Accountsで作成したvaultしかアイテムの削除ができない、既存のvaultはアクセスさせないやreadのみにできるなど権限設定でき安全
  • 作成したvaultにアクセスできるのは1Password Service Accountsのみ、ユーザからはアクセスできないようにできる
  • 必要があれば、ユーザにvaultのアクセス権限を付与できる
  • アイテムのシェアリンクを作成、削除ができる

1Password Service Account security | 1Password Developer

ただし、1Password Service Accountsにはレートリミットの制限があります。

Service account rate limits | 1Password Developer

個人な所感としては1時間あたりに100回の書き込み、1000回の読み込みができれば特に問題にはならなそうですし、予備のトークンを払い出しておいて、制限に引っかかれば、別のトークンを利用するなどで対応はできるかなと思いました。

1Password Service Accountsの他には1Password Connectを利用する方法もありますが、サーバーを立てる必要があったりと運用コストがあるため候補から一旦外してます。

1Password Connect | 1Password Developer

やってみる

1PasswordのService Accountsを作成

1PasswordのWeb画面から作成していきます。

(※個人で契約しているファミリープランの画面になります。)

  • 「デベロッパーツール」にアクセス

  • 「ディレクトリ」のタブをクリックし、アクセストークンのサービスアカウントをクリックするとサービスアカウント作成画面に移動

  • サービスアカウント名を決めて次へ

  • 保管庫(vault)の権限設定し、トークン作成

新規の保管庫(vault)の作成、既存の保管庫の読み取り、書き込み、共有の権限を選択できます。

  • サービスアカウントトークンを保存

トークンが作成されたら、今後利用するため手元に保存しておきます。(1Passwordに保存するボタンがあるのとてもいいですね。)

  • 作成したサービスアカウントを確認

「デベロッパーツール」にアクセスし、アクティブのタブにアクセスし、作成したトークンを選択すると、許可されている権限など確認できます。

有効期限はCLIから作成する際に設定できます。

1Password側の設定はこれで完了です。

Lambda関数の構築

AWS LambdaはコンテナイメージをLambda環境から利用できるので、そのコンテナ上で1Password CLIを実行します。

今回はNode.jsのコンテナを利用します。

Node.js Lambda 関数をコンテナイメージとともにデプロイする - AWS Lambda

AWS CDKを使ってデプロイ

今回、コンテナを利用するためのECRのレポジトリ作成、ビルド、デプロイをまるっと実施してLambdaデプロイできるCDKを使ってデプロイするので、CDKの環境を構築します。

mkdir my-app
cd my-app
cdk init app -l typescript
npm i aws-sdk
mkdir lambda
touch lambda/index.js

1Password CLIを実行するスクリプトを用意

今回、トークンのレートリミットを確認するコマンドを実行するスクリプトを用意しておきます。

scripts/1password/ratelimit.sh

#!/bin/bash
export HOME=/tmp
op service-account ratelimit  --format json

export HOME=/tmp は、デフォルトではコマンド実行時に一時ファイルが作成されるため、Lambda上で作成権限がないとエラーが発生するのを防ぐための設定になります。

Lambda関数のコード

基本的にシェルスクリプトを実行するだけの処理になります。今回は省略していますが、トークンはSercret Managerなどのサービスから取得するようにしてください。

const { exec } = require("node:child_process");
const util = require("node:util");
const execPromise = util.promisify(exec);

// シェルスクリプトのパス
const scriptPath = "./1password/ratelimit.sh";

exports.handler = async (event) => {
  const token = "";
  const env = Object.assign({}, process.env, {
    OP_SERVICE_ACCOUNT_TOKEN: token,
  });

  try {
    // シェルスクリプトを実行
    const { stdout, stderr } = await execPromise(`sh ${scriptPath}`, {
      env: env,
    });
    if (stderr) {
      console.error(`標準エラー出力: ${stderr}`);
    }
    console.log(`標準出力: ${stdout}`);
    const response = {
      statusCode: 200,
      body: stdout,
    };
    return response;
  } catch (error) {
    console.error(`エラー: ${error.message}`);
  }
};

Dockerfile

Dockerfileを用意します。1Password CLIのインストールや、CLIを実行するスクリプト、Lambda関数用のコードをコピーしています。

FROM --platform=linux/amd64 public.ecr.aws/lambda/nodejs:20
WORKDIR /var/task/

# Import 1Password GPG key and set up the repository
RUN rpm --import https://downloads.1password.com/linux/keys/1password.asc
RUN sh -c 'echo -e "[1password]\nname=1Password Stable Channel\nbaseurl=https://downloads.1password.com/linux/rpm/stable/\$basearch\nenabled=1\ngpgcheck=1\nrepo_gpgcheck=1\ngpgkey=https://downloads.1password.com/linux/keys/1password.asc" > /etc/yum.repos.d/1password.repo'

# Install 1Password CLI
RUN dnf update -y
RUN dnf install -y 1password-cli

# Copy scripts directory and set execute permissions
COPY scripts/1password ${LAMBDA_TASK_ROOT}/1password
RUN chmod +x ${LAMBDA_TASK_ROOT}/1password/*

# Copy function code
COPY lambda/index.js ${LAMBDA_TASK_ROOT}
  • --platform=linux/amd64はM1 MacからCDK経由でデプロイするとエラーが出るのでその対策のために設定してます。

AWS CDK

lib/cdk-stack.ts

import * as cdk from "aws-cdk-lib";
import { Construct } from "constructs";
import * as lambda from "aws-cdk-lib/aws-lambda";

export class MyAppStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);
    createDockerImageLambdaFunction(this);
  }
}

function createDockerImageLambdaFunction(scope: Construct) {
  const assetImageCodeProps: lambda.AssetImageCodeProps = {
    cmd: ["lambda/index.handler"],
  };
  const props: lambda.DockerImageFunctionProps = {
    code: lambda.DockerImageCode.fromImageAsset(".", assetImageCodeProps),
    memorySize: 512,
    timeout: cdk.Duration.seconds(30),
  };

  return new lambda.DockerImageFunction(scope, "lambda", props);
}

デプロイ

npm run cdk -- deploy

動作確認

AWSのマネコンからデプロイされたLambdaをテスト実行してみましょう。

正常に成功し、1Password CLIが実行された結果が返ってきてることがわかります。

CloudWatch Logs上にもログが出力されていることがわかります。

これで、同様に1Password CLIのスクリプトを用意することでパスワード周りの自動化、効率化ができそうです。

さいごに

1Password Service Accountsを利用し、AWS Lambda上で1Password CLIが実行できるか検証し、手順をまとめてみました。

調べてもなかなか情報が出てこなかったり、ハマったりして時間がかかりましたが、AWS Lambda上で1Password CLIが実行できることが確認できました。

誰かの参考になれば幸いです