AWS

sopsとAWS KMSで機密情報データを暗号化する

Mozillaで開発されている sops (Secrets OPerationS) を使用することで、YAMLやJSON、INI形式で書かれたファイルをAWS KMSやGCP KMS、PGPで暗号化できるようになります。

特に便利なのが Terraform や Terragrunt と連携できる点であり、ファイルを暗号化したまま機密情報を Terraform, Terragruntから利用できます。(実際にTerragrunt で利用する場合はこちらの記事を参考にしてみてください)

また暗号化しているので、Terraform, Terragrunt のソースコードとともに機密情報ファイルをGitHubで共有することができるようになります。

今回は sops の基本的な使い方をまとめてみます。

スポンサーリンク

前提

sopsを動かす前にいくつか事前準備が必要となります。

ローカルPCのAWS認証情報の設定

ローカルPCのAWS認証情報を使用します。

以下の記事を参考にしながらAWSプロファイルを設定してください。こちらのTipsのようにAWS Vault経由でも sops を使用できます。

KMSでキーを作成

sops で必要となるキーをKMSで作成します。

AWSマネジメントコンソールの画面上部検索窓 [ kms ] で検索 → [ Key Management Service ]をクリック → KMS画面の左ペイン [ カスタマー管理型のキー ] → 画面右上の[ キーの作成 ] を押下します。

キーを設定

キーのタイプ で [ 対称 ] を選択。詳細オプションはデフォルトのままでOKです。[ 次へ ] を押下します。

ラベルを追加

エイリアス に任意の名前を入力し [ 次へ ] を押下します。

キーの管理アクセス許可を定義

キーの作成や削除、更新などの管理作業ができるIAMユーザー、ロールを設定します。

キーの使用アクセス許可を定義

キーを使用できるIAMユーザー、ロールを設定します。

今回はsopsをローカルPCにインストールして作業を行うため、ローカルPCで使用するAWS認証情報でKMSにアクセスできるように設定をしてください。

最後に [ 完了 ] を押下し、キーを作成します。

KMSキーのARNの確認

sops実行時にキーのARNが必要となります。

KMS画面の左ペイン [ カスタマー管理型のキー ] → 当該キーをクリック → 一般設定 の ARN を確認してください。

sopsの使用方法

インストール

環境ごとに用意されたこちらのパッケージを使用します。MacであればHomebrewでインストールできます。

$ brew install sops
...
$

注意事項としてpipでもsopsのインストールが可能ではあるのですが、このPython版のsops非推奨となっています。

現在はGoで開発されていますが、Ver1.18まではPythonで開発されていたようです(本記事執筆時点の最新版は3.7.2)。このPython版のsopsはもうメンテナンスはされておらず、sopsの設定ファイル構文も今とは異なる部分があります。

間違いなく最新版を利用できるようにするため(OSパスの設定次第でPython版が使用される可能性がある)、Python版がローカルPCにある場合はpipでアンインストールしておきましょう。

$ which sops
/Users/zoo200/.pyenv/shims/sops
$
$ pip uninstall sops
...
Proceed (Y/n)? y
  Successfully uninstalled sops-1.18
$

暗号化ファイルを新規作成

sopsでの暗号化時にKMSキーのARNを指定する必要があります。ARNの指定方法は以下に挙げるように何通りかあります。

また暗号化できるファイル形式はYAML, JSON, INIなど何種類かありますが、今回はYAMLを使用します。

エディタはデフォルトでvimが使用されます。環境変数$EDITORで変更可能です。

コマンドのオプションでARNを指定して暗号化

-k ( --kms ) オプションでARNを指定しつつ、以下の例では demo_secrets.yml というファイル名で暗号化ファイルを新規に作成しています。

新規作成時には最初にサンプルが表示されますが、このサンプルは全部消してしまって構いません。

$ sops -k 'arn:aws:kms:ap-northeast-1:123456789012:key/451aaa-bbb-ccc-ddd-eee' demo_secrets.yml
## サンプルを削除しYAMLで機密情報を記述します
demo-db:
    user: demo-user
    pass: demo-pass
demo-key:
    id: demo-id
    secret: demo-secret

作成されたファイルを cat で開くと以下のとおり、key:valueのvalueの部分が ENC[AES256_GCM,… と暗号化されていることが確認できます。

$ cat demo_secrets.yml
#ENC[AES256_GCM,data:FAKMXzjX2gFOrfqvTvBaN35xS2v3SbE5l1xoNRckEO3zFbA9JRk80E6kWihQ0H+tMRE=,iv:H4w5aI1lxK83NOxavWN0F78l7ztaxcAPZs8iEjTW8KM=,tag:QRd8DragjngVJf86k6TQ7g==,type:comment]
demo-db:
    user: ENC[AES256_GCM,data:ofKe2TcUGcJV,iv:gGpahtoJG8WuIC6rL8rN2kC9ZK3RhYGK4cOOeO1b8fU=,tag:bjzh5zvFuWBm+uNBAP2Xdw==,type:str]
    pass: ENC[AES256_GCM,data:BnnmCKnTmc0m,iv:pQXYDYBfP3mUNVXEptCdo9u8W382uRuohAqLS8Zx74o=,tag:TWr527VfdujQQosWOD31IA==,type:str]
demo-key:
    id: ENC[AES256_GCM,data:zQmdVXEueg==,iv:bhBm6zqLLpvTN9KfQYu14yRC0wLF4AdwGLNUWAulPaE=,tag:Arlv+c5BY2o2N8L+8lzo7A==,type:str]
    secret: ENC[AES256_GCM,data:PSOSLiKZlqkkAwI=,iv:RtdE5Srl3O2YaaloIcejooBKAIGGo+KULXJmN2IvzCA=,tag:eldtU4JnqW+32ypl4fd1Sg==,type:str]
sops:
    kms:
        - arn: arn:aws:kms:ap-northeast-1:123456789012:key/451aaa-bbb-ccc-ddd-eee
          created_at: "2022-04-10T12:23:25Z"
          enc: AQICAHiKFjkZeSZKiMFsjJFkuflNTlnelBlSW/mid67FbkwFjwFsAXdAcTu0K0S7bRG0Dg2mAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQM7Bqu0GqwSLDCvK3TAgEQgDtQjSy4wosF888d3+rpcOR29NEnJiSneNw+U6j/F+a/Qb8Qbiosdvw3t2aBhofRKFX7MpIEGpsB68w6Xw==
          aws_profile: ""
    gcp_kms: []
    azure_kv: []
    hc_vault: []
    age: []
    lastmodified: "2022-04-10T12:23:40Z"
    mac: ENC[AES256_GCM,data:go6I5MzfIjdZQgpPXlGt2Fb1/9n7d8H6JJ/qOiG8xWOTe/QM/s9AHV44ctuGFR9HoH+Lj8BvzpIkXkj87DFpznvxpi3yOd8Vr4q8nVW9kMCITeuI5CxRc/8CjzIggdiceX5CfW6nEB8MxBoTQdkmN5UWePXYdIr+3h3/NK9t6cw=,iv:d7GrCGvKh7tQJZmbNvr6kB8UASvQbcEpoqxEQEGtFac=,tag:pVYIdDYhdRd2jwkix8dscQ==,type:str]
    pgp: []
    unencrypted_suffix: _unencrypted
    version: 3.7.2
$

なお暗号化ファイルを作成するとファイル自体にキー情報が記述されるため、以降そのファイルの編集時にキーの指定は不要となります。

$ sops demo_secrets.yml
## キー指定なしで編集可能
...

環境変数でARNを指定して暗号化

SOPS_KMS_ARNという環境変数でARNを指定します。

$ export SOPS_KMS_ARN='arn:aws:kms:ap-northeast-1:123456789012:key/451aaa-bbb-ccc-ddd-eee'
$ sops demo_secrets.yml
...

.sops.yamlでARNを指定して暗号化

sopsの設定ファイルである .sops.yaml(ファイル名固定。拡張子がymlでもNG)を機密情報ファイルがあるカレントディレクトリや上位ディレクトリに作成します。

$ cat .sops.yaml
creation_rules:
  ## path_regex で暗号化対象ファイルの正規表現を指定
  ## dev_secrets.ymlというファイル名に一致
  - path_regex: ^dev_secrets\.yml$
    kms: 'arn:aws:kms:ap-northeast-1:123456789012:key/451aaa-bbb-ccc-ddd-eee'
  ## prod_secrets.ymlというファイル名に一致
  - path_regex: ^prod_secrets\.yml$
    kms: 'arn:aws:kms:ap-northeast-1:210987654321:key/111aaa-bbb-ccc-ddd-eee'
  ## hoge/任意.yml というファイル名に一致
  - path_regex: hoge/.+\.yml$
    kms: 'arn:aws:kms:ap-northeast-1:123456789012:key/777aaa-bbb-ccc-ddd-eee'
  ## デフォルト(設定しない場合、上述のpath_regexに明示的に一致するファイルがなければエラーとなる)
  - kms: 'arn:aws:kms:ap-northeast-1:123456789012:key/888aaa-bbb-ccc-ddd-eee'
$
$ sops dev_secrets.yml
...
$
$ mkdir hoge
$ cd hoge
$ sops fuga.yml
...
$

チーム内でキー情報を共有する場合は、この方法が一番便利かと思います。

暗号化したファイルと共に.sops.yamlをGitHubなどにコミットすれば良いので、共有ミスのリスクを減らせたり新規メンバー参画時の共有コストが省けます。

既存の平文ファイルの暗号化

-e ( --encrypt ) オプションで既存の平文ファイルを暗号化して標準出力します。ただの表示であるため元のファイルには変更ありません。

$ sops -e plain_text.yml
demo-db:
    user: ENC[AES256_GCM,data:rcZm/3d6JExJ,iv:DoNIoHO4uKXSa8RSm2VNvmw0JRVfN7K+l8hHhMtsSXk=,tag:aXMGYLSalg86IOc/aT+WsA==,type:str]
    pass: ENC[AES256_GCM,data:E32OkSIIZJLa,iv:yp0z7jZBHrzG0qABpLubwUnxVkUKdKr3xBIuqPxfw2s=,tag:uD3pvk7KFdZn3OYMsOELPw==,type:str]
demo-key:
    id: ENC[AES256_GCM,data:GxkCsWEkEQ==,iv:Xdr0Ow6P0zjow7QBCydsR104DNhDCJtrXpPe6ICC2R8=,tag:rMjhQVwd4/o1n4vEJcu0BQ==,type:str]
    secret: ENC[AES256_GCM,data:qqcZ8EP8e5QkC3k=,iv:rAtZiJu6AACqLXCwP/2/eKcaShh0nx61LF3+QI3ZrPo=,tag:ej219ep5GfAk+z0wrC6prA==,type:str]
...
$
## 別ファイルにリダイレクト
$ sops -e plain_text.yml > demo_secrets.yml
$

元のファイルそのものを直接暗号化する場合は -i ( --in-place )オプションを同時に指定します。

$ sops -e -i plain_text.yml
$
$ cat plain_text.yml
demo-db:
    user: ENC[AES256_GCM,data:Q/k0yyH9pkmt,iv:+Qjfl9uSt/bCeAtIFGOV9KauOgPblsB2uh7pngAB/y4=,tag:CZiEl644NoQ1g1VhLuIsfg==,type:str]
    pass: ENC[AES256_GCM,data:8o32yqcAgv2i,iv:kuNNdYPhRGYVD7iGljh/+sjSavgfgyPjYL5m8LREU68=,tag:v5CzG3vYC16XhHOROuSz+g==,type:str]
demo-key:
    id: ENC[AES256_GCM,data:yLdU+0YzXQ==,iv:fBFOpksfx83DLH0h0u00F7BmFIsYSz5Y+SvumbDG7os=,tag:OrfyPfkQYl87bBJGqLneSA==,type:str]
    secret: ENC[AES256_GCM,data:CvmN9gmzEZdFdVg=,iv:8sL+jyRhuMIHItEy4X4ufm1qPdt46w59O7DhOLcts1w=,tag:vLShIP4l8mGersD6zfsgzw==,type:str]
...
$

一部分のみ暗号化

基本的には普通のテキストファイル扱いにはしたいけど、一部の機密情報のみ隠したいなどのケースに利用できそうです。

デフォルトでは _unencrypted のサフィックスがつくJSONキー配下の値が平文となります。

コマンドのオプションで一部分のみ暗号化

–encrypted-regex オプションで指定した正規表現に一致するJSONのキー配下の値のみ暗号化します。

## 平文ファイルの内容
$ cat plain_text.yml
demo-db:
    user: demo-user
    pass: demo-pass
demo-key:
    id: demo-id
    secret: demo-secret
$
$ export SOPS_KMS_ARN=arn:aws:kms:ap-northeast-1:123456789012:key/451aaa-bbb-ccc-ddd-eee
$
## pass と secret のみ暗号化
$ sops -e --encrypted-regex '^(pass|secret)' plain_text.yml
demo-db:
    user: demo-user
    pass: ENC[AES256_GCM,data:W16XtJ9N9CeE,iv:bq9vrLP/ge6tLt26dPCLnIn/UYauQWi3UPtLrh1GtKs=,tag:Ebs1Y/Qkulo8Eugw+55m4A==,type:str]
demo-key:
    id: demo-id
    secret: ENC[AES256_GCM,data:aO9J5z+YQnJV8Y0=,iv:46ieUGSLC7R/iaQO/USSU/Jy+LA3N9oLxM0yWz+qJx0=,tag:ukyxWsR7oqAc6SwYNaVSYw==,type:str]
...
$

逆に --unencrypted-regex オプションで指定した正規表現に一致するJSONのキー配下の値のみ平文のままとして、それ以外を暗号化します。

$ sops -e --unencrypted-regex '^(pass|secret)' plain_text.yml
demo-db:
    user: ENC[AES256_GCM,data:PIEATFAy25vC,iv:gWe4YcxvLI3Zz8PGf91YCgkoHK3UMeB0JGv3WN5pU30=,tag:hDyW+VCVFbpI+ReBDfdlfA==,type:str]
    pass: demo-pass
demo-key:
    id: ENC[AES256_GCM,data:R4q1gN3BmQ==,iv:ZQquyOPXXOvzjfk8QPbPCBzUEPJc9OF9Ye7mMUZWyUU=,tag:eTy5bJEHbdhJpmnCRbX9vg==,type:str]
    secret: demo-secret
...
$

--encrypted-suffix オプションでその文字で終わるJSONのキー配下の値のみ暗号化します。

$ sops -e --encrypted-suffix '-db' plain_text.yml
demo-db:
    user: ENC[AES256_GCM,data:u5q2W/GfgOiI,iv:2+TWP6+US/0JyjiXwHgiB3O7BBr0RtJZ5FoVKkt8szg=,tag:S8QzMY0496nb4WowcumLRw==,type:str]
    pass: ENC[AES256_GCM,data:b/2874O3Ab3l,iv:IVSokoiAYAsRnhRIwQniQwywp20ZkQ7z4iNe6nGD1R0=,tag:zMVKq1wa753tXHM2V9cu4g==,type:str]
demo-key:
    id: demo-id
    secret: demo-secret
...
$

逆に --unencrypted-suffix オプションで指定した正規表現に一致するJSONのキー配下の値のみ平文のままとして、それ以外を暗号化します。

$ sops -e --unencrypted-suffix '-db' plain_text.yml
demo-db:
    user: demo-user
    pass: demo-pass
demo-key:
    id: ENC[AES256_GCM,data:l/Fn4jcQaQ==,iv:rhWek9BIiIhfef1Wj2xm7Us43LFDlSdHWZ14PIlv5y4=,tag:nE7NRKILxQ3AKan8gj3Zrw==,type:str]
    secret: ENC[AES256_GCM,data:8qCnmqMnFB9DxQI=,iv:0O2JydcPqGY0KBX3afL4K6MRADfp2WAFN+7CCxoj4Cw=,tag:soYrUuVAYFMlDzilpi43OA==,type:str]
...
$

.sops.yaml で一部分のみ暗号化

.sops.yaml でもコマンドのオプションと同様の指定ができます。

ただしオプション指定のときは単語をハイフンでつなぐケバブケースでしたが、.sops.yaml にはアンダースコアでつなぐスネークケースで記述します。

$ cat .sops.yaml
creation_rules:
        - kms: arn:aws:kms:ap-northeast-1:123456789012:key/451aaa-bbb-ccc-ddd-eee
          encrypted_regex: ^(pass|secret)
$
$ sops -e plain_text.yml
demo-db:
    user: demo-user
    pass: ENC[AES256_GCM,data:vsS5vv8tXbkh,iv:4o7TS6SWye5ARRVGjaZYzb25NJKYB/hZagk0cQBKwvQ=,tag:ievmXZVe8pVFiM4PDx4TGA==,type:str]
demo-key:
    id: demo-id
    secret: ENC[AES256_GCM,data:8jUL+0zygNZYH14=,iv:mOaZlQ0MU0v4If8MDwCUcoRkpQXRv8m9m3QuFpFUTrw=,tag:tqkCPuJu1AMxd770tD49DQ==,type:str]
sops:
...
    encrypted_regex: ^(pass|secret)
...
$

暗号化ファイルの復号

-d ( --decrypt ) オプションで暗号化済みファイルを平文へ復号して標準出力します。ただの表示であるため元のファイルには変更ありません。

$ sops -d demo_secrets.yml
demo-db:
    user: demo-user
    pass: demo-pass
demo-key:
    id: demo-id
    secret: demo-secret
$
## 別ファイルにリダイレクト
$ sops -d demo_secrets.yml > plain_text.yml
$

元のファイルそのものを直接復号する場合は -i ( –in-place ) オプションを同時に指定します。

$ sops -d -i  demo_secrets.yml
$
## 平文ファイルとなる
$ cat demo_secrets.yml
demo-db:
    user: demo-user
    pass: demo-pass
demo-key:
    id: demo-id
    secret: demo-secret
$

–extract オプションで表示する内容を絞ることができます。

$ sops -d --extract '["demo-db"]' demo_secrets.yml
user: demo-user
pass: demo-pass
$ sops -d --extract '["demo-db"]["user"]' demo_secrets.yml
demo-user
$


KMSキーの更新

KMSキーを別のキーへ変更するやり方も何通りかあります。

.sops.yaml と updatekeys コマンドでキーを更新

.sops.yamlでキーを管理している場合に利用できる方法です。

.sops.yamlに記載してある鍵をエディタで変更した後、updatekeysコマンド でキーを変更します。

## 変更前の鍵
$ cat .sops.yaml
creation_rules:
        - kms: arn:aws:kms:ap-northeast-1:123456789012:key/451aaa-bbb-ccc-ddd-eee
$
## 変更前の鍵で暗号化済みのファイル
$ cat demo_secrets.yml
demo-db:
    user: ENC[AES256_GCM,data:Bd/eX/QToXdv,iv:Zb2lXQLlf1ExpIr4Z+RbdGBEb/2f9Ui6H6hpkjyHvMc=,tag:X08SLNmXVfISKervSNuQTw==,type:str]
...
sops:
    kms:
        - arn: arn:aws:kms:ap-northeast-1:123456789012:key/451aaa-bbb-ccc-ddd-eee
 ...
$
## .sops.yaml のキーをエディタで変更
$ vi .sops.yaml
creation_rules:
        - kms: arn:aws:kms:ap-northeast-1:123456789012:key/338vvv-www-xxx-yyy-zzz
$
## キーを変更。 -y オプションもつけることでyes/no確認がなくなる
$ sops updatekeys demo_secrets.yml
2022/04/09 19:08:47 Syncing keys for file /Users/zoo200/demo_secrets.yml
The following changes will be made to the file's groups:
Group 1
+++ arn:aws:kms:ap-northeast-1:123456789012:key/338vvv-www-xxx-yyy-zzz
--- arn:aws:kms:ap-northeast-1:123456789012:key/451aaa-bbb-ccc-ddd-eee
Is this okay? (y/n):y   ・・・y を入力
2022/04/09 19:08:48 File /Users/zoo200/demo_secrets.yml synced with new keys
$
## 暗号化済みのファイルのキーが更新された
$ cat demo_secrets.yml
demo-db:
    user: ENC[AES256_GCM,data:Bd/eX/QToXdv,iv:Zb2lXQLlf1ExpIr4Z+RbdGBEb/2f9Ui6H6hpkjyHvMc=,tag:X08SLNmXVfISKervSNuQTw==,type:str]
...
sops:
    kms:
        - arn: arn:aws:kms:ap-northeast-1:123456789012:key/338vvv-www-xxx-yyy-zzz
 ...
$

コマンドのオプションでキーを更新

-r ( –rotate )–add-kms オプションでキーの追加を行った後、-r–rm-kms オプションでキーを削除します。

## 暗号化済みのファイルにキーを追加
$ sops -r -i --add-kms 'arn:aws:kms:ap-northeast-1:123456789012:key/338vvv-www-xxx-yyy-zzz' demo_secrets.yml
$
## キーが2つ表示される
$ cat demo_secrets.yml
...
sops:
    kms:
        - arn: arn:aws:kms:ap-northeast-1:123456789012:key/451aaa-bbb-ccc-ddd-eee
...
        - arn: arn:aws:kms:ap-northeast-1:123456789012:key/338vvv-www-xxx-yyy-zzz
...
$
## 古い方のキーを削除
$ sops -r -i --rm-kms 'arn:aws:kms:ap-northeast-1:123456789012:key/451aaa-bbb-ccc-ddd-eee' demo_secrets.yml
$
## キーが新しい方の1つのみ表示される
$ cat demo_secrets.yml
...
sops:
    kms:
        - arn: arn:aws:kms:ap-northeast-1:123456789012:key/338vvv-www-xxx-yyy-zzz
...
$

なおこちらのREADMEには -s -i オプションを指定することで直接キーを更新することもできると記述があるのですが、なぜか僕の環境では更新できませんでした。

KMSキーを複数指定する(OR条件)

公式READMEでは異なるリージョンの2つのKMSキーを使用することが推奨されています。理由は特に記述されていませんが、AWS上でKMSキーを誤って削除した場合に備えてかと思われます。

以下のようにカンマ区切りでキーを複数指定することによって、どちらのキーでも暗号化と復号ができるようになります。

$ export SOPS_KMS_ARN='arn:aws:kms:ap-northeast-1:123456789012:key/451aaa-bbb-ccc-ddd-eee,arn:aws:kms:ap-northeast-1:123456789012:key/338vvv-www-xxx-yyy-zzz'
$
$
$ cat demo_secrets.yml
...
sops:
    kms:
        - arn: arn:aws:kms:ap-northeast-1:123456789012:key/451aaa-bbb-ccc-ddd-eee
...
        - arn: arn:aws:kms:ap-northeast-1:123456789012:key/338vvv-www-xxx-yyy-zzz
...
$

前述の–add-kmsオプションでも同様にキーの追加が可能です。

2つのキーが両方使用できなくなると以下のようなエラーとなります。

$ sops demo_secrets.yml
Failed to get the data key required to decrypt the SOPS file.

Group 0: FAILED
  arn:aws:kms:ap-northeast-1:123456789012:key/451aaa-bbb-ccc-ddd-eee: FAILED
    - | Error decrypting key: DisabledException:
      | arn:aws:kms:ap-northeast-1:123456789012:key/451aaa-bbb-ccc-ddd-eee
      | is disabled.

  arn:aws:kms:ap-northeast-1:123456789012:key/338vvv-www-xxx-yyy-zzz: FAILED
    - | Error decrypting key: DisabledException:
      | arn:aws:kms:ap-northeast-1:123456789012:key/338vvv-www-xxx-yyy-zzz
      | is disabled.

Recovery failed because no master key was able to decrypt the file. In
order for SOPS to recover the file, at least one key has to be successful,
but none were.
$

KMSキーを複数指定する(AND条件)

複数のキーがどちらかあれば良いOR条件とは逆に、複数キーを設定してどちらもなければならないAND条件の設定も可能です。

キーグループというものを設定するのですが、このやり方も何通りかあります。

.sops.yamlでキーグループを設定

.sops.yamlの key_groups でグループ化するキーを指定します。

キーグループはグループ自体を複数作成できたり、必要なキーグループの閾値を指定するなど複雑な設定も可能です。単順に1つのキーグループを設定する場合は以下のように記述します。

## .sops.yaml で key_groups を設定
$ vi .sops.yaml
creation_rules:
    - key_groups:
          - kms:
              - arn: 'arn:aws:kms:ap-northeast-1:123456789012:key/451aaa-bbb-ccc-ddd-eee'
          - kms:
              - arn: 'arn:aws:kms:ap-northeast-1:123456789012:key/338vvv-www-xxx-yyy-zzz'
$
## 暗号化ファイル作成
$ sops demo_secrets.yml
$
## 2つのキーで暗号化された
$ cat demo_secrets.yml
...
sops:
    shamir_threshold: 2
    key_groups:
        - kms:
            - arn: arn:aws:kms:ap-northeast-1:123456789012:key/451aaa-bbb-ccc-ddd-eee
...
        - kms:
            - arn: arn:aws:kms:ap-northeast-1:123456789012:key/338vvv-www-xxx-yyy-zzz
$
## キーを1つでも無効化すると以下のようにエラーとなる
$ sops demo_secrets.yml
Failed to get the data key required to decrypt the SOPS file.

Group 0: FAILED
  arn:aws:kms:ap-northeast-1:123456789012:key/451aaa-bbb-ccc-ddd-eee: FAILED
    - | Error decrypting key: DisabledException:
      | arn:aws:kms:ap-northeast-1:123456789012:key/451aaa-bbb-ccc-ddd-eee
      | is disabled.

Group 1: SUCCESS

Recovery failed because the file was encrypted with a Shamir threshold of
2, but only 1 part(s) were successfully recovered, one for each successful
key group. In order for SOPS to recover the file, at least 2 groups have to
be successful. In order for a group to be successful, decryption has to
succeed with any of the keys in that key group.
$

groups add コマンドでキーグループを設定

暗号化済みのファイルに --groups add オプションでキーを追加してキーグループを設定します。

## まず1つのキーで暗号化
$ sops -k arn:aws:kms:ap-northeast-1:123456789012:key/451aaa-bbb-ccc-ddd-eee demo_secrets.yml
$
$ cat demo_secrets.yml
...
sops:
    kms:
        - arn: arn:aws:kms:ap-northeast-1:123456789012:key/451aaa-bbb-ccc-ddd-eee
...
$
## 2つ目のキーを追加
$ sops groups add -i --file demo_secrets.yml --kms 'arn:aws:kms:ap-northeast-1:123456789012:key/338vvv-www-xxx-yyy-zzz'
$
## key_groupsが作成された
$ cat demo_secrets.yml
...
sops:
    shamir_threshold: 2
    key_groups:
        - kms:
            - arn: arn:aws:kms:ap-northeast-1:123456789012:key/451aaa-bbb-ccc-ddd-eee
...
        - kms:
            - arn: arn:aws:kms:ap-northeast-1:123456789012:key/338vvv-www-xxx-yyy-zzz
...
$


git diffで平文の差分表示

Gitで管理されている暗号化済みのファイルを修正後に git diff コマンドで差分表示すると、当然ですが以下のように暗号文のまま差分が表示されます。

$ git diff demo_secrets.yml
diff --git a/demo_secrets.yml b/demo_secrets.yml
index 6e82466..7453932 100644
--- a/demo_secrets.yml
+++ b/demo_secrets.yml
@@ -1,6 +1,6 @@
 demo-db:
-    user: ENC[AES256_GCM,data:2SKxX/Zn/VJ+,iv:7Mfy9Vc/6xFb3Qg4oRJs37OSrXSGylQMvShQ97KBK0Q=,tag:pWwhli0W1Xh9hV54Nf34cQ==,type:str]
-    pass: ENC[AES256_GCM,data:NX01md6bHx4d,iv:pGSAeCtssa27Ce1/c6Bn8+bP+T8CjJOYyqYsJiPzvFw=,tag:pwEf9HNWe4rEJ9wqSysWeg==,type:str]
+    user: ENC[AES256_GCM,data:TCvVFnGG/aQUKbs=,iv:Mo8KsH/8l8N3P7UIfelqq5wsQcYvQ+5u3Uqsko+GTPU=,tag:J7jrdpl9jL8UQW7pG/c1iQ==,type:str]
+    pass: ENC[AES256_GCM,data:Mxk5pKSZmH7II8Y=,iv:9Hjz0SoRMOYe90YfoUsjYzG1rpI1QWx/PufY5jI3WZs=,tag:Ba3vTc0ffbD7bJ2eN9UWww==,type:str]
 demo-key:
     id: ENC[AES256_GCM,data:9J1YBHHeTQ==,iv:NEYQcwjhZnfhwD5KTjGxuD1LWdJuxtBgs6sPn6hgKAI=,tag:a6RQ7wSWVgAm6KBuQ2KSVg==,type:str]
     secret: ENC[AES256_GCM,data:2EF3GZGREMxpc/Y=,iv:a2VixMnESL2FRCI9Wa0TQINsBOZsiJvGrnzcfy+8dL0=,tag:pa1uHmOtn4F8am6fCgwXlA==,type:str]
@@ -14,8 +14,8 @@ sops:
     azure_kv: []
     hc_vault: []
     age: []
-    lastmodified: "2022-04-14T12:40:28Z"
-    mac: ENC[AES256_GCM,data:Xdr2F5syCE6oHYSTsh5yuMsUfntNBSvloY7EpNnLwa4d045W/9jlH1CT106qKYeTibrzSmYiPh9E85Teu4KzEdZHeEC28PblS2DhywXTvcv/N+94XKlASdCWHkAqx0yLLLKqnj4tOQ2SM4caI1OJyiKRUqImJJG7q8D4IGmQy3A=,iv:ljQWpR4YG3gF38bt3hOn97ih8zmHvZpHAB3BbVV4Uk0=,tag:nwm90PT9GNM1jwkaCSrMMA==,type:str]
+    lastmodified: "2022-04-14T12:49:49Z"
+    mac: ENC[AES256_GCM,data:WLtpJ+pRDUyJQJg5FvOLXDrSipc0nXtW89XRDVX8F6CwaFyG+ICTsFcwmTzpUCuxoD5am5sfLQzL6ifGg+BFrROwHbewqpNvOKfx4xw2TIoia0hudu5O0eX8w4ODfwQGntso7tEEfWD7/GDtgVLy9SvGLYJEOmgynlcex0uR2UA=,iv:muAkgnwH1pW5SFGhPgUEB2y+3VoYnLMEoDl+llMY0L0=,tag:t0AZG1aW+10+5nDA5rjJOg==,type:str]
     pgp: []
     unencrypted_suffix: _unencrypted
     version: 3.7.2
$

差分を復号した状態で表示させたい場合は、リポジトリのルートディレクトリにある .gitattributes.git/config を以下のとおり設定します。

## 復号対象のファイルパターン diff=sopsdiffer の設定を追記
$ vi .gitattributes
...
*.yml diff=sopsdiffer
$
## .git/config に設定を追加
$ git config diff.sopsdiffer.textconv "sops -d"
$ cat .git/config
...
[diff "sopsdiffer"]
        textconv = sops -d
$
## 平文で差分表示
$ git diff demo_secrets.yml
diff --git a/demo_secrets.yml b/demo_secrets.yml
index 6e82466..7453932 100644
--- a/demo_secrets.yml
+++ b/demo_secrets.yml
@@ -1,6 +1,6 @@
 demo-db:
-    user: demo-user
-    pass: demo-pass
+    user: demo-user-2
+    pass: demo-pass-2
 demo-key:
     id: demo-id
     secret: demo-secret
$

Tips

AWS Vaultを通してsopsを使用

AWS Vaultで2段階認証(MFA)制限のスイッチロールを行いつつ、sopsを使用できます。

$ aws-vault exec zoo200 -- sops -e -k 'arn:aws:kms:ap-northeast-1:123456789012:key/451aaa-bbb-ccc-ddd-eee' plain_text.yml
Enter token for arn:aws:iam::210987654321:mfa/zoo200: 123456  -> MFAコードを入力
demo-db:
    user: ENC[AES256_GCM,data:JDcZ6WYuNTcJ,iv:fxa6mY5biYCAGiJILCd/CmWNBrDY1IwEBApeuobWSS4=,tag:qnJ4aNQleCVtWSwA9guthA==,type:str]
    pass: ENC[AES256_GCM,data:LEIE4kNliSW7,iv:/euWixL/9UN4J51CjsJsA2//Jf6fNFobTCIj09UqQzQ=,tag:CmEOxln9CkSSSj/BkZpzYQ==,type:str]
...
$

Terraform, Terragruntでsopsを使用

TerraformやTerraformのラッパーツールであるTerragurntでsopsを利用できます。

Terraformはこちらのプロバイダを使用して、Terragruntはsops_decrypt_fileという組み込み関数でsopsを利用できます。

実際にTerragrunt で利用する場合は以下の記事を参考にしてみてください。

今回は以上です〜ノシ

参考

アリガト━━━ヾ(´∀`)ノ━━━━♪

Github mozilla/sops
Terraform の秘匿情報を mozilla/sops で管理する