Shogo's Blog

Oct 21, 2023 - 2 minute read - golang 1password github

1Passwordで管理しているシークレットを各種サービスに同期するプログラムを書いた

弊社では各種シークレットは1Passwordで管理しています。 シークレットの棚卸し作業をしていて不便を感じたので、 1Passwordで管理しているシークレットを各種サービスに同期するプログラムを書いてみました。

背景

何が困るって、シークレットの出処がわからないこと。

たとえばCI/CDサービスのシークレット管理機能にシークレットが登録されている場合、 ヒントが FIREBASE_SECRET という名前だけ、ということがよくあります。 このヒントだけだと、Firebaseで使われていそうなのはわかる・・・でもFirebaseのプロジェクトたくさんあるんだよな、どれだろう・・・ってなります。 プロジェクト名やサービスアカウント名までわからないと、シークレットの出処を特定できません。

1Passwordにはシークレットの出処をメモとして残しておけるので、 せめて「このシークレットは、1Passwordのこの項目と対応しています」という情報が分かれば特定が簡単になります。

「シークレットと1Passwordの項目の対応付け」、ちょっとしたテキストを書くだけで十分なんですが、それができないのが人間というもの・・・。 そこでさらに推し進めて「シークレットと1Passwordの項目の対応付け」を機械可読な形式にして、「1Passwordの項目からシークレットの自動反映」を行います。 普段から自動反映する習慣をつけておけば、対応付けが漏れる心配はありません。

使い方

1Passwordとの連携には1Password CLIを使用しています。 あらかじめインストールしておいてください。

シークレットをファイルに書き出す

Private Vaultに入っている、Testという名前のアイテムを読み出す設定例です。

# .op-sync.yml
secrets:
  MyPassword:
    type: template
    output: .envrc
    template: |
      MY_PASSWORD={{ op://Private/Test/password }}      

1Password CLIでサインインして、 op-sync コマンドを叩けば 1Password からパスワードを引っ張ってきてファイルに書き出してくれます。

$ eval $(op signin)
Enter the password for shogo82148@gmail.com at my.1password.com:

$ op-sync
2023/10/21 16:58:32 INFO 1password user information url=https://my.1password.com email=shogo82148@gmail.com
The following changes will be applied:
file ".envrc" will be created
Do you want to continue? (y/n) [n]: y

$ cat .envrc
MY_PASSWORD=MRj2wMsXU2qL9XAfKwdmdfFW

これは実際のところ、内部で echo "MY_PASSWORD={{ op://Private/Test/password }}" | op inject -o .envrc 相当のことをやっているだけなので、 シェルスクリプトでも十分実現可能です。 しかし実用するとなると、 Dry Run して実行内容を事前に確認する、1Passwordの内容と実際のファイルの内容があっているか確認する、などなど色々やりたいことが出てきます。 これらを全部シェルスクリプトで実装するのは大変です。

GitHub Actionsと連携する

以下は GitHub Actions のシークレットに設定する例です。 反映には GitHub CLI が必要です。

secrets:
  MyPassword:
    type: github
    repository: shogo82148/op-sync
    name: MY_PASSWORD
    source: op://Private/Test/password

Environmentsにも対応しています。

secrets:
  MyPassword:
    type: github
    repository: shogo82148/op-sync
    environment: production # production environment からのみアクセスを許可する
    name: MY_PASSWORD
    source: op://Private/Test/password

Dependabotだけにアクセスを許可することも可能です。

secrets:
  MyPassword:
    type: github
    repository: shogo82148/op-sync
    application: dependabot
    name: MY_PASSWORD
    source: op://Private/Test/password

AWS System Manager Parameter Store と連携する

Parameter Store とも連携できます。

secrets:
  MyPassword:
    type: aws-ssm
    account: "123456789012"
    region: ap-northeast-1
    name: /path/to/secret
    source: op://Private/Test/password

実務ではAWSアカウントを複数扱うことも多いので、アカウント番号も記載するようにしました。 現在のセッションとアカウント番号が異なる場合は、単純にこの設定を無視します。

AWS Secrets Manager と連携する

AWS Secrets Managerと連携する例です。

secrets:
  MyPassword:
    type: aws-secrets-manager
    account: "123456789012"
    region: ap-northeast-1
    name: password
    template:
      username: admin
      password: "{{ op://Private/Test/password }}"

{{ }} で囲われている部分がテンプレートになっています。 実際には {"username":"admin","password":"MRj2wMsXU2qL9XAfKwdmdfFW"} のようなJSONが シークレットとして設定されます。

まとめ

1Passwordで管理しているシークレットを各種サービスに同期するプログラムを書いてみました。 一度設定ファイルを書いてしまえば op-sync コマンドを叩くだけで、1Passwordの設定を各種サービスに反映できます。

まだ「とりあえず書いてみました」な段階ですが、みなさんからのフィードバックをお待ちしてます。