はじめに
今さらながら、AWS の Service-Linked Role(SLR)を初めて知りました。
このロールが陰ながら支えてくれていたことに、感謝しています。
一方で先日、このロールの影響でエラーが出ました。
同じ轍を踏んでほしくないと思い、記事にしました。
概要
- SLR は、サービスの権限を利用者に壊させない仕組みです。その分、消えにくい性質があります。
- 本番の
applyが、regional NAT Gateway の作成で 403 になりました。 - 足りなかったのは、SLR を作る IAM の権限でした。
- ステージ環境で通ったのは、コンソール操作で SLR がすでに作られていたからでした。
- Terraform を実行するデプロイロールに、SLR の作成権限を足して解決しました。
事象: ステージ環境で通った terraform apply が、本番で 403 になった
前提の構成
あるプロジェクトの本番デプロイでの話です。
使用しているバージョンは次のとおりです。
| ツール | バージョン |
|---|---|
| Terraform | 1.15 |
| hashicorp/aws | 6.47.0 |
新しく作るリソースに、regional NAT Gateway が含まれていました。
regional NAT Gateway の詳細は、AWS 公式ドキュメントを参照してください。
Terraform では、availability_mode に regional を指定します。
この引数は、hashicorp/aws 6.24.0 で追加されました。
# Terraform AWS provider のドキュメントの例(auto mode)
resource "aws_nat_gateway" "example" {
vpc_id = aws_vpc.example.id
availability_mode = "regional"
}
出たエラー
本番で apply を流すと、次のエラーで止まりました。
Error: creating EC2 NAT Gateway: operation error EC2: CreateNatGateway,
https response error StatusCode: 403, RequestID: XXXXXXXXXX,
api error UnauthorizedOperation: Account XXXXXXXXXX is not authorized
to create service-linked role for regional NAT Gateways.
「service-linked role を作成する権限が無いです」とのことでした。
Service-Linked Role(SLR)ってなんだ?
ステージ環境ではちゃんと動いたのになぜ?
ということで調べました。
Service-Linked Role(SLR)とは
SLR は、AWS のサービスが自分で使う IAM ロールです。
SLR は、サービスを初めて使うときに作られます。
今回は、regional NAT Gateway の作成時に、AWS が SLR を自動で作ろうとします。
その SLR が AWSServiceRoleForNATGateway です。
この SLR を作るには、iam:CreateServiceLinkedRole の権限が必要でした。
権限が要るのは、API を呼ぶ IAM ロールで、今回では Terraform の apply を実行するロール (デプロイロール)です。
SLR が必要なのは NAT Gateway だけではない
SLR を使うサービスは、ほかにもたくさんあります。
たとえば、次のようなものです。
| サービス | SLR の名前 |
|---|---|
| Elastic Load Balancing | AWSServiceRoleForElasticLoadBalancing |
| EC2 Auto Scaling | AWSServiceRoleForAutoScaling |
| Amazon RDS | AWSServiceRoleForRDS |
| Amazon ECS | AWSServiceRoleForECS |
SLR を使うサービスの一覧は、IAM と連携する AWS のサービスで確認できます。
表の「Service-linked roles」列が「Yes」のものが対象です。
なぜ SLR という仕組みが必要なのか
regional NAT Gateway は、裏で API を呼ぶ
regional NAT Gateway は、作ったあとも自分で動きます。
AWS のドキュメントによると、SLR で次のことをします。
- 負荷に応じて Elastic IP を割り当て、自動でスケールする
- 指定された既存の Elastic IP を付け外しする
- ネットワークインターフェースを調べ、新しい AZ へ自動で広がる
どれも、自分のアカウントの中で API を呼ぶ操作です。
API を呼ぶには、IAM の権限を持った主体が要ります。
その主体が SLR です。
SLR は、サービスが持ち主のロール
SLR は、普通の IAM ロールとは扱いが違います。
| 項目 | SLR | 自分で作るサービスロール |
|---|---|---|
| 権限を決めるのは | AWS のサービス | 自分 |
| 引き受けられるのは | そのサービスだけ | 信頼ポリシー次第 |
| 権限の編集 | できない(説明文のみ可) | できる |
| 削除 | 関連リソースを消すまでできない | いつでもできる |
NAT Gateway の SLR の中身は次のとおりです。
- 名前:
AWSServiceRoleForNATGateway - 引き受けられるのは
ec2-nat-gateway.amazonaws.comだけ - 権限は
AWSNATGatewayServiceRolePolicyで定義済み
無ければどうなるか
サービスに権限を渡すには、自分でロールを作ることになります。
信頼ポリシー・権限ポリシーも、自分で書きます。
自分で作ったロールは自分で壊せます。
権限を削りすぎたり、誤って消したりできます。
すると regional NAT Gateway は、動いている最中にスケールできなくなると考えられます。
SLR は、この「うっかり壊す」を仕組みで防ぎます。
権限は AWS が決めて、利用者は編集できません。
全リージョンの regional NAT Gateway を消すまで、削除もできません。
SLR は「サービスが動き続けるための権限」を、利用者の手から切り離す仕組みです。その代わり、最初に作る瞬間だけは利用者の権限に頼ります。
逆に、SLR が作れないと regional NAT Gateway そのものが作れません。
今回の 403 が、まさにその状態です。
原因: ステージ環境で通った理由
ステージ環境では、リリースリハーサルの前に検証をしていました。
そのとき、コンソールから regional NAT Gateway を一度作っていました。
この操作で、SLR もアカウントに作られていました。
SLR は、一度あれば作り直されません。
リハーサルの時点で、SLR はすでにありました。
そのため、本番と同じロール・ポリシーでもエラーは出ませんでした。
コンソールで NAT Gateway を作った人には、SLR を作る権限がありました。
SLR は、その人の権限で黙って作られます。
手動操作の影響で、デプロイロールの不足を隠していました。
解決策: デプロイロールに SLR の作成権限を足す
デプロイロールに、次の権限を追加しました。
{
"Sid": "IamCreateNatGatewaySlr",
"Effect": "Allow",
"Action": "iam:CreateServiceLinkedRole",
"Resource": "arn:aws:iam::XXXXXXXXXXXX:role/aws-service-role/ec2-nat-gateway.amazonaws.com/AWSServiceRoleForNATGateway",
"Condition": {
"StringLike": {
"iam:AWSServiceName": "ec2-nat-gateway.amazonaws.com"
}
}
}
ポイントは3つです。
-
Actionは、"iam:CreateServiceLinkedRole"。"iam:CreateRole"ではないので注意 -
Resourceで、作れる SLR を NAT Gateway のものだけに絞る- SLR のパスは
role/aws-service-role/...になる
- SLR のパスは
-
Conditionで、対象のサービス名も NAT Gateway に限る
権限を足したあと、本番の apply は最後まで成功しました。
再発防止
デプロイ前に SLR の有無を確かめる
アカウントに SLR があるかは、次のコマンドで分かります。
aws iam get-role --role-name AWSServiceRoleForNATGateway
無ければ NoSuchEntity のエラーになります。
そのアカウントでは、apply が初めて SLR を作ります。
デプロイロールに作成権限があるかを、先に確認できます。
新しいサービス・機能の初回 apply では SLR を確認する
SLR は、サービスや機能を初めて使うときに作られます。
今回の regional NAT Gateway も、本番では初めて使う機能でした。
そこで、新しいサービス・機能の初回 apply 前に、次を確認します。
- そのサービス・機能が SLR を使うか
- 本番アカウントに、その SLR がすでにあるか
- 無い場合、デプロイロールに作成権限があるか
SLR を Terraform で管理する方法もある
SLR は、aws_iam_service_linked_role で明示的に作れます。
resource "aws_iam_service_linked_role" "nat_gateway" {
aws_service_name = "ec2-nat-gateway.amazonaws.com"
lifecycle {
prevent_destroy = true
}
}
resource "aws_nat_gateway" "example" {
vpc_id = aws_vpc.example.id
availability_mode = "regional"
depends_on = [aws_iam_service_linked_role.nat_gateway]
}
こう書くと、SLR の作成が plan に出ます。
レビューの時点で、SLR が作られると分かります。
ただし、次の点に注意が要ります。
- 作成権限(
iam:CreateServiceLinkedRole)は、引き続き必要 - SLR がすでにあるアカウントでは、
importが要る - この SLR はアカウントに1つなので、管理する場所を1か所にする
- 誤って消さないよう、
prevent_destroyを付ける
今回のプロジェクトでは、IAM を Terraform で管理していないため、この方法は使っていません。
自分への問い
この件の前にも、同じ形の見落としがありました。
ステージ環境では運よく成功し、本番で初めて問題が出る形です。
そこで、次の問いを持つことにしました。
この環境は、まっさらな本番と同じ状態か
まとめ: 同じ目に遭わないために
- EC2 の API が 403 でも、メッセージに service-linked role とあれば IAM の権限を見る
- SLR を作る権限は、API を呼んだ側に要る。デプロイロールも例外ではない
- 権限は
ResourceとConditionで、必要な SLR だけに絞って渡す - ステージ環境の成功が、過去の手動操作に影響されていないかを疑う
- 新しいサービス・機能を初めて
applyするときは、SLR が要るかを確認する