AWS Secrets ManagerでEC2キーペア秘密鍵の安全な受け渡しを管理してみる
みなさん、こんにちは! AWS事業本部の青柳@福岡オフィスです。
チームやプロジェクトで作業をする際、EC2の「キーペア」のプライベートキー (秘密鍵) をどのように管理していますか?
まず、最初にキーペアを作成した人が、秘密鍵を入手してローカルディスクに保存します。 そこからが問題です。 チーム/プロジェクトの複数のメンバーも秘密鍵を使う必要がある場合、秘密鍵ファイルをどうやって共有 (あるいは受け渡し) するのかが悩みどころです。
- 社内ファイルサーバーの共有フォルダーに置く (フォルダーのアクセス権をしっかり管理しなければ・・・)
- メールでやり取りする (社内と言えども平文は心配・・・添付ファイルの暗号化/復号は手間がかかる・・・)
社内だけでなく社外の取引先や協力会社とプロジェクトを進めている場合などは、なおさらセキュリティに気を払う必要が出てきます。
そこで、AWSで秘匿情報を取り扱うためのサービスとして用意されている AWS Secrets Manager を使って、EC2キーペア秘密鍵の安全な受け渡しを管理する方法を検討してみました。
Secrets Managerマネジメントコンソールを使って試してみる
PEM形式の秘密鍵ファイルは、Base64でエンコードされたテキストファイルです。
-----BEGIN RSA PRIVATE KEY----- ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqestuvwxyz0123456789+/ABCDEFGHIJKL MNOPQRSTUVWXYZabcdefghijklmnopqestuvwxyz0123456789+/ABCDEFGHIJKLMNOPQRSTUVWX YZabcdefghijklmnopqestuvwxyz0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghij klmnopqestuvwxyz0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqestuv wxyz0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqestuvwxyz01234567 89+/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqestuvwxyz0123456789+/ABCDEFGH IJKLMNOPQRSTUVWXYZabcdefghijklmnopqestuvwxyz0123456789+/ABCDEFGHIJKLMNOPQRST UVWXYZabcdefghijklmnopqestuvwxyz0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef ghijklmnopqestuvwxyz0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqe stuvwxyz0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqestuvwxyz0123 456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqestuvwxyz0123456789+/ABCD EFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqestuvwxyz0123456789+/ABCDEFGHIJKLMNOP QRSTUVWXYZabcdefghijklmnopqestuvwxyz0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZab cdefghijklmnopqestuvwxyz0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmn opqestuvwxyz0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqestuvwxyz 0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqestuvwxyz0123456789+/ ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqestuvwxyz0123456789+/ABCDEFGHIJKL MNOPQRSTUVWXYZabcdefghijklmnopqestuvwxyz0123456789+/ABCDEFGHIJKLMNOPQRSTUVWX YZabcdefghijklmnopqestuvwxyz0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghij klmnopqestuvwxyz0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqestuv wxyz0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqestuvwxyz01== -----END RSA PRIVATE KEY-----
(これはサンプルのダミーファイルです)
Secrets Managerで新しいシークレットを作成する際に、シークレットの内容として秘密鍵ファイルの内容をまるごとコピーペーストしてみます。
作成したシークレットを開き、値を取得してみます。
一見うまく行っているようですが・・・
改行コードがスペースに置き換えられていて、ここからコピーペーストして新しいPEMファイルを作ろうとしても上手く行きませんでした。
そもそも、画面上に秘密鍵の内容を表示して作業するというのは、精神衛生上よくありません・・・
秘密鍵をバイナリデータとしてSecrets Managerに登録する
何か良い手はないものか・・・ と考えたところ、Secrets Managerは、テキストデータだけでなくバイナリデータもシークレットとして登録できることを思い出しました。
Secrets Managerへバイナリデータを登録する方法については、弊社コンサルティング部の八幡がブログを執筆していましたので、参考にしました。
AWS Secrets Managerでバイナリデータを管理する | Developers.IO
秘密鍵をSecrets Managerに登録する
バイナリデータの登録・参照・取り出しは、AWSマネジメントコンソールではサポートされておらず、AWS CLIやSDKを使う必要があります。
今回はAWS CLIを使いました。
aws secretsmanager create-secret
コマンドの引数に直接ファイルパスを指定できます。
$ KEYPAIR_NAME=example-keypair $ aws secretsmanager create-secret \ --name ${KEYPAIR_NAME} \ --secret-binary file://~/.ssh/${KEYPAIR_NAME}.pem { "ARN": "arn:aws:secretsmanager:ap-northeast-1:123456789012:secret:example-keypair-XXXXXX", "Name": "example-keypair", "VersionId": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" }
どのようにシークレットに格納されたのか確認してみます。
$ aws secretsmanager get-secret-value \ --secret-id ${KEYPAIR_NAME} { "ARN": "arn:aws:secretsmanager:ap-northeast-1:123456789012:secret:example-keypair-XXXXXX", "Name": "example-keypair", "VersionId": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", "SecretBinary": "LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWmFiY2RlZmdoaWprbG1ub3BxZXN0dXZ3eHl6MDEyMzQ1Njc4OSsvQUJDREVGR0hJSktMCk1OT1BRUlNUVVZXWFlaYWJjZGVmZ2hpamtsbW5vcHFlc3R1dnd4eXowMTIzNDU2Nzg5Ky9BQkNERUZHSElKS0xNTk9QUVJTVFVWV1gKWVphYmNkZWZnaGlqa2xtbm9wcWVzdHV2d3h5ejAxMjM0NTY3ODkrL0FCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaYWJjZGVmZ2hpagprbG1ub3BxZXN0dXZ3eHl6MDEyMzQ1Njc4OSsvQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVphYmNkZWZnaGlqa2xtbm9wcWVzdHV2Cnd4eXowMTIzNDU2Nzg5Ky9BQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWmFiY2RlZmdoaWprbG1ub3BxZXN0dXZ3eHl6MDEyMzQ1NjcKODkrL0FCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaYWJjZGVmZ2hpamtsbW5vcHFlc3R1dnd4eXowMTIzNDU2Nzg5Ky9BQkNERUZHSApJSktMTU5PUFFSU1RVVldYWVphYmNkZWZnaGlqa2xtbm9wcWVzdHV2d3h5ejAxMjM0NTY3ODkrL0FCQ0RFRkdISUpLTE1OT1BRUlNUClVWV1hZWmFiY2RlZmdoaWprbG1ub3BxZXN0dXZ3eHl6MDEyMzQ1Njc4OSsvQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVphYmNkZWYKZ2hpamtsbW5vcHFlc3R1dnd4eXowMTIzNDU2Nzg5Ky9BQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWmFiY2RlZmdoaWprbG1ub3BxZQpzdHV2d3h5ejAxMjM0NTY3ODkrL0FCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaYWJjZGVmZ2hpamtsbW5vcHFlc3R1dnd4eXowMTIzCjQ1Njc4OSsvQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVphYmNkZWZnaGlqa2xtbm9wcWVzdHV2d3h5ejAxMjM0NTY3ODkrL0FCQ0QKRUZHSElKS0xNTk9QUVJTVFVWV1hZWmFiY2RlZmdoaWprbG1ub3BxZXN0dXZ3eHl6MDEyMzQ1Njc4OSsvQUJDREVGR0hJSktMTU5PUApRUlNUVVZXWFlaYWJjZGVmZ2hpamtsbW5vcHFlc3R1dnd4eXowMTIzNDU2Nzg5Ky9BQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWmFiCmNkZWZnaGlqa2xtbm9wcWVzdHV2d3h5ejAxMjM0NTY3ODkrL0FCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaYWJjZGVmZ2hpamtsbW4Kb3BxZXN0dXZ3eHl6MDEyMzQ1Njc4OSsvQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVphYmNkZWZnaGlqa2xtbm9wcWVzdHV2d3h5egowMTIzNDU2Nzg5Ky9BQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWmFiY2RlZmdoaWprbG1ub3BxZXN0dXZ3eHl6MDEyMzQ1Njc4OSsvCkFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaYWJjZGVmZ2hpamtsbW5vcHFlc3R1dnd4eXowMTIzNDU2Nzg5Ky9BQkNERUZHSElKS0wKTU5PUFFSU1RVVldYWVphYmNkZWZnaGlqa2xtbm9wcWVzdHV2d3h5ejAxMjM0NTY3ODkrL0FCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWApZWmFiY2RlZmdoaWprbG1ub3BxZXN0dXZ3eHl6MDEyMzQ1Njc4OSsvQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVphYmNkZWZnaGlqCmtsbW5vcHFlc3R1dnd4eXowMTIzNDU2Nzg5Ky9BQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWmFiY2RlZmdoaWprbG1ub3BxZXN0dXYKd3h5ejAxMjM0NTY3ODkrL0FCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaYWJjZGVmZ2hpamtsbW5vcHFlc3R1dnd4eXowMT09Ci0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0t", "VersionStages": [ "AWSCURRENT" ], "CreatedDate": 1582595237.313 }
SecretBinary
に、秘密鍵ファイルの内容がBase64でエンコードされた文字列が格納されています。
(元々Base64でエンコードされているテキストファイルを更にBase64でエンコードするというのも変な感じですが・・・ 改行を含むテキストをSecrets Managerに格納するにはこの方法が確実だと思います)
Secrets Managerから秘密鍵を取り出してファイルに保存する
ここからは、Secrets Managerに登録された秘密鍵を、別のユーザーが取り出すというシチュエーションで進めます。
さきほど説明した通り、Secrets Managerに格納されたバイナリデータはBase64でエンコードされています。
したがって、ファイルとして取り出す場合には base64
コマンドでデコードすれば良いということになります。
$ KEYPAIR_NAME=example-keypair $ aws secretsmanager get-secret-value \ --secret-id ${KEYPAIR_NAME} \ --query 'SecretBinary' \ --output text \ | base64 -d > ${KEYPAIR_NAME}.pem
ファイルの中身を確認してみましょう。
$ cat example-keypair.pem -----BEGIN RSA PRIVATE KEY----- ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqestuvwxyz0123456789+/ABCDEFGHIJKL MNOPQRSTUVWXYZabcdefghijklmnopqestuvwxyz0123456789+/ABCDEFGHIJKLMNOPQRSTUVWX YZabcdefghijklmnopqestuvwxyz0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghij klmnopqestuvwxyz0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqestuv wxyz0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqestuvwxyz01234567 89+/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqestuvwxyz0123456789+/ABCDEFGH IJKLMNOPQRSTUVWXYZabcdefghijklmnopqestuvwxyz0123456789+/ABCDEFGHIJKLMNOPQRST UVWXYZabcdefghijklmnopqestuvwxyz0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef ghijklmnopqestuvwxyz0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqe stuvwxyz0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqestuvwxyz0123 456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqestuvwxyz0123456789+/ABCD EFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqestuvwxyz0123456789+/ABCDEFGHIJKLMNOP QRSTUVWXYZabcdefghijklmnopqestuvwxyz0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZab cdefghijklmnopqestuvwxyz0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmn opqestuvwxyz0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqestuvwxyz 0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqestuvwxyz0123456789+/ ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqestuvwxyz0123456789+/ABCDEFGHIJKL MNOPQRSTUVWXYZabcdefghijklmnopqestuvwxyz0123456789+/ABCDEFGHIJKLMNOPQRSTUVWX YZabcdefghijklmnopqestuvwxyz0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghij klmnopqestuvwxyz0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqestuv wxyz0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqestuvwxyz01== -----END RSA PRIVATE KEY-----
登録した秘密鍵ファイルの内容と同じですね。
これで、Secrets Managerを介して別のユーザーへ秘密鍵を受け渡すことができました。
今回紹介したAWS CLIのコマンドラインをスニペット化やbashスクリプト化することで、Secrets Managerの操作に慣れない開発者やオペレーター等でも秘密鍵の受け渡しの操作が行えるようになるかと思います。
Secrets Managerのアクセス制御
Secrets Managerに登録した秘密鍵ですが、誰でもアクセスできる状態では当然問題があるため、適切にアクセス制御を行う必要があります。
Secrets Managerにおけるアクセス制御の方法には、次の2通りがあります。
- アイデンティティベースのアクセス制御
- リソースベースのアクセス制御
アイデンティティベースのアクセス制御
IAMユーザー、IAMグループ、IAMロール等に対してIAMポリシーを適用することによりアクセス権限を付与する方法です。
IAMポリシーは以下のように定義されます。
{ "Version": "2012-10-17", "Statement": { "Effect": "Allow", "Action": [ "secretsmanager:Describe*", "secretsmanager:Get*", "secretsmanager:List*" ], "Resource": "arn:aws:secretsmanager:ap-northeast-1:123456789012:secret:project-a/*" } }
Action
には、Secrets Managerのシークレットに対する「参照」「値の取り出し」の権限を指定します。
Resource
には、アクセス権限を与える対象シークレットをARNで指定します。
Secrets Managerのシークレットの名前は「xxxxxx/xxxxxx」のように「/」で区切ることで疑似的な階層構造を取ることができます。 この場合、「project-a/*」というようにワイルドカード指定をすることで、「project-a/」で始まるシークレットに対して一律でアクセス権限を設定することもできます。
ポリシーJSONファイルからIAMポリシーを作成します。
$ aws iam create-policy \ --policy-name secrets-manager-access-policy \ --policy-document file://identity-based-policy.json
作成したIAMポリシーを、IAMグループ「project-a-group」にアタッチします。
$ aws iam attach-group-policy \ --group-name project-a-group \ --policy-arn arn:aws:iam::123456789012:policy/secrets-manager-access-policy
これにより、IAMグループ「project-a-group」に対して、「project-a/」の階層にある全てのシークレットへのアクセス許可を与えることができます。
リソースベースのアクセス制御
リソース、つまり「シークレット」に対してアクセス権限を記述したポリシーを適用することで、アクセス制御を行う方法です。
ポリシーは以下のように記述します。
{ "Version": "2012-10-17", "Statement": { "Effect": "Allow", "Action": [ "secretsmanager:Describe*", "secretsmanager:Get*", "secretsmanager:List*" ], "Principal": {"AWS": "arn:aws:iam::123456789012:user/testuser1"}, "Resource": "*" } }
Principal
で、アクセス権限を与える対象のプリンシパル (IAMユーザー等) を指定します。
注意点として、リソースベースのポリシーではプリンシパルにIAMグループを指定することはできません。
リソースに対して設定するポリシーですので Resource
の指定は無意味となりますが、Resource
エントリーを省略することはできません。
固定で「*」を指定する必要があります。
作成したポリシーをシークレットに適用するには、aws secretsmanager put-resource-policy
コマンドを使用します。
$ aws secretsmanager put-resource-policy \ --secret-id project-a/keypair-1 \ --resource-policy file://resource-based-policy.json
これにより、IAMユーザー「testuser1」に対して、シークレット「project-a/keypair-1」へのアクセス許可を与えることができます。
リソースベースのアクセス制御は、シークレットの管理担当者がIAMに対する管理権限を持っていない場合に、Secrets Manager側でアクセス制御を行うことができるという利点があります。
その代わり、アイデンティティベースのアクセス制御と比べると「IAMグループに対してアクセス制御を行うことができない」「シークレット一つ一つに対してアクセス制御を行う必要がある」という制約があります。
おわりに
Secrets Managerを使用してキーペアの秘密鍵を安全に管理する方法について紹介しました。
より安全性を高めるために、いくつかの方法が考えられます。 例えば、今回はシークレットの暗号化に「デフォルトのサービス暗号化キー」を使いましたが、KMSのカスタマー管理キーを使うことで、より安全なシークレット管理の運用ができるかもしれません。
また、今回はAWS CLIを使って一連の操作を行いましたが、秘密鍵の利用者がWindowsユーザーの場合はどうすればよいのか? といったことも考える余地があると思います。