リモートワークでSecurity Groupの変更をユーザーの所属するプロジェクトだけ許可する設定をABACでやってみた

一部のリソース操作権限をユーザーに渡したい時、IAMポリシーで対象リソースを絞ります。でもその組み合わせが多かったら、都度IAMポリシーを作成するのが大変です。そんなときはABACを使いましょう。実際にSecurity GroupでABACする例を紹介します。
2021.01.11

こんにちは、臼田です。

みなさん、アクセス制御してますか?(挨拶

今回は比較的簡単なABAC(Attribute Based Access Control / 属性ベースのアクセス制御)としてSecurity Groupのルール変更だけを許可する設定をやってみたいと思います。

どんなことを実現したいか

説明の前提として、AWSの管理者と、EC2を利用して作業をしたいユーザーがいるとします。ざっくり以下のことを実現したいとします。

  • ユーザーはEC2にアクセスしたい
  • リモートワークをしているのでIPはユーザーに設定させたい
  • 管理者はユーザーにあまり強い権限を渡したくないためEC2にアタッチしたSecurity Groupの変更のみを許可したい
  • 他にもEC2やSecurity Groupがあるので変更を許可するリソースを限定したい

ここまでの条件の場合、「IAMポリシーで対象のリソースを指定すればいいのでは?」と考えると思います。それでも可能ですが、更に以下の条件を考えるとリソースの指定が大変になります。

  • 制御したいユーザーとリソースとプロジェクトがたくさんある

この場合、管理者はプロジェクトが増えるたびIAMポリシーを作成したり、リソースが増えるたびにIAMポリシーを変更したりしなければなりません。

これを解決するのがABACです。ABACはAWSのドキュメントでは以下のような図で説明されます。

図の左側はIAMリソースです。これは帽子のアイコンなのでIAM Roleですが、IAMユーザーやIAMグループでも同じように利用することが可能で、重要なのは帽子の右上についているタグです。3種類のタグがあると表現されています。そして右側の各種リソースも、それぞれタグが付いています。真ん中にあるのがIAMポリシーです。ABACはタグを利用したアクセスコントロールで、IAMリソースについているタグと操作対象のリソースについているタグが同じ場合にのみ利用できるようにIAMポリシーを記述して制御します。

今回の要件を満たす場合、ユーザーに渡すIAMに付与したProjectタグと、Security Groupに付与したProjectタグを一致させ、それをつなぐためのABACのIAMポリシーを作成してユーザーのIAMに付与すればOKです。

ABACやってみた

それでは実際にやってみます。

ABAC対応のIAMポリシー作成

まずABACに対応したIAMポリシーを作成します。これが今回の肝です。

IAMのポリシー画面からポリシーの作成を始めます。直接jsonを指定してもいいですが、今回はビジュアルエディタで説明します。サービスでEC2を選択(Security GroupはEC2のサービスAPIです。VPCも)、アクションはAuthorizeSecurityGroupIngress(Security Groupのインバウンドルールの追加)とRevokeSecurityGroupIngress(Security Groupのインバウンドルールの削除)を選択、リソースは比較的自由ですが今回はリージョンとアカウントまでは絞っておきます。そしてリクエスト条件を展開して「条件の追加」をします。

リクエスト条件の追加では、条件キーとしてaws:ResourceTagを選択します。これは操作する対象に付与されたタグを条件にします。条件キーは候補がたくさんあって、グローバルの条件キーとサービスレベルの条件キーという2つの種類に分類されてリストがソートされているのと、似たような名前の条件キー(aws:RequestTagなど)があるので注意して下さい。TagKeyはリソースに付けられた、条件にしたいタグのKeyです。今回はProjectというタグを利用するためProjectと入力します。限定条件は対象のリソースによって変えますが今回はデフォルトのまま。演算子は一致であるStringEqualsにします。そして値はポリシー変数である${aws:PrincipalTag/タグキー}を利用します。ポリシー変数はIAMポリシーを記述する際に活用できる変数です。詳細はこちらaws:PrincipalTagはプリンシパル、つまりAPIを実行するIAMなどの認証情報側のタグを意味していて、今回は${aws:PrincipalTag/Project}とすることで値をプリンシパルに付いているProjectタグの値を利用することができます。今回はリソース側のタグとプリンシパルのタグを同じProjectとしていますが、別々のものを利用することもできます。

ついでに今回はすべてのSecurity Groupが見れるようにDescribeSecurityGroupsも条件を付けずに追加しておきます。

ここまでのポリシーをjsonにすると以下のようになっています。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "ec2:RevokeSecurityGroupIngress",
                "ec2:AuthorizeSecurityGroupIngress"
            ],
            "Resource": "arn:aws:ec2:ap-northeast-1:999999999999:security-group/*",
            "Condition": {
                "StringEquals": {
                    "aws:ResourceTag/Project": "${aws:PrincipalTag/Project}"
                }
            }
        },
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": "ec2:DescribeSecurityGroups",
            "Resource": "*"
        }
    ]
}

適当な名前を(今回はAllowEditSecurityGroupABACPolicy)付けて作成します。

IAMユーザーへポリシーをアタッチ

続いて作成したIAMポリシーをIAMユーザーへアタッチします。もちろんIAMグループやRoleでも大丈夫ですが、今回はユーザーで簡単に試します。

適当にproject-a-userという名前のユーザーを作ります。作成時に先ほど作成したIAMポリシーを直接アタッチします。IAMユーザーへの直接アタッチはベストプラクティスではないので、ちゃんとやるときはグループを使って下さい。

そしてIAMユーザーのタグとしてProject: Project-Aを付与します。後ほどこれと同じものをSecurity Groupにも付与します。これでユーザー作成を完了します。

Security Groupの作成

操作の対象となるSecurity Groupを作成します。作成時に前述の通りこちらにもタグとしてProject: Project-Aを付与します。これで準備は整いました。

インバウンドルールの追加

実際にABACを利用したインバウンドルールの追加をやってみます。

作成したユーザーでログインした後、SSHをマイIPで指定して作成してみます。

成功しました。

これだけだと本当に制御されているか分かりづらいので、元の権限でログインし直してIAMユーザーのProjectタグをProject-Bにしてみます。ユーザー名はproject-a-userだけどタグはProject-Bなんて、実環境では絶対にやめたほうがいい設定ですが。

再度作成したユーザーでログインして今度はHTTPのマイIPを追加してみます。

エラーとなり失敗しました。これで、タグが適切に評価されてABACが実現できていることが確認できました。

まとめ

ABACを利用してSecurity GroupのIPアドレス設定をユーザーに任せつつ、制御をタグで行う方法を紹介しました。リモートワークでのIPアドレス管理が少し楽になると思います。

もちろんSecurity Group以外のリソースもABACで制御ができます。是非試してみて下さい。

ABACの条件をより詳細に理解したい場合は以下を参考にして下さい。読み応えがあります。

また、IPアドレスの設定を任せる場合にはうっかり0.0.0.0/0からのアクセスを許可しないよう周知徹底しつつ、以下のようなチェックや自動修復、あるいはポリシーで禁止するなども合わせて検討するといいでしょう。