cdkでアクセスキー生成を行いローカルでログに出力しつつ管理コンソール上の出力には残さない方法を考えてみた

cdkでアクセスキーが作れることを知って試したものの、視認する際に管理コンソール上で出力として残されるのは不味いなーと感じ、結果として管理コンソール上に出力としては残らないながらもアクセスキー自体は使える形を模索してみました。
2021.06.16

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

cdkでCfnAccessKeyを使ってアクセスキーやシークレットを生成する場合、CfnOutputを使わないと人が読み取れる状態で出力されません。ですが、CfnOutputを使うと管理コンソール上に出力として残り、セキュリティ的に宜しくありません。

 出力に残ることを防ぎたくてcdk deploy時にconsole.logを仕込んだとしても、あくまでCfnテンプレートを吐き出している段階のデータなので当然求めるキーの構成ではありません。

const userName = deployUser.userName || 'redash-deploy';
const accessKey = new CfnAccessKey(this, `RedashDeployAccessKey`, { userName: userName });
console.log(accessKey.ref);
${Token[TOKEN.76]}

管理コンソール上でCloudFormationの利用権限を絞る等の対策を取る必要に迫られますが、今まで存在しなかったルールや制約に振り回されるのも面倒です。何か妙案はないものかと考えた結果、要はアクセスキーを確認できつつ、管理コンソール上の出力に残らなければよいのだと思い至りました。

実用に耐えうるものかは置いておき、CfnAccessKeyを使いつつも管理コンソールのCloudFormationログには永続して残さないやり方を試してみました。

やり方

今回必要な要素は以下の通り。

  • 利用できるアクセスキーとシークレットがローカル環境で永続して確認できること
  • 実行終了後に管理コンソール上のCloudFormation出力には残さない

以下、順に読み進めます。

アクセスキーとシークレットの生成

まずは、アクセスキーとシークレットを生成し、人が読める形(=CfnOutput)にします。

      const userName = deployUser.userName || 'redash-deploy';
      const accessKey = new CfnAccessKey(this, `RedashDeployAccessKey`, { userName: userName });
      const key = new CfnOutput(this, 'RedashDeployUserAccessKeyOutPut', {
        value: accessKey.ref,
        description: 'AccessKey for Deploy with AssumeRole'
      });
      const secret = new CfnOutput(this, 'RedashDeployUserSecretAccessKeyOutPut', {
        value: accessKey.attrSecretAccessKey,
        description: 'SecretAccessKey for Deploy with AssumeRole'
      });

これで双方とも出力される状態となりました。

Outputs:
deploy-role.RedashDeployRole = arn:aws:iam::XXXXXXXXXXXX:role/redash-deploy
deploy-role.RedashDeployUserAccessKeyOutPut = XXXXXXXXXXXXXXXXXXXX
deploy-role.RedashDeployUserSecretAccessKeyOutPut = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

CfnOutputを迂回再実行する

次にIF判定を入れて、CfnOutputを実行しない状態にて再実行します。CfnAccessKeyの生成はそのままなので、スタックの更新は行われず同じキーとシークレットが維持されます。

      const need_show = process.env.SHOW_ACCESS_KEY || undefined;
      const userName = deployUser.userName || 'redash-deploy';
      const accessKey = new CfnAccessKey(this, `RedashDeployAccessKey`, { userName: userName });
      if (need_show) {
        const key = new CfnOutput(this, 'RedashDeployUserAccessKeyOutPut', {
          value: accessKey.ref,
          description: 'AccessKey for Deploy with AssumeRole'
        });
        const secret = new CfnOutput(this, 'RedashDeployUserSecretAccessKeyOutPut', {
          value: accessKey.attrSecretAccessKey,
          description: 'SecretAccessKey for Deploy with AssumeRole'
        });
      }

cdk deployは都合2回実行します。

set -a
npm run build
SHOW_ACCESS_KEY=1 \
npm run cdk -- deploy --require-approval never --profile ${AWS_PROFILE} deploy-role -f
SHOW_ACCESS_KEY= \
npm run cdk -- deploy --require-approval never --profile ${AWS_PROFILE} deploy-role -f

以下実際に実行した内容の一部抜粋です。

deploy-role: deploying...
deploy-role: creating CloudFormation changeset...


 ✅  deploy-role

Outputs:
deploy-role.RedashDeployRole = arn:aws:iam::XXXXXXXXXXXX:role/redash-deploy
deploy-role.RedashDeployUserAccessKeyOutPut = XXXXXXXXXXXXXXXXX
deploy-role.RedashDeployUserSecretAccessKeyOutPut = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Stack ARN:
arn:aws:cloudformation:ap-northeast-1:XXXXXXXXXXXX:stack/deploy-role/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX

> cdk@ cdk /path/to/redash
> cdk "deploy" "--require-approval" "never" "--profile" "XXXXXXXX" "deploy-role" "-f"

MFA token for arn:aws:iam::XXXXXXXXXXXX:mfa/XXXXXXXXXXXX: 
deploy-role: deploying...
deploy-role: creating CloudFormation changeset...


 ✅  deploy-role

Outputs:
deploy-role.RedashDeployRole = arn:aws:iam::XXXXXXXXXXXXXX:role/redash-deploy

Stack ARN:
arn:aws:cloudformation:ap-northeast-1:XXXXXXXXXXXX:stack/deploy-role/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX

初回でアクセスキーとシークレットを出力に残し、再実行にて出力に残らない形へ変更します。ローカルの実行ログに残るため確認は可能です。念の為管理コンソール上から過去出力パラメータを辿れるか確認してみましたが、常に最新のもののみが表示されるようです。

アクセスキーとシークレットの更新

新しい組み合わせにする場合は、CfnAccessKey部分をコメントアウト等で迂回実行後に有効な形で再実行することで刷新されます。

あとがき

CfnOutputに出しておかないと読み取れないという時点で扱いに困るリソースだと思いましたが、アクセスキーとシークレット自体を維持される状態にて出力だけ打ち消せれば結果として秘匿できるのではと思い、試してみました。

事前に挙動を理解していないと戸惑う仕組みではありますが、CfnAccessKeyとCfnOutputの挙動に悩む場合で対処の参考例になれば幸いです。

参考リンク