弊社では各種シークレットは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の設定を各種サービスに反映できます。
まだ「とりあえず書いてみました」な段階ですが、みなさんからのフィードバックをお待ちしてます。