Link and Motivation Developers' Blog

リンクアンドモチベーションの開発者ブログです

AWSの"TEAM"を利用して、権限付与時間の最小化を目指す

SREの篠原です。
セキュリティのベストプラクティスには「最小権限のアクセスを付与する」という原則があります。

docs.aws.amazon.com

クラウド利用におけるセキュリティ対策として大切ですが、必要な権限のみに制限することは思ったよりも難しいです。
特に開発者に付与する権限については以下のようなケースになりがちです。

  • インシデント対応などの緊急時を想定して念の為に権限を渡しておく
  • 組織やプロダクト規模拡大が進み、最適な権限設定のメンテが追いつかなくなる
  • 逆に権限を減らし過ぎて新しい取り組みをする際に都度申請が必要になる

そのため開発者の生産性を守りつつセキュリティを向上するためには、付与する権限の強さを制限するよりも、権限を付与する時間を短くすることが適切ではないかと考えました。
それを実現するために「必要な時だけ権限を付与して、不要になったらすぐに剥奪する」運用を検討しました。

TEAMとは

そこでAWSの "TEAM" を導入しました。
TEAMは AWS Solutionsの一つで、今回のような一時的な権限昇格の仕組みです。
マイクロソフトは関係ありません。

概要や利用イメージについては公式ドキュメントの動画やクラメソさんの記事がわかりやすいかと思います。

aws-samples.github.io

dev.classmethod.jp

IAM Identity Center を利用していればすぐにデプロイできます。
実際に導入する上での設計要素はいくつかありますが、デプロイから設定完了まで基本的には数時間で完了します。

主な設計要素 (=カスタマイズできる箇所) は以下です。

  • 誰が権限リクエストできるか(Eligibility policy)
  • 誰がリクエストを承認できるか(Approval policy)
  • 通知設定 (Email or SNS or Slack)

上記の設計と実際の運用移行については少し工数がかかりましたが、実装部分をほぼスキップできたためスムーズに導入できました。

よかったところ

弊社で導入して約2ヶ月ほど経過し、概ね順調に運用できています。
その中でも特によかった点をいくつかピックしました。

権限管理を開発チームに委譲できた

これまでは開発者の権限管理は全てSREチームで管理し、開発者はSREに権限申請してもらう形で運用していました。
それをこのタイミングでTEAMでの承認者を各プロダクトのマネージャーやリーダーにお願いしました。

具体的には先ほど説明した Approval policyにおいて、以下の運用にしました。
「各プロダクトの本番AWSアカウントへの申請は、そのプロダクトのマネージャー/リーダーが承認する。」
※ プロダクトごとにAWSアカウントを分離しています

それによってチームを跨ぐ権限申請のコストが減りました。
さらに同じチームの人が承認するので、「本当にそのアクセス必要か?」といった確認の精度も向上したと思います。

公式がサポートしてくれる

AWS Solutionsは基本的に技術サポートの対象になります。
そのためTEAMに関する不具合も基本的には公式でサポートしてもらえます。

実際に弊社でTEAMをデプロイする際にバグがあり一度エラーになりました。
しかしサポートに問い合わせたところ原因調査と対応方法を教えていただきスムーズに解決できました。

権限周りなどAWSを利用する際に重要な機能になるので、サポートの有無は結構大事だと思います。

SSH -> Session Managerへの乗り換え

TEAMそのものの良かった点ではないのですが、TEAMでの権限管理に統合するべく、SSHでの認証をやめてSession Managerでサーバアクセスするようにしました。
これにより踏み台サーバのユーザや鍵の管理がなくなり、開発者も直接目当てのサーバにアクセスできるので生産性向上にもつながりました。

ログインする際にEC2のインスタンスIDを調べるなどの手間が発生しますが、そこはaws cli alias + pecoを組み合わせてなるべく負担にならないようにしました。
ちなみにMacOSでSession Managerでログイン中に Ctrl + Cを押すとセッションが切れてしまう不具合があったのですが、スクリプト内で合わせて対応しています。

github.com

.aws/cli/aliasのイメージ (click to open)

[toplevel]
# ssm
ec2-login =
    !f() {
        echo "接続するEC2を選択してください"
        # EC2インスタンスのリストを取得し、Instance IDとNameタグを表示する
        INSTANCES=$(aws ec2 describe-instances \
            --query 'Reservations[*].Instances[*].[InstanceId,Tags[?Key==`Name`].Value | [0]]' \
            --filters "Name=instance-state-name,Values=running" \
            --output text | awk '{print $1 " (" $2 ")"}' \
        )

        # pecoでインスタンスを選択
        SELECTED_INSTANCE=$(echo "$INSTANCES" | peco | awk '{print $1}')

        # 選択されたインスタンスにSession Manager経由でログイン
        if [ -n "$SELECTED_INSTANCE" ]; then
            echo "Connecting to $SELECTED_INSTANCE..."
            # Ctrl+Cによるセッション切れを防ぐため、ローカルでの割り当てを無効化する
            # SEE: https://github.com/aws/session-manager-plugin/issues/29
            stty intr undef
            aws ssm start-session --target "$SELECTED_INSTANCE"
            # Ctrl+Cを戻す
            stty intr ^C
        else
            echo "No instance selected or found."
        fi

    }; f

ちょっと微妙なところ

基本的には満足していますが、弊社の利用方法だと少し微妙な箇所もありました。

日本語入力のエラー

権限リクエストする際に申請理由 (justification)を記入する項目があるのですが、ここの1文字目が日本語の場合validationがエラーになってしまいます。

validation errorの例

そのため今はワークアラウンドとして、 hoge障害調査のため~~ みたいな形で適当な文字を先頭に書いてから真の理由を書いてもらっています。

しかしこれは修正PRを出して取り込んでもらえたので、v1.1.1の次のリリースからは修正されると思います。

github.com

Slack通知のカスタマイズ幅がない

通知方法として Email/SNS/Slack の3種類が対応しています。
Slack通知の場合、リクエストが発行された際にそれを承認できる人全員に通知が飛びます。

slack通知の例

弊社の場合緊急時に備えて、SREとEMは全リクエストの承認者にしています。
そのため全てのリクエストの通知が飛んでノイズになってしまっており、ここがもう少しカスタマイズできるといいなと思っています。

また通知が当人しか閲覧できないというのもあり、Slack通知とは別にSNSへの通知も設定し、SNS -> Lambda -> Slackといった形でpublicチャンネルへの通知も設定しています。
承認者の設計や通知周りの要件によっては少し作り込みが必要になります。

まとめ

TEAMの導入により、特権を必要な時だけ持つ形になるためセキュリティが大きく向上したと思います。
もしAWSの権限周りでお悩みであればぜひ検討してみてください。