社外の開発メンバーをAWSアカウントに入れるときのIAM設定を考えている

サービスを開発する際に、社外の業者さんに開発をお願いしたり、社外パートナーに開発に参加してもらう、ということがよくあります。 開発に使うAWS環境として、うちの会社で作成したAWSアカウントに入ってきてもらうこともあるのですが、このときにAWSアカウントの管理者として社外の開発メンバーにどのような権限を持たせるのが良いか、それをどう実現するのが良いか、いつも悩みます。

このエントリでは現時点での考えと実装方法をまとめておこうと思います。

課題

私が関わる案件で社外の開発メンバーに協力を仰ぐ場合、大抵はPoCから始まるような新規サービスの構築案件となるためAWSのどのサービスを使うか最初からすべて決まっていることは稀です。
最初は ALB + EC2 + RDS で作り始めたシステムにDynamoDBが導入され、AWS IoT coreが導入され、Kinesis Stream が導入され、、、と拡張されていったり、API Gateway + Lambda でサーバーレス構成に切り替えたり、と様々な状況が発生します。また、AWSから新サービスが発表されたら検証のために触ってみたりもします。

そういう事情があるので、IAMポリシー設計の超大原則である「基本は全拒否、必要なサービスへのアクセスのみ許可」というホワイトリスト方式では柔軟性に欠け効率が悪くなるケースが多いです。運用フェーズに入るとホワイトリスト方式でまわることも多いのですが、開発中はある程度自由にAWSを触ってもらえる権限を付与しておきたいところです。

かといって、闇雲にAdministrator権限のように本当になんでもできる権限を付与するのもリスクが大きすぎます。最低限のセキュリティは維持しつつ、自由に開発してもらうにはどのくらいの権限を残し、どのような権限を制限するのが良いでしょうか。

基本方針

  • 社内外に関わらず、AWS利用者が個人レベルで識別できるようにIAMユーザを作成する
  • 基本的に、ほぼすべてのAWSのサービスを自由に使える権限を開放する
  • ただし、IAM関連やBilling関連、CloudTrailやAWS ConfigなどなどAWSアカウントの管理に関わる部分は触れないようにする
  • IAMだけで制御するのが難しい点は、AWS Config Rulesなどを活用して問題をすぐ検知できるようにする

今のところ、この辺を落とし所としています。

具体的な設定内容

入り口

  • 社外の開発メンバーには個人ごとにIAMユーザを作成する
  • IAMユーザはMFA設定を必須とし、パスワードポリシーもある程度の複雑さを担保できるレベルに縛る
  • IAMユーザには自ユーザのアクセスキー/シークレットキーを発行できる権限を与え、必要になったら自分でキーの発行を実施してもらう
  • IAMユーザに直接IAMポリシーを付与せず、グループに所属させた上でグループにIAMポリシーを付与して権限を管理する

いきなりベストプラクティスから外れていますが、社外メンバー用に IAMユーザを作成する運用です。

セキュリティ的には外部ID (ExternalId) *1を必要とするIAMロールを用意して、社外開発メンバーには自社のAWSアカウントのIAMユーザもしくはIAMロールからAssumeRole API でロールの切り替えをしてもらえばベストなわけですが、外部IDを必要とするIAMロールへはAWSマネジメントコンソール上から切り替えることができません*2。社外の開発メンバーが皆AWSのIAM周りに詳しく、しかもAWSマネジメントコンソールを使わず開発を進められるような状況はそうそう無いと思います。普通はマネジメントコンソールも使いたいですよね。

そうなると、Switch Role用のIAMロールを用意するか、IAMユーザを用意するか、という選択になります。自社のAWSアカウントからSwitch Role して入ってくるほうが本人たちは楽だと思うのですが、今度は元のアカウントのIAMユーザがセキュリティ上どれだけ保護されているのかがわからないため、うちの会社のAWSアカウントでIAMユーザを作成してそれを使ってもらっています。こうすることで、MFAが設定されているか、パスワードやアクセスキーのローテーションは行われているか、最後にアクセスしてきたのはいつか、といった情報を監査できるようになります。なお、社内のメンバーについては、元のAWSアカウントのIAMユーザの状況が把握可能な場合はむしろSwitch Roleを推奨しています。また社外のメンバーでもうちのAWSアカウントを複数使う場合は二つ目以降は Switch Role を使ってもらっています。基本的に開発環境用AWSアカウントにIAMユーザーを作成し、QA環境や本番環境のAWSアカウントには Switch Role で入る、という感じになります。

アクセスキーの運用に関しては、AWSに慣れている人は何の問題もなく自分でアクセスキーを発行して管理・運用できますし、逆にAWSに慣れてない人に無闇にアクセスキーを渡すとソースコード内にハードコードしてしまったりIAMロールとの使い分けが曖昧になってしまったりすることがあるため、自分でIAMユーザ・アクセスキーとIAMロールの使い分けなどを学んだ後に必要性を感じたらアクセスキーを発行してもらう、という運用にしています。

IAMユーザに直接IAMポリシーを付与しないでグループ単位で権限を管理しているのは、権限を変更するときにIAMユーザによって抜け漏れが発生してしまうのを防ぐのと、人の入れ替わりがあるときに権限付与の手間を省く意味があります。大体、1社につき開発者用グループと閲覧専用グループを用意する感じですが、閲覧専用グループはあまり使われる機会がないな、という感触です。AWSマネジメントコンソールに入って来るような人は大抵開発者なので開発者用グループを使いますし、開発フェーズでは「CloudWatchだけ見たい」みたいなニーズに遭遇したことがありません。

これ以降の項ではこの「開発者用グループ」にどのような権限を与えているかを示します。

IAMポリシー全体

gist.github.com

1つのIAMポリシーにまとめずとも、後半のいくつかのStatement は別のIAMポリシーに切り出して、ケースバイケースで一部許可したりなど調整できるようにしてもよいと思います。今のところ、だいたいこんな感じにしている、というサンプルです。

基本は全許可

IAMはデフォルトで全ての操作が禁止されている*3ため、4行目からの最初のStatementでは、一部のAWSサービスについての操作(Action) 以外を一括で明示的に許可しています。 一部制限をかけている操作は、AWS Organizations や AWS Single Sign-On など、まあ普通は社外の開発メンバーが気にする必要はないよね、というサービスを列挙しています。

        {
            "Effect": "Allow",
            "NotAction": [
                "organizations:*",
                "sso:*",
                "sso-directory:*"
            ],
            "Resource": "*"
        },

案件によっては「このAWSサービスは絶対に使わないだろう」というものがあるかもしれません。その場合は、触る必要の無いAWSサービスをこのStatement の NotAction に追加してしまうと楽ちんです。たとえば、うちの会社ではAWS Ground Station は当面使うことはなさそうですから、ここでNotResourceに追加してしまってもいいかもしれません。

ここから先のStatementでは、禁止したい行為を一つ一つ制限していくことになります。

勝手に「入り口」のルールを変えてはならない

続くStatement では先述した「入り口」のルールが勝手に壊されないようにIAM関連の特定の操作を禁止しています。

        {
            "Effect": "Deny",
            "Action": [
                "iam:AddUserToGroup",
                "iam:AttachGroupPolicy",
                "iam:AttachUserPolicy",
                "iam:CreateGroup",
                "iam:DeleteGroup",
                "iam:DeleteGroupPolicy",
                "iam:DetachGroupPolicy",
                "iam:PutGroupPolicy",
                "iam:CreateUser",
                "iam:DeleteUser",
                "iam:DeleteUserPolicy",
                "iam:DetachUserPolicy",
                "iam:PutUserPolicy",
                "iam:RemoveUserFromGroup"
            ],
            "Resource": "*"
        },
  • 勝手に新しいIAMユーザやIAMグループを作ったり、既存のIAMユーザやIAMグループを消してはならない
  • 勝手にIAMユーザをIAMグループに加えたり、IAMユーザをIAMグループから抜いてはならない
  • 勝手にIAMユーザやIAMグループにIAMポリシーを付与したり削除したりしてはならない

ここに穴があると、自分でより強い権限のIAMポリシーを作成して自分のIAMユーザやIAMグループに付与する、ということができてしまいます。
上記のポリシー設定だと、自分でIAMポリシーを作ることは可能ですがIAMユーザやIAMグループに付与(Attach)することができないため、結果として「人」に対する権限付与の自由を制限していることになります(なってると思う…)。こんなめんどくさいことしなくてもIAMポリシーを作ること自体を禁止すればいいのでは?と思えるのですが、そうすると今度はEC2やLambdaなどのAWSリソースに割り当てるIAMロールに付与するIAMポリシーが作れなくなってしまい、開発に支障が出てしまいます。そのため、あくまで「人」に対して意図しない強い権限を付与できないところを防衛ラインとしています(ここのStatementだけでは穴があるので、次のStatement+αでその穴を潰しています)。

勝手にIAMロールに強い権限のIAMポリシーを割り当ててはならない

続く2つのStatementでは、先のStatementで埋められなかった穴を多少埋めています。
IAMロールの作成は自由にできるよう許可しているため、例えばEC2に割り当てるIAMロールに (AWSが標準で用意しているAWS管理ポリシー) AdministratorAccess のような強力なIAMポリシーを付与することができてしまいます。すると当該EC2からはすべてのAWS操作が可能になってしまうので、これを禁止します。

        {
            "Effect": "Deny",
            "Action": [
                "iam:AttachRolePolicy",
                "iam:CreatePolicy*"
            ],
            "Resource": "arn:aws:iam::*:policy/Administrator*"
        },
        {
            "Effect": "Deny",
            "Action": [
                "iam:AttachRolePolicy",
                "iam:CreatePolicy*"
            ],
            "Resource": "*",
            "Condition": {
                "ArnEquals": {
                    "iam:PolicyARN": [
                        "arn:aws:iam::*:policy/Administrator*"
                    ]
                }
            }
        }
  • Administrator* のような強力なAWS管理ポリシーや自作IAMポリシーをIAMロールに付与してはならない (2個めのStatement)
  • 勝手に Administrator* のような名前のIAMポリシーを作ってはならない (1個めのStatement)

実はこれでもまだ穴があります。

例えば AdministratorAccessと同様の内容のIAMポリシーを自作し、EC2に割り当てるIAMロールにそのIAMポリシーを付与すると、結局当該EC2インスタンスからはすべてのAWSの操作が可能になってしまいます。これをIAMだけで防ぐことも Permissions Boundary を活用することで可能だとは思うのですが、IAMロールを作成するときに制約*4ができてしまったりして運用が複雑化するため、IAMで頑張ることは諦めました。 そのかわり、AWS Config Rules で検知することですぐに潰せるようにしています。AWS Config マネージドルールのiam-policy-no-statements-with-admin-access を参考に、IAM関連の特定操作が可能だったり、強い権限のIAMポリシーが作成されたら検知できるようにします。

勝手に自分以外のIAMユーザのMFA設定を変えてはならない

次のStatement では、自分のIAMユーザ以外のIAMユーザのMFA設定を勝手に変えられないように制限をかけています。
他人のIAMユーザのMFA設定を自由に変えられてしまうと、設定されているMFAデバイスとの紐づけを解除して自分が持っているMFAデバイスと紐付けることで他人のIAMユーザをロックすることができてしまいます。共同開発しているような状況で他人のIAMユーザをロックしたくなる動機があるのかどうかは謎ですが。

        {
            "Effect": "Deny",
            "Action": [
                "iam:EnableMFADevice",
                "iam:ResyncMFADevice",
                "iam:DeactivateMFADevice",
                "iam:DeleteVirtualMFADevice"
            ],
            "NotResource": [
                "arn:aws:iam::*:mfa/${aws:username}",
                "arn:aws:iam::*:user/${aws:username}"
            ]
        }

ミソは自分のIAMユーザに対してはMFA設定を行える権限を維持するために NotResource で「自分のIAMユーザ については操作禁止対象から除外」している点です。二重否定になりややこしいのですが、IAMポリシーでは明示的な拒否(Deny)は明示的な許可(Allow)よりも強い*5ため、"Resource": "*" に対してDeny したあと "Resource": (自分のIAMユーザとMFAデバイス) にAllowの設定を書いても効かず(DenyとAllowの順序を逆にしてもダメ)、こうするしかないのかなぁと思っています。より良い書き方がありそうな気はしているのですが...。 なお、自分のIAMユーザに対してのMFA設定権限がないと、「IAMユーザにはMFA設定を必須とする」という方針と矛盾してしまいます。

勝手に自分以外のIAMユーザのアクセスキーやパスワードを作ったり消したりしてはならない

次のStatement では、自分のIAMユーザ以外のIAMユーザの認証情報をいじれないように制限をかけています。

        {
            "Effect": "Deny",
            "Action": [
                "iam:CreateAccessKey",
                "iam:DeleteAccessKey",
                "iam:UpdateAccessKey",
                "iam:ChangePassword",
                "iam:DeleteLoginProfile",
                "iam:UpdateLoginProfile",
                "iam:DeleteSSHPublicKey",
                "iam:UpdateSSHPublicKey",
                "iam:CreateServiceSpecificCredential",
                "iam:DeleteServiceSpecificCredential",
                "iam:ResetServiceSpecificCredential",
                "iam:UpdateServiceSpecificCredential",
                "iam:DeleteSigningCertificate",
                "iam:UpdateSigningCertificate",
                "iam:UploadSigningCertificate"
            ],
            "NotResource": [
                "arn:aws:iam::*:user/${aws:username}"
            ]
        },

こちらもMFA関連の設定と同じく、ミソは自分のIAMユーザに対してはアクセスキーの発行やパスワードの変更を許可しなければならないため、NotResource で自身のIAMユーザは制限の対象外としている点です。

この辺までは割と汎用的に使える設定かと思っていますが、これ以降のStatementは社外の開発メンバーにお願いしている業務内容によっては一部許可しても良いのでは?というものも含まれています。 なので、これ以降のStatement は別々のIAMポリシーに切り出して、例えば会社ごとに足したり引いたり調整できるようにしてもよいと考えています。

勝手にAWSアカウントの設定を変更してはならない

次のStatement は、AWSのアカウント情報(「マイアカウント」画面)の参照や支払い方法の変更、リージョンの有効化・無効化などを制限するためのものです。

        {
            "Effect": "Deny",
            "Action": [
                "aws-portal:*Account",
                "aws-portal:ModifyBilling",
                "aws-portal:*PaymentMethods",
                "account:EnableRegion",
                "account:DisableRegion"
            ],
            "Resource": [
                "*"
            ]
        },

アカウント情報は、場合によっては参照されても困らないこともあるかと思いますが、連絡先などを伏せたい場合が多いので制限をかけています。危険なのは aws-portal: ModifyAccount で、これはAWSアカウントの設定情報を変更ができてしまうので確実に禁止しておくことにしています。 同様に、支払い方法を勝手に変更されるのも困るので、aws-portal:ModifyBilling Action も禁止しています。
リージョンの有効化・無効化は現時点(2019年9月) では 中東 (バーレーン) と アジアパシフィック (香港) しか切り替えることができません。今後、現在は無効化できないリージョンが無効・有効を切り替えられるようになったりした場合などに混乱を防ぐために禁止しています。(意図しないリージョンにAWSリソースを作ってしまったり、コッソリ誰も使っていないようなリージョンにAWSリソースを作成されるのを防ぐため、現在は無効化できないリージョンも将来 無効・有効が選べるようになるといいな、と期待しています)

勝手にAWS CloudTrail を無効化したり証跡情報を削除してはならない

次のStatementはAWS CloudTrail に関するものです。

        {
            "Effect": "Deny",
            "Action": [
                "cloudtrail:StopLogging",
                "cloudtrail:DeleteTrail"
            ],
            "Resource": "*"
        },

AWS CloudTrailは基本的に私が関わる全AWSアカウントの全リージョンで有効化しています。いつ・誰が・何をしたか の証跡情報を確実に残しておくため、AWS CloudTrail を勝手に停止されたり、証跡情報を削除されたりしないように制限をかけています。AWS CloudTrail の状態監視にはAWS Config Rules のマネージドルールもいくつか用意されている*6ので、合わせて活用しています。
AWS CloudTrail の証跡情報はS3やCloudWatch Logsに保存できるのですが、それらのバケットやログストリームについても削除されないよう、同じAWSアカウント内にある場合はIAMポリシーで保護しておくのが良いでしょう。

勝手にAWS Config の記録を停止したり構成管理情報を削除してはならない

次のStatementはAWS Config に関するものです。

        {
            "Effect": "Deny",
            "Action": [
                "config:Delete*",
                "config:StopConfigurationRecorder"
            ],
            "Resource": "*"
        }

AWS Configは、全てのAWSリソースタイプに対応しているわけではありませんが、IAMやEC2、S3などなどメジャーなサービスの構成変更履歴を自動で記録してくれるサービスで、AWS CloudTrail と同様に基本的に全リージョンで有効化しています。これも勝手に構成変更の記録を停止されたり削除されたりしては困るので、制限をかけています。

その他

今回記載したIAMポリシーの例には出ていない Amazon GuardDuty や Amazon Macie など、セキュリティ関連のAWSサービスではアクセス権限を管理したいものがありそうですが、この辺はまだ案件によって導入できてたりできてなかったりするので割愛しました。

まとめ

AWSアカウントの管理者として、社外の開発メンバーに対してどのようなアクセス制限が必要か、またそれをどのように実現するかについて、現時点での考えをまとめました。

考慮が漏れている点やもっとキレイに実現できる方法などなどたくさんありそうなので、いいやり方をご存知でしたらフィードバックをいただけたら幸いです。

参考