ENGINEER BLOG ENGINEER BLOG
  • 公開日
  • 最終更新日

【CloudFormation】Secrets Managerでパスワードを作成・管理する

この記事を共有する

はじめに

パーソル&サーバーワークス嶋田です。

AWSにはSecrets Managerと呼ばれるサービスがあり、パスワードなどの秘匿情報を管理する目的で使われます。

Secrets Managerで管理するパスワードは、自動・手動の両方で設定ができます。

本ブログでは、Secrets Managerを使ったパスワードを、以下のパターンで設定してみたいと思います。

  • 特定の文字を含まないで自動生成パスワードを設定
  • 一定の文字数量で自動生成パスワードを設定
  • 手動でのパスワードを設定

なお、本ブログで紹介する自動生成パターンは1例であり、要件に応じて柔軟なカスタマイズができます。

本ブログで用いるテンプレートの基本形

自動設定と手動設定の基本形を簡単に説明します。
以下の2種類のテンプレートをベースに本ブログは進行します。

自動設定

AWSTemplateFormatVersion: '2010-09-09'
Description: Secrets Manager Tutorial
Resources:
  MySecret1:
    Type: AWS::SecretsManager::Secret
    Properties:
      Description: generate password
      GenerateSecretString:
        # パスワードと併せてSecrets Managerで保管する値をJSON形式で記述
        SecretStringTemplate: |
          {}
        # 自動生成される文字列を格納するキー名
        GenerateStringKey: "password"
        #### ここから↓に、以降のセクションでパスワード自動生成時に指定可能な条件などを書いていきます ####
        # 生成される文字列の長さ
        PasswordLength: 20
        # 生成される文字列から除外する文字
        ExcludeCharacters: abcdefghijklmnopqrstuvwz
        # 小文字を除外するか
        ExcludeLowercase: false
        # 数字を除外するか
        ExcludeNumbers: false
        # 【! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \ ] ^ _ ` { | } ~】 を除外するか 
        ExcludePunctuation: false
        # 大文字を除外するか
        ExcludeUppercase: false
        # スペースを生成文字列に含めるか
        IncludeSpace: false
        # 各文字種(大文字、小文字、数字、句読点など)を必ず1文字ずつ含むか
        RequireEachIncludedType: false
  • SecretStringTemplate にSecrets Managerで管理する値を記述します
  • GenerateSecretString の配下に、パスワードを自動生成するときの設定値を書きます

手動設定

AWSTemplateFormatVersion: '2010-09-09'
Description: Secrets Manager Tutorial
Resources:
  MySecret2:
    Type: AWS::SecretsManager::Secret
    Properties:
      Description: manually password
      SecretString: 'password'
  • SecretString にSecrets Managerで管理する値を記述します

特定の文字を含まないで自動生成パスワードを設定

以下のテンプレートファイルを用意します。

AWSTemplateFormatVersion: '2010-09-09'
Description: Secrets Manager Tutorial
Resources:
  MySecret1:
    Type: AWS::SecretsManager::Secret
    Properties:
      Description: generate password
      GenerateSecretString:
        # パスワードと併せてSecrets Managerで保管する値をJSON形式で記述
        SecretStringTemplate: |
          {}
        # 自動生成される文字列を格納するキー名
        GenerateStringKey: "password"
        # 生成される文字列から除外する文字
        ExcludeCharacters: opqrstuvwxyz
        # 小文字を除外するか
        ExcludeLowercase: false
        # 数字を除外するか
        ExcludeNumbers: true
        # 【! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \ ] ^ _ ` { | } ~】 を除外するか 
        ExcludePunctuation: true
        # 大文字を除外するか
        ExcludeUppercase: true
        # スペースを生成文字列に含めるか
        IncludeSpace: false
        # 各文字種(大文字、小文字、数字、句読点など)を必ず1文字ずつ含むか
        RequireEachIncludedType: false
Outputs:
  SecretArn1:
    Value: !Ref MySecret1

本ブログでは、テンプレートファイル名をsecrets-manager.yamlとします。

準備ができたら、AWS CLIコマンドでデプロイします。

$ aws cloudformation deploy --stack-name SecretStack --template-file secrets-manager.yaml
Waiting for changeset to be created..
Waiting for stack create/update to complete
Successfully created/updated stack - SecretStack

デプロイが完了しているか確認します。

$ aws cloudformation describe-stacks --stack-name SecretStack --query "Stacks[].StackStatus" --output text
CREATE_COMPLETE

CREATE_FAILED になっている場合は、以下のコマンドでイベントを確認して、トラブルシューティングが必要です。

$ aws cloudformation describe-stack-events --stack-name SecretStack

※本ブログではトラブルシューティングの説明は割愛いたします
※イベント履歴の確認は、CloudFormationのマネジメントコンソールでも可能です

CREATE_COMPLETE と表示されていることを確認したら、以下のコマンドを実行します。

$ aws cloudformation describe-stacks --stack-name SecretStack --query "Stacks[0].Outputs[?contains(OutputKey, 'SecretArn1')].OutputValue" --output text
arn:aws:secretsmanager:ap-northeast-1:012345678910:secret:MySecret1-STxw8hu524Q5-1bzKXq

SecretArn1 の値は、テンプレート内のOutputsセクションに記述した値に合わせます。
取得したARNを元手に、Secrets Managerサービスを確認してみましょう。
以下のコマンドを実行します。

$ aws secretsmanager get-secret-value --secret-id arn:aws:secretsmanager:ap-northeast-1:012345678910:secret:MySecret1-STxw8hu524Q5-1bzKXq --query "SecretString" --output text
{"password":"fjmfjjfbfcnjdddkgncbbimidhdlnagk"}

パスワードの値が確認できました。
テンプレートファイルに記述した設定に則って自動生成されていますね。
念のため、どんな条件で自動生成される想定だったか、おさらいします。

  • 特定の小文字(opqrstuvwxyz)を除いた文字列で生成
  • 数字を除外
  • 記号(! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \ ] ^ _ ` { | } ~)を除外
  • 大文字を除外

一定の文字数量で自動生成パスワードを設定

特定の文字を含まないで自動生成パスワードを設定 セクションで使ったテンプレートに、
PasswordLength を設定して、文字数を制御してみます。
以下のテンプレートは、設定反映後のコードです。

AWSTemplateFormatVersion: '2010-09-09'
Description: Secrets Manager Tutorial
Resources:
  MySecret1:
    Type: AWS::SecretsManager::Secret
    Properties:
      Description: generate password
      GenerateSecretString:
        # パスワードと併せてSecrets Managerで保管する値をJSON形式で記述
        SecretStringTemplate: |
          {}
        # 自動生成される文字列を格納するキー名
        GenerateStringKey: "password"
        # 生成される文字列の長さ
        PasswordLength: 8
        # 生成される文字列から除外する文字
        ExcludeCharacters: opqrstuvwxyz
        # 小文字を除外するか
        ExcludeLowercase: false
        # 数字を除外するか
        ExcludeNumbers: true
        # 【! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \ ] ^ _ ` { | } ~】 を除外するか 
        ExcludePunctuation: true
        # 大文字を除外するか
        ExcludeUppercase: true
        # スペースを生成文字列に含めるか
        IncludeSpace: false
        # 各文字種(大文字、小文字、数字、句読点など)を必ず1文字ずつ含むか
        RequireEachIncludedType: false
Outputs:
  SecretArn1:
    Value: !Ref MySecret1

上記の通り編集を終えたら、改めてデプロイを実施します。

$ aws cloudformation deploy --stack-name SecretStack --template-file secrets-manager.yaml
Waiting for changeset to be created..
Waiting for stack create/update to complete
Successfully created/updated stack - SecretStack

デプロイが成功したようなので、スタックの状態を確認してみましょう。

$ aws cloudformation describe-stacks --stack-name SecretStack --query "Stacks[].StackStatus" --output text
UPDATE_COMPLETE

UPDATE_COMPLETE と表示されていれば成功です。

改めてSecrets Managerの値を確認してみましょう。
Secrets Managerの値確認には、get-secret-valueコマンドを使います。

$ aws secretsmanager get-secret-value --secret-id arn:aws:secretsmanager:ap-northeast-1:012345678910:secret:MySecret1-STxw8hu524Q5-1bzKXq --query "SecretString" --output text
{"password":"ckmdbieh"}

8文字になっていますね。

ここまでまとめ

ここまでで、 GenerateSecretString の設定を使うことで、
自動生成されるパスワードが制御できることを体験しました。

一旦、作成したリソースを削除したいと思います。
以下のコマンドを実行します。

$ aws cloudformation delete-stack --stack-name SecretStack

リソースが消えているか念のため確認します。

$ aws secretsmanager get-secret-value --secret-id arn:aws:secretsmanager:ap-northeast-1:012345678910:secret:MySecret1-STxw8hu524Q5-1bzKXq --query "SecretString" --output text
An error occurred (ResourceNotFoundException) when calling the GetSecretValue operation: Secrets Manager can't find the specified secret.
$ aws cloudformation describe-stacks --stack-name SecretStack --query "Stacks[].StackStatus" --output text
An error occurred (ValidationError) when calling the DescribeStacks operation: Stack with id SecretStack does not exist

上記のように、 ResourceNotFoundException / SecretStack does not exist と表示されていればOKです。

最後に、自動生成ではなく手動でパスワードを設定する方法について触れて締めたいと思います。

手動でのパスワードを設定

以下のテンプレートファイルを用意します。
※シークレットの情報をテンプレートに直書きするのは非推奨です

AWSTemplateFormatVersion: '2010-09-09'
Description: Secrets Manager Tutorial
Resources:
  MySecret2:
    Type: AWS::SecretsManager::Secret
    Properties:
      Description: manually password
      SecretString: 'password'
Outputs:
  SecretArn2:
    Value: !Ref MySecret2

このままでは秘匿したい情報が平文で残るので、本ブログでは順を追って改善します。
一旦はこの書き方で動くことを確認したいので、このまま進めます。

それではデプロイしてみましょう。
※パスワード自動生成デモの時に使っていたファイル名を流用しています

$ aws cloudformation deploy --stack-name SecretStack --template-file secrets-manager.yaml
Waiting for changeset to be created..
Waiting for stack create/update to complete
Successfully created/updated stack - SecretStack

スタックの状態を確認します。

$ aws cloudformation describe-stacks --stack-name SecretStack --query "Stacks[].StackStatus" --output text
CREATE_COMPLETE

CREATE_COMPLETE となっていればスタックの作成はOKです。
ただし、この書き方には問題があります。
CloudFormationテンプレートにパスワードを直書きしているため、テンプレートを見られる人全員にパスワードが露呈します。

$ aws cloudformation get-template --stack-name SecretStack --query "TemplateBody"
"AWSTemplateFormatVersion: '2010-09-09'\nDescription: Secrets Manager Tutorial\nResources:\n  MySecret2:\n    Type: AWS::SecretsManager::Secret\n    Properties:\n      Description: manually password\n      SecretString: 'password'\nOutputs:\n  SecretArn2:\n    Value: !Ref MySecret2"

テンプレートファイルを表示するコマンドを実行してみると、 SecretString: 'password' と出ていますね。
つまり、テンプレートを閲覧できる人であれば誰でも見られる状態になっています。

ではどうするか?

Parametersを活用して、外部から値を注入しましょう。
テンプレートを以下の通り書き換えます。

AWSTemplateFormatVersion: '2010-09-09'
Description: Secrets Manager Tutorial
Parameters:
  Password:
    Type: String
    Description: SecretString Value
Resources:
  MySecret2:
    Type: AWS::SecretsManager::Secret
    Properties:
      Description: manually password
      SecretString: !Ref Password
Outputs:
  SecretArn2:
    Value: !Ref MySecret2

こうすることで、Parametersに定義した値を参照するようになりました。
この変更によって、パスワードがテンプレートに直書きされることは回避できます。
では、実際にデプロイします。

$ aws cloudformation deploy --stack-name SecretStack --template-file secrets-manager.yaml --parameter-overrides Password="ParametersPassword"
Waiting for changeset to be created..
Waiting for stack create/update to complete
Successfully created/updated stack - SecretStack

デプロイが完了したので、スタックの状態を確認します。

$ aws cloudformation describe-stacks --stack-name SecretStack --query "Stacks[].StackStatus" --output text
UPDATE_COMPLETE

UPDATE_COMPLETE となっているため、問題なさそうです。
テンプレートファイルを確認します。

$ aws cloudformation get-template --stack-name SecretStack --query "TemplateBody"
"AWSTemplateFormatVersion: '2010-09-09'\nDescription: Secrets Manager Tutorial\nParameters:\n  Password:\n    Type: String\n    Description: SecretString Value\nResources:\n  MySecret2:\n    Type: AWS::SecretsManager::Secret\n    Properties:\n      Description: manually password\n      SecretString: !Ref Password\nOutputs:\n  SecretArn2:\n    Value: !Ref MySecret2"

SecretString: !Ref Password となっているので、一見問題なさそうです。
ですがまだ不完全です。以下のコマンドを実行します。

$ aws cloudformation describe-stacks --stack-name SecretStack --query "Stacks[].Parameters" --output table
----------------------------------------
|            DescribeStacks            |
+---------------+----------------------+
| ParameterKey  |   ParameterValue     |
+---------------+----------------------+
|  Password     |  ParametersPassword  |
+---------------+----------------------+

マネジメントコンソールのパラメータタブからも確認できますが、上記の通りパスワードが隠せていないですね。
そこで、NoEchoの登場です。以下のようにテンプレートを書き換えます。

AWSTemplateFormatVersion: '2010-09-09'
Description: Secrets Manager Tutorial
Parameters:
  Password:
    Type: String
    Description: SecretString Value
    NoEcho: true
Resources:
  MySecret2:
    Type: AWS::SecretsManager::Secret
    Properties:
      Description: manually password
      SecretString: !Ref Password
Outputs:
  SecretArn2:
    Value: !Ref MySecret2

改めてスタックに更新をかけます。パスワードも変更します。

$ aws cloudformation deploy --stack-name SecretStack --template-file secrets-manager.yaml --parameter-overrides Password="ParametersPasswordNoEcho"
Waiting for changeset to be created..
Waiting for stack create/update to complete
Successfully created/updated stack - SecretStack

改めてCloudFormationパラメーターをチェックしてみましょう。

$ aws cloudformation describe-stacks --stack-name SecretStack --query "Stacks[].Parameters" --output table
------------------------------------
|          DescribeStacks          |
+---------------+------------------+
| ParameterKey  | ParameterValue   |
+---------------+------------------+
|  Password     |  ****            |
+---------------+------------------+

パスワードがマスクされています。
これで手動でパスワードを設定しつつ、テンプレートファイルやパラメータに平文で表示されるのを回避できました。

補足ですが、AWS公式としてはDynamic Referenceを活用したソリューションを推奨しているようです。

例えば、以下のような記述のことを指します。

{{resolve:ssm-secure:parameter-name:version}}

CloudFormationで値が隠せるようになったので、Secrets Managerに値が生成されていることを確認します。
以下のコマンドを実行します。

$ aws cloudformation describe-stacks --stack-name SecretStack --query "Stacks[0].Outputs[?contains(OutputKey, 'SecretArn2')].OutputValue" --output text
arn:aws:secretsmanager:ap-northeast-1:012345678910:secret:MySecret2-W95HxAf5gIAJ-K7qTRu

Secrets ManagerのARNを控えて、設定したパスワードが格納されているか確認します。

$ aws secretsmanager get-secret-value --secret-id arn:aws:secretsmanager:ap-northeast-1:012345678910:secret:MySecret2-W95HxAf5gIAJ-K7qTRu --query "SecretString" --output text
ParametersPasswordNoEcho

Secrets Managerに格納されている値も想定通りですね。

後片付け

それでは、最後にリソースを削除して締めたいと思います。
以下のコマンドを実行します。

$ aws cloudformation delete-stack --stack-name SecretStac

リソースが残っていないか、以下のコマンドで確認をしましょう。

$ aws secretsmanager get-secret-value --secret-id arn:aws:secretsmanager:ap-northeast-1:012345678910:secret:MySecret2-W95HxAf5gIAJ-K7qTRu --query "SecretString" --output text
An error occurred (ResourceNotFoundException) when calling the GetSecretValue operation: Secrets Manager can't find the specified secret.
$ aws cloudformation describe-stacks --stack-name SecretStack --query "Stacks[].StackStatus" --output text
An error occurred (ValidationError) when calling the DescribeStacks operation: Stack with id SecretStack does not exist

上記のように、 ResourceNotFoundException / SecretStack does not exist と表示されていればOKです。

お疲れさまでした。

この記事は私が書きました

嶋田 龍登

記事一覧

インフラからアプリのことまでなんでもやりたい、フルスタックを目指すエンジニア

嶋田 龍登

この記事を共有する

クラウドのご相談

CONTACT

クラウド導入や運用でお悩みの方は、お気軽にご相談ください。
専門家がサポートします。

サービス資料ダウンロード

DOWNLOAD

ビジネスをクラウドで加速させる準備はできていますか?
今すぐサービス資料をダウンロードして、詳細をご確認ください。