【AWS】IAM ユーザーに IP 制限

はじめに

AdministratorAccess

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "*",
            "Resource": "*"
        }
    ]
}

こんな権限を持った IAM ユーザーのアクセスキーがインターネットに流出したら、いやですよね。

そもそも、そんな権限を付与するなよっていう話ですが、 Serverless 開発とかやっているとカジュアルに AdministratorAccess を求められることがよくあるし、なんだかんだ AmazonEC2FullAccess やら AmazonS3FullAccess やらが必要になる場面はザラにある。

かといって、下記のように単純な IAM ユーザーに IP 制限 (aws:SourceIp 条件) をかけると、マネジメントコンソールからの操作が謎に失敗するなど正常なオペレーションにも支障が出る。

{
  "Version": "2012-10-17",
  "Statement": {
    "Effect": "Deny",
    "Action": "*",
    "Resource": "*",
    "Condition": {"NotIpAddress": {"aws:SourceIp": [
      "192.168.1.0/24"
    ]}}
  }
}

aws:SourceIp 条件キーは、ユーザーに代わって呼び出しを実行する AWS CloudFormation などの AWS サービスへのアクセスを拒否します。
AWS: 送信元 IP に基づいて AWS へのアクセスを拒否する – AWS Identity and Access Management

解決策

「IAM ユーザーによる AWS の API 呼び出し権限に対して IP 制限を設定」

するのではなく、

「IAM ユーザーが特権的な IAM ロールを引き受ける (AssumeRole する) ことに対して IP 制限を設定」

すればよいらしい。

・長期のアクセスキーの代わりに一時的なセキュリティ認証情報 (IAM ロール) を使用する
……IAM ロールや AWS Security Token Service の他の機能を通して取得した一時的なセキュリティ認証情報は、短期間で期限切れとなります。認証情報が誤って開示された場合のリスクに備えて、一時的なセキュリティ認証情報を使用することができます。
AWS アクセスキーを管理するためのベストプラクティス – アマゾン ウェブ サービス

部外者 => NG

中の人 => 特権ロール利用可

一時的な認証情報を利用して特権ロールを使用

方法

特権ロールを使用する (特権ロールを引き受ける) IAM ユーザーの作成

CLI から使用する (プログラムからのアクセス) 想定で IAM ユーザーを追加。

アタッチするポリシーは新規作成。

とりあえず、特定 IP 以外を全拒否するポリシーを設定する。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "SourceIPRestriction",
            "Effect": "Deny",
            "Action": "*",
            "Resource": "*",
            "Condition": {
                "NotIpAddress": {
                    "aws:SourceIp": [
                        "xxx.xxx.xxx.xxx/32"
                    ]
                }
            }
        }
    ]
}

AllowAssumeRoleWithSourceIPRestriction というポリシー名で作成。

作成した AllowAssumeRoleWithSourceIPRestriction をアタッチ。

作成した IAM ユーザーのアクセスキーを忘れずに保存。

特権 IAM ロール (引き受けられるロール) の作成

信頼されたエンティティとして、とりあえず、自分の AWS アカウント ID の数字 12 桁を指定 (あとで変更する)。
必要に応じて MFA を必須にするとより安全。

特権的なポリシーをアタッチ。
ここでは AdministratorAccess を選択。

ロール名 AssumedAdministratorAccessRole として作成。

信頼関係を編集。

特定 IP から、先に作成した IAM ユーザーがこのロールを引き受けられるように設定。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::xxxxxxxxxxxxx:user/test-user"
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "IpAddress": {
          "aws:SourceIp": "xxx.xxx.xxx.xxx/32"
        }
      }
    }
  ]
}

信頼関係の表示が変わることを確認。

AllowAssumeRoleWithSourceIPRestriction ポリシーの編集

特定 IP のみ特権ロールの AssumeRole を許可する。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AllowAssumeRole",
            "Effect": "Allow",
            "Action": "sts:AssumeRole",
            "Resource": "arn:aws:iam::xxxxxxxxxx:role/AssumedAdministratorAccessRole"
        },
        {
            "Sid": "SourceIPRestriction",
            "Effect": "Deny",
            "Action": "*",
            "Resource": "*",
            "Condition": {
                "NotIpAddress": {
                    "aws:SourceIp": [
                        "xxx.xxx.xxx.xxx/32"
                    ]
                }
            }
        }
    ]
}

AWSCLI での設定と使い方

参考: ロールを割り当てる – AWS Command Line Interface

~/.aws/credentials

IAM ユーザーのアクセスキーを設定。

[test-user]
aws_access_key_id = xxxxxxxxxxxxxxxxx
aws_secret_access_key = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

~/.aws/config

[profile test-user]
region = ap-northeast-1
output = json

[profile admin-access]
region = ap-northeast-1
output = json
role_arn = arn:aws:iam::xxxxxxxxxxxx:role/AssumedAdministratorAccessRole
source_profile = test-user

使い方

こんな感じ。
AWS CLI が AWS STS からの一時的セキュリティ認証情報の取り回しを勝手にうまいことやってくれる。

$ aws s3 --profile admin-access ls
2014-10-01 15:17:33 test
2014-11-21 17:51:43 prod
.
.
.

権限ないのでこっちは無理。

$ aws s3 --profile test-user ls

An error occurred (AccessDenied) when calling the ListBuckets operation: Access Denied

許可済み IP 以外からのアクセスも弾かれる。

$ aws --profile admin-access s3 ls

An error occurred (AccessDenied) when calling the AssumeRole operation: User: arn:aws:iam::xxxxxxxxxx:user/zzzzzzzzzzzzz is not authorized to perform: sts:AssumeRole on resource: arn:aws:iam::xxxxxxxxxxxx:role/AssumedAdministratorAccessRole with an explicit deny

AWS SDK 周りでの使い方

Serverless Framework など、AWS SDK を使っているコマンドは AWS_SDK_LOAD_CONFIG=1 のような変数を定義すれば大丈夫そう。

# serverless framework
$ AWS_SDK_LOAD_CONFIG=1 sls deploy -v

# gulp-awspublish
$ AWS_SDK_LOAD_CONFIG=1 gulp publish

おわりに

これで、認証情報流出からの破滅に震えながら眠る夜が少しでも減らせそう。