💡

Bitwardern CLI + sops で無料かつ安全に API Token を git 管理

に公開

備忘録
無料にこだわったやり方
※geminiを使って書いています。

やりたいこと

  1. 秘密鍵を生成
  2. Bitwardenに秘密鍵を格納
  3. API token暗号化しgit管理
  4. APIを叩く際にBitwardenから秘密鍵を取得、API tokenを複合して好きに使う(私の例ではcloudflare をterrafromを使って管理しています)

手順

0. 必要なツールのインストール

Macの場合、Homebrewを使って必要なCLIツールをインストールします。

brew install sops age bitwarden-cli

1. 秘密鍵の生成 (age)

age を使って、暗号化と復号に使うための鍵ペア(公開鍵・秘密鍵)を生成します。

age-keygen
# 実行すると、以下のように公開鍵と秘密鍵が出力されます
# Public key: age1lq...(長い公開鍵)...cqyma
# Private key: AGE-SECRET-KEY-1...(長い秘密鍵)...gqj7a

2. Bitwardenに秘密鍵を格納

  1. BitwardenのVault(Web版やデスクトップアプリ)で「セキュアノート」を新規作成します。

名前: SOPS Private Key (など、自分にとってわかりやすい名前)
ノート(メモ): 手順1で生成した 秘密鍵 (AGE-SECRET-KEY-1...で始まる行全体) を貼り付けて保存します。

  1. Bitwarden CLIにログイン

    bw login
    
  2. ターミナルで、今保存したアイテムの ID を取得します。

    bw list items --search "SOPS Private Key"
    

    出力されるJSONの中から "id": "..." の値(a1b2c3d4-.... のようなUUID形式)をコピーして、控えておきます。

3. API token暗号化しgit管理 (sops)

  1. sopsの設定ファイルを作成
    プロジェクトのルートに .sops.yaml ファイルを作成し、手順1で生成した 公開鍵 (age1...) を指定します。

    # .sops.yaml
    creation_rules:
      - age: 'age1lq...(ここにあなたの公開鍵)...cqyma'
    
  2. APIトークンファイルの暗号化
    sopsコマンドで暗号化ファイルを作成します。Terraformで読み込むファイル名を secrets.sops.yaml とします。

    sops secrets.sops.yaml
    
  3. コマンドを実行すると、ターミナル上でデフォルトのエディタ(vinanoなど)が起動します。そのエディタ上で、暗号化したいAPIトークンをYAML形式で入力します。(cloudflareは例です。)

    # エディタ内で入力する内容
    api_token: "your-super-secret-api-token"
    
  4. ファイルを保存してエディタを終了します。secrets.sops.yaml が作成され、中身が暗号化されていることが確認できます。

  5. この .sops.yaml(公開鍵の情報)と secrets.sops.yaml(暗号化されたトークン)の2つをGitリポジトリにコミットします。秘密鍵は含まれていないため、安全にGit管理できます。

4. Terraformの設定

main.tf(または providers.tf)に sops プロバイダを追加し、暗号化ファイルを読み込む設定を記述します。

terraform {
  required_providers {
    # sopsプロバイダ
    sops = {
      source  = "carlpett/sops"
      version = "~> 1.0"
    }
    # Cloudflareプロバイダ (今回の例)
    cloudflare = {
      source  = "cloudflare/cloudflare"
      version = "~> 4.0"
    }
  }
}

# 1. sopsで暗号化ファイルをメモリ上に復号
data "sops_file" "secrets" {
  source_file = "secrets.sops.yaml"
}

# 2. 復号した値をローカル変数に格納
locals {
  # secrets.sops.yaml内の "cloudflare_api_token" キーを参照
  cloudflare_api_token = data.sops_file.secrets.data["cloudflare_api_token"]
}

# 3. providerブロックでローカル変数を参照
provider "cloudflare" {
  api_token = local.cloudflare_api_token
}

# 4. あとは好きにリソースを定義
resource "cloudflare_record" "example" {
  # ...
}

terraform apply を実行する際に、Bitwardenから秘密鍵を取得して SOPS_AGE_KEY 環境変数に渡します。

  1. Bitwarden Vaultのアンロック (セッション毎に1回):
    bw unlock を実行し、セッションキーを環境変数 BW_SESSION にセットします。
    export BW_SESSION=$(bw unlock --raw)
    # Master Passwordの入力が求められます
    
  2. Terraformの実行:
    SOPS_AGE_KEY 環境変数に「Bitwardenから取得した秘密鍵」をセットしつつ、terraform apply を実行します。
    # <BitwardenのアイテムID> は手順2で取得したIDに置き換えてください
    SOPS_AGE_KEY=$(bw get notes <BitwardenのアイテムID> --session $BW_SESSION) terraform apply
    

これで、terraform apply が実行されるプロセス内でのみ秘密鍵がメモリにロードされ、secrets.sops.yaml が復号されてTerraformに渡されます。平文のキーがディスク上に残ることはありません。

Discussion