Edited at

CircleCIから安全にAWSのプロダクション環境へデプロイする

CircleCIから複数のAWSアカウント、たとえばステージング環境とプロダクション環境(本番環境)へ、安全にデプロイする方法を記載します。


課題


  • CircleCIはAWSのCodePipeline等より使い勝手が良いのでAWSのプロダクション環境へのデプロイに使いたい


    • しかしステージング環境とプロダクション環境は別のAWSアカウントで構築しておりIAMのアクセスキーの切り替えが必要

    • そしてIAM権限をAWSの外に出すのはセキュリティ上の懸念がある




対策


  • 別アカウントなので認証情報の切り替えが必要



  • IAM権限をAWSの外に出すのはセキュリティ懸念がある


    • => 利用するときだけIAMアクセスキーを有効化する


      • Slackから簡単にキーの有効/無効を切り替えられるようにしておきましょう





順に説明してゆきます。


ブランチごとにAWS認証情報を切り替え

AWSの別アカウントで構築したプロダクションへデプロイするために、ブランチごとにAWS認証情報を切り替える手順を記載します。

ここではmasterブランチへの更新をプロダクション環境、その他のブランチへの更新をステージングへデプロイすると想定します。

まず前提として、CircleCIの環境変数を利用してステージングへのデプロイを実現できます。

手順の詳細は割愛しますが、環境変数に AWS_DEFAULT_REGIONAWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEY(シークレットキー) を登録した後にOrbsを利用します。

※従来の設定項目であるAWS Permissionsは非推奨となり、将来的に廃止される予定です。

この状態から、masterブランチへの更新時のみ別のIAM認証を使いたい場合、以下の手順で実現できます。


  • プロダクション環境へのデプロイ用にContextを作成し、AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEY を登録


    • ここでは例として production_deployment という名前で作成します




  • 登録したContext production_deployment は、以下のような感じで利用できます。

version: 2.1 # Contextを利用するにはWorkflowsを使う必要があるので`2.1`を指定しています

orbs: # Orbs, CircleCIのスニペットみたいなものです
aws-cli: circleci/aws-cli@0.1.13

executors:
default:
working_directory: ~/repo
docker:
- image: circleci/python:3.7.1-node

jobs:
deploy:
executor:
name: default

steps:
- checkout

- restore_cache:
keys:
- v1-npm-dependencies-{{ checksum "package-lock.json" }}
- v1-npm-dependencies-

- run:
command: npm ci

- save_cache:
paths:
- ./node_modules
key: v1-npm-dependencies-{{ checksum "package-lock.json" }}

# Orbsを使用してaws-cliのインストールとAWS認証情報の反映を行う
- aws-cli/install
- aws-cli/configure

- deploy:
command: npm run deploy

workflows:
deploy:
jobs:
- deploy: # このjobはfiltersによりmasterブランチ「以外」で実行される
filters:
branches:
ignore:
- master
- deploy: # このjobはfiltersによりmasterブランチ「のみ」で実行される
context: production_deployment # ここで先程作成したcontextのAWS認証情報を指定
filters:
branches:
only:
- master

このように config.yml を記述することで、masterブランチへの更新はContextのAWS認証情報、それ以外は環境変数の認証情報と切り分けることができます。

この例では環境変数を使用していますが、今後はステージング環境などへの認証情報もContextで管理するのが保守性の観点で筋が良いでしょう。


プロダクション環境のIAMアクセスキーは利用時のみ有効化

CircleCIをプロダクション環境へのデプロイに利用する時、どうしてもネックとなるのが AWS外にそこそこ強力なIAMアクセスキーを出さなければならないこと です。

もちろんCircleCIでContextを利用すれば外部に公開されることはなく、そのContextもグループによる制限を指定することできめ細やかなパーミッションの管理ができます。しかしCircleCIがクラックされたり、何か不足の事態が起こらないとも限りません。

なのでここでは、 プロダクションへのデプロイ時のみデプロイ用のIAMアクセスキーを有効化する 運用を行うための手順を記載します。


手順


  1. まず、circleciという名前でCircleCIからのデプロイ専用のIAMユーザを作成し、アクセスキーを作成します(手順割愛)。

  2. そのアクセスキーを上記手順に従いCircleCIのContextへ登録します。

以上の操作で、マネコンからアクセスキーの有効/無効を切り替えることで運用が可能となりました。これでも良いのですが、いちいちマネコンを開くのは面倒です。

そこで、ここではSlackを利用してみましょう。


IAMアクセスキーの有効/無効 切り替えSlackコマンドの作成

Slackを利用して簡単かつ安全にアクセスキーの有効/無効を切り替える環境を構築します。


SlackのユーザIDを取得

まずコマンドを実行できる(=強力なIAMアクセスキーの有効/無効を切り替えられる)Slackユーザーを決め、そのユーザIDを取得します。sot528 のような ユーザ名 ではなく U8FS891QA というような ユーザID です。SlackのAPIトークンがあればここでメンバーのユーザIDを取得できます。

ここでは例として、U1234567U89ABCDEという2人のユーザに権限を割り当てるものとします。


SlackのOutgoing Webhookを作成

Outgoing WebhookはDuplicatedになっているようです。知らなかった..まあ今回は気にせず使いましょう。

Outgoing Webhookを 2件 作成し、そのトークンを取得しておきます。作成画面↓の赤枠部分です。作成画面は開いておいてください。

ここでは例として、XXXXXXXXYYYYYYYYという2つのトークンを利用するものとします。


IAM操作用のserverlessアプリをデプロイ

この運用を行うためにシンプルなツールを作りましたのでこちらを使います。serverlessフレームワークを使用してAPI Gateway => Lambdaの構築を行っています。


使い方

git clone https://github.com/AlisProject/toggle-iam-access-key.git

npm ci

環境変数を設定します。ここではdirenvを使っています。

cp .envrc.sample .envrc

direnv edit

今回の例では、設定する値は以下のとおりです。

複数の値が存在するものはカンマ区切りで指定します。


  • SLACK_ACCESS_TOKENS: XXXXXXXX,YYYYYYYY

  • ACCEPTED_SLACK_USERS: U1234567,U89ABCDE

  • AWS_IAM_USER_NAME: circleci

  • AWS_IAM_ACCESS_KEY_ID: IAMユーザ circleci のアクセスキーのID

  • その他、AWSの認証情報: このツールをデプロイするのに必要なAWSの認証情報です

デプロイします。

npm run deploy:production

成功したら以下のような表示が出るので、 endpoints を2件控えておきます。

さきほど開いておいた2つのOutgoing Webhookの作成画面で、このendpointsを1つずつ URL(s) に設定します。末尾が enable がIAMキーを有効化するもので、disableが無効化するものです。

それぞれ適当な Trigger Word(s) も設定しましょう。ここではenableに key!、 disableにkey_disable!を割り当てます。

これは必須ではないですが、お好みで、Customize NameIAMCustomize IconIAMのアイコンとか登録しておくとわかりやすくてGoodです。

さて、準備が整いました。SlackからIAMアクセスキーの開閉をしてみましょう。Outgoing Webhookは公開チャンネルしか有効ではないのでご注意。

鍵の開閉ができましたね。これでだいぶ楽に運用できそうです。

ちゃんとマネコンでアクセスキーが有効化/無効化されていることを確認しておきましょう。

ちなみに許可されていないIDのSlackユーザがコマンドを叩くと。

(:point_up:՞ਊ ՞):point_up:ウェーイ!


備考


  • 今回の手順では割愛していますが、CircleCIのプロダクション環境用のContextにはグループによる制限を付加した方が良いでしょう。

  • 現状、CircleCIのContextを複数指定することはできないようです。


    • これができると大変に便利なんですが。Jobを細かく分けろという話ですかね



  • 重要なリポジトリは本番環境への反映前にCircleCIのワークフローで承認(approve)を挟むと良いかと思います



  • CodePipelineやCodeBuildなど、AWSのCode兄弟達とも格闘しましたが、私の結論は CircleCI最強 です..


    • もちろん要件によりCode兄弟の方が適している場合もありますが



  • TODO: 全ビルドが終わったら勝手にIAMを無効化する まで自動化したい

  • ご参考までに: だいたいのリポジトリでCircleCI使用していてほとんどコード公開してます



  • もっとよろしい運用をご存知でしたらご教示ください🙏