CloudFormationでIAMアクセスキーの発行とSecrets Managerへの格納をしてみた

2021.07.18

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは、CX事業本部 IoT事業部の若槻です。

今回は、CloudFormationでIAMアクセスキーの発行とSecrets Managerへの格納をしてみました。

なぜCloudFormationとSecrets Managerなのか?

(主観ですが)AWSのIaC機能は下記の2つです。

また、AWSのセキュアなパラメータ管理機能は主に下記の2つです。

このうち、IAMアクセスキーの発行とそのクレデンシャルの格納をIaCで完結させられる方法は、調べてみたところ「CloudFormationSecrets Managerの組み合わせのみ」だったため、今回その方法についてご紹介します。また記事後半では、その他の組み合わせがなぜ出来なかったかについても記載します。

やってみた

CloudFormation テンプレート

IAMユーザー作成、アクセスキー発行、Secrets Managerへの格納という検証に必要な最低限のリソース定義です。

template.yaml

AWSTemplateFormatVersion: '2010-09-09'

Resources:
  IAMUser1:
    Type: AWS::IAM::User
    Properties:
      Path: /
      UserName: IAMUser1

  IAMUser1AccessKey:
    Type: AWS::IAM::AccessKey
    Properties:
      UserName: !Ref IAMUser1

  IAMUser1AccessKeySecret:
    Type: AWS::SecretsManager::Secret
    Properties:
      Name: !Sub ${IAMUser1}-credentials
      SecretString: !Sub "{\"accessKeyId\":\"${IAMUser1AccessKey}\",\"secretAccessKey\":\"${IAMUser1AccessKey.SecretAccessKey}\"}"

デプロイします。

% aws cloudformation deploy \
  --template-file template.yaml \
  --stack-name access-key-to-secret-stack \
  --capabilities CAPABILITY_NAMED_IAM \
  --no-fail-on-empty-changeset

Secrets Manager に格納されたクレデンシャルの確認

AWSマネジメントコンソールでAWS Secrets Manager > シークレットより作成されたシークレットを見てみると、発行したIAMアクセスキーのIDとシークレットが格納されているのが確認できます。

その他の組み合わせ

いずれも仕様の制限により出来ませんでした。

AWS CDK と Secrets Manager

CloudFormationでは任意の値をSecrets Managerに格納できましたが、AWS CDKではSecrets Managerが自動生成した値しか格納できません。ドキュメントから抜粋の下記コードのようにgenerateSecretStringは使えますが、SecretStringは使えない制限があるようです。

    // Default secret
    const secret = new secretsmanager.Secret(this, 'Secret');
    secret.grantRead(role);

    new iam.User(this, 'User', {
      password: secret.secretValue,
    });

    // Templated secret
    const templatedSecret = new secretsmanager.Secret(this, 'TemplatedSecret', {
      generateSecretString: {
        secretStringTemplate: JSON.stringify({ username: 'user' }),
        generateStringKey: 'password',
      },
    });

    new iam.User(this, 'OtherUser', {
      userName: templatedSecret.secretValueFromJson('username').toString(),
      password: templatedSecret.secretValueFromJson('password'),
    });

この制限については下記のIssueでも取り沙汰されています。もともとはセキュリティを考慮した制限であったようですが、それが必要な制限であるかの議論が交わされており、Amazonの中の人もフィードバックとして受け取っているようなので、今後の展開を注視したいと思います。

AWS CDK (CloudFormation) と Parameter Store

下記はIAMアクセスキーを発行し、そのクレデンシャルをParameter StoreにSecureString形式で格納するCDKコードです。

cdk-stack.ts

import * as cdk from "@aws-cdk/core";
import * as iam from "@aws-cdk/aws-iam";
import { StringParameter, ParameterType } from "@aws-cdk/aws-ssm";

export class SampleAppStack extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const operationUser = new iam.User(this, "operation_user");

    const accessKey = new iam.CfnAccessKey(this, "access_key", {
      userName: operationUser.userName,
    });

    const accessKeyId = accessKey.ref;
    const secretAccessKey = accessKey.attrSecretAccessKey;

    //アクセスキーIDをパラメータストアに格納
    new StringParameter(this, "access_key_id_string_parameter", {
      parameterName: "access-key-id",
      stringValue: accessKeyId,
      type: ParameterType.SECURE_STRING,
    });

    //シークレットアクセスキーをパラメータストアに格納
    new StringParameter(this, "secret_access_key_string_parameter", {
      parameterName: "secret-access-key",
      stringValue: secretAccessKey,
      type: ParameterType.SECURE_STRING,
    });
  }
}

しかしこれをCDKデプロイするとエラーとなってしまいます。SecureString形式ではAWS CDK(CloudFormation)を使用して値を格納できないようです。

% cdk deploy
SampleAppStack: deploying...
SampleAppStack: creating CloudFormation changeset...
10:47:01 PM | UPDATE_FAILED        | AWS::SSM::Parameter | accesskeyidstringparameter35E68FEF
SSM Parameters of type SecureString cannot be created using CloudFormation

ちなみに平文のStringであればAWS CDKからでも格納はできますが、機密情報を暗号化せずに管理することになるのでそれは避けたいです。下記ドキュメントでもSecureString以外での機密情報の管理は非推奨と明記されています。

String パラメータまたは StringList パラメータに機密データを保存しないでください。機密データを暗号化したままにする場合は、SecureString パラメータタイプのみを使用します。

参考

以上