AWSにおけるアクセス制御はIAM(API操作レベル)とSecurity Group(ネットワーク通信レベル)の2レイヤーで構成される。 アクセス先のサービスによってどちらが効くかが異なる。 それぞれの役割の違いと、Terraformで一括管理する際の共通設計パターンを解説する。 本稿はアクセス制御テーマの親記事であり、IAMとSecurity Groupに共通する設計パターンはここで説明する。個別の実装は子記事で扱う(6章)。
1. AWSにおける2つのアクセス制御レイヤー
AWSでリソース間のアクセスを制御する仕組みは、大きく2つのレイヤーに分かれる。
- IAM(Identity and Access Management): API操作レベルの認可。「誰が何をできるか」を制御する
- Security Group: ネットワーク通信レベルのフィルタリング。「誰がどこに接続できるか」を制御する
どちらのレイヤーが効くかは、アクセス先のAWSサービスによって異なる。 S3やDynamoDBのようなリージョナルサービスへのアクセスはIAMで制御される。 RDSやElastiCacheのようなVPC内に配置されるサービスへのアクセスはSecurity Groupで制御される。 一部のケース(RDS IAM認証など)では両方が関与するが、これは限定的である。
よくある混同
「IAMで許可すればアクセスできる」「Security Groupを開ければ通信できる」と単純に考えてしまうケースがある。 しかし実際には、この2つは制御する対象が異なる。
- Security Groupはネットワークパケットの到達を制御する。ポートが閉じていれば通信は届かない
- IAMはAWS APIリクエストの認可を制御する。権限がなければAPI呼び出しは拒否される
たとえばECSタスクがRDSに接続する場合、一般的なパスワード認証であればSecurity Groupの通信許可だけで接続できる。 一方、ECSタスクがS3にファイルをアップロードする場合は、IAMポリシーの許可だけで操作できる(Security Groupは関与しない)。
この2つを混同すると、「Security Groupを開けたのにS3にアクセスできない」「IAMポリシーを付けたのにRDSに接続できない」といった切り分けの混乱が生じる。
2. IAMとSecurity Groupの違い
制御レイヤーの比較
適用される場面の違い
多くの場面ではどちらか一方が主たる制御レイヤーとなる。 両方が同時に評価されるケース(RDS IAM認証等)は存在するが、限定的である。
3. 共通する設計パターン:箱の一括作成 + リソース側での詳細追加
IAMとSecurity Groupは制御レイヤーが異なるが、Terraformで管理する際の設計パターンは共通化できる。
パターンの構造
IAMでの適用
「リソース側で追加」は、リソースの種類ごとに作り分けるものではなく、共通の設計として繰り返し使う。 Lambda、ECS、CodeBuild、EventBridge、Glue、Step Functionsなど、リソースを作るTerraformの部品それぞれに「追加の権限(ポリシーJSON)を受け取る口」を持たせる。 呼び出す側は、そのリソースが実際に必要とする権限だけを、使う時点で渡す。
Security Groupでの適用
なぜこのパターンが有効か
- 一覧性: 一括管理モジュールを見れば、環境内の全ロール / 全SGが把握できる
- 可読性: リソースのTerraformを見れば、そのリソースに「誰がアクセスできるか」「どこから通信できるか」が完結する
- ライフサイクルの一致: リソース削除時に、そのリソース固有の権限 / ルールも自然に削除対象になる
- 上限の回避: ロールにアタッチできるマネージドポリシーの数には上限がある(既定20、引き上げ後の最大25)。リソース固有の権限をインラインポリシーで追加すれば、この枠を消費しない(インラインポリシーは個数ではなく合計サイズに上限がある。詳細は子記事)
4. この設計が生む効果:プライベートIP管理からの解放
「箱の一括作成 + リソース側での詳細追加」という設計には、可読性以外にもう一つ重要な効果がある。
使わなければ、穴は開かない
モジュールはデフォルトで「何も許可しない」状態の箱を作る。Security Groupは空のIngress、IAMロールは汎用ポリシーのみ。具体的な通信許可やAPI権限は、そのリソースを実際に呼び出す側が、明示的に追加しない限り発生しない。
つまり、ある接続(例: ECSからRDS)を一度も書かなければ、その接続は最初から存在しない。「念のため広めに開けておく」という判断が入る余地がない。
使う時に、必要な分だけ追加される
ALBがECSタスクにリクエストを転送する構成を作ったときだけ、ECS側のSecurity GroupにALBからのIngressが追加される。LambdaがS3を読む処理を実装したときだけ、そのLambdaのロールにs3:GetObjectが追加される。許可は常に「今まさに必要な、具体的な1つの接続・1つの操作」の単位で生成される。
繰り返し使えば、まとめられる
同じ接続が複数回必要になる場合(複数のECSサービスが同じRDSクラスタに接続する等)も、個別に新しい開口部を作るのではなく、既存の許可リスト(接続元Security Group IDのリスト、ポリシーのStatement)に追加する形で表現できる。
結果:プライベートIPアドレスがどこにも登場しない
この3つの性質を積み重ねると、アクセス制御の全体が「どのSecurity Group IDが」「どのIAMロールが」何にアクセスできるか、というグラフだけで完結する。
ECSタスクのIPアドレスがいくつであるか、どのサブネットに配置されているかは、この設計において一度も意識する必要がない。サブネット構成を変更しても、Terraformを書き直す必要がない。プロジェクト内でプライベートIPアドレスを管理する作業そのものが発生しない。
5. 独立した2レイヤーであることの意味
多くの場面ではどちらか一方が効く
実際の構成では、IAMとSecurity Groupの両方が同時に評価される場面は限定的である。
RDS IAM認証のように両方が同時に必要なケースは存在するが、一般的なパスワード認証のDB接続ではIAMは関与しない。 多くの場面では、IAMかSecurity Groupのどちらか一方が主たる制御レイヤーとなる。
それでも2つを区別して管理する理由
2つのレイヤーが独立していることは、トラブルシューティングと監査の観点で意味がある。
- 障害切り分け: 「ネットワーク的に到達できるか」と「API操作が許可されているか」を独立して検証できる。接続エラーの原因がSecurity Groupなのかポリシーなのかを切り分けやすい
- 監査対応: 「このリソースにネットワーク的に到達可能なリソースはどれか」と「このロールが操作可能なリソースはどれか」を別々に回答できる
- 設計の明確さ: 「通信経路の設計」と「権限の設計」を別の関心事として扱うことで、それぞれの変更が互いに影響しない
6. 実装パターンへの展開
本テーマの具体的な実装パターンは、以下の子記事で扱う。子記事では3〜4章の設計パターンを繰り返さず、それぞれに固有の判断に絞っている。
- IAM一括管理 — ロール・ポリシーの集約管理、マネージドポリシーとインラインポリシーの使い分け、AWSクォータを踏まえた設計
- Security Group一括管理 — SGの集約管理、Security Group ID参照による通信許可、リソース別の通信許可パターン
7. まとめ
AWSのアクセス制御は、IAMとSecurity Groupの2レイヤーで構成される。 アクセス先のサービスによってどちらが効くかが異なるため、この2つを混同せず、それぞれの役割を理解した上で、共通の設計パターンで管理することが、可読性と保守性の高いインフラ構成につながる。 さらに、この設計は「使う時にだけ、必要な分だけ許可を生成する」という性質を持つため、プロジェクト内でプライベートIPアドレスを意識して管理する作業自体が発生しない。
