0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

S3 Endpoint Policy で yum が通らないときの確認と対策

0
Posted at

背景・目的

インターネットに疎通できない VPC 上の EC2 インスタンスで、SSM Patch Manager によるパッチ自動更新が失敗していました。

原因を調査したところ、S3 Gateway Endpoint の Endpoint Policy に設定された aws:PrincipalAccount 条件が、OS のパッケージマネージャ(yum)による anonymous リクエストを拒否していることがわかりました。

本記事では、SSM Agent の通信アーキテクチャを整理し、なぜこの問題が起きるのか、どう対策するかをまとめます。

まとめ

項目 内容
事象 SSM Patch Manager でパッチ適用が失敗する
原因 S3 Gateway Endpoint の aws:PrincipalAccount 条件が yum の anonymous リクエストを拒否
根本理由 yum は AWS 署名なし(anonymous HTTP)でリポジトリにアクセスするため、IAM の Principal 条件が評価不能
対策 Endpoint Policy にリポジトリバケットへの条件なし許可を追加
影響範囲 パッチ取得のみ。SSM Agent 自体の通信(コマンド受信・セッション等)は IAM 認証付きなので影響なし

概要

登場人物の整理

AWS Systems Manager(SSM)は EC2 の運用管理サービスの総称で、その中に複数の機能があります。本記事で関係するのは以下の 2 つです。

SSM Agent SSM Patch Manager
何か マネージドノード上で動くソフトウェア(デーモン) AWS Systems Manager の機能/サービス
役割 SSM サービスからの指示を受けて実行する「手足」 パッチ適用を管理・指示する「司令塔」
動く場所 EC2、オンプレミスサーバー、エッジデバイス AWS マネジメントコンソール / API
Patch Manager(クラウド側)
  「このベースラインに従ってパッチを当てろ」
        │
        ▼ AWS-RunPatchBaseline コマンドを送信
SSM Agent(EC2 上)
  「了解、スクリプト取得して yum update 実行します」
        │
        ▼
OS (yum/dnf)
  「パッケージダウンロードします」

SSM Agent

AWS Systems Manager Agent(SSM Agent)は、EC2 インスタンスやオンプレミスサーバー上にインストールされる Amazon のソフトウェアです。Systems Manager サービスからのリクエストを処理し、実行結果を返送します。Amazon Linux 2/2023 の AMI にはプリインストールされています。

SSM Agent のアーキテクチャ

SSM Agent は EC2 上で動作するデーモンで、全ての通信をアウトバウンドで開始します。インバウンドポートは一切不要です。

┌──────────┐          HTTPS (443) アウトバウンドのみ
│   EC2    │ ──────────────────────────────────────→  AWS Systems Manager
│(SSM Agent)│ ←── ロングポーリングでコマンド受信
└──────────┘

クライアントから EC2 にアクセスする流れ(Session Manager)

① クライアント(AWS CLI / Console)
   → AWS Systems Manager にセッション開始をリクエスト
        │
        ▼
② AWS Systems Manager が EC2 上の Agent 向けにセッション情報を作成
        │
        ▼
③ EC2 上の SSM Agent がポーリングでセッション開始を検知
        │
        ▼
④ SSM Agent が ssmmessages endpoint 経由で WebSocket チャネルを確立
        │
        ▼
⑤ クライアント ↔ AWS Systems Manager ↔ SSM Agent で双方向通信成立

SSH との違い: SSH はインバウンド 22 番ポートが必要だが、Session Manager は SSM Agent が常にアウトバウンドでポーリングしているため、外からポートを開ける必要がない。

プライベートサブネットで必要な VPC Endpoints

VPC Endpoint タイプ 用途
com.amazonaws.<region>.ssm Interface SSM API(登録、Association 取得)
com.amazonaws.<region>.ec2messages Interface コマンド受信(ロングポーリング)
com.amazonaws.<region>.ssmmessages Interface Session Manager / 結果返送(WebSocket)
com.amazonaws.<region>.s3 Gateway パッチスクリプト・リポジトリアクセス

Patch Manager

Patch Manager は「いつ・何を・どのインスタンスに」パッチを当てるかを決める仕組みで、実際の実行は SSM Agent が担います。SSM Agent は Patch Manager 以外にも Run Command や Session Manager など、SSM の様々な機能の実行エンジンとして動きます。

※ 本記事では EC2(クラウド上)の構成に限定して説明します。

Patch Manager 実行時の通信フロー

① SSM Agent → ec2messages endpoint
   「新しいコマンドある?」(ポーリング)

② SSM Service → SSM Agent
   「AWS-RunPatchBaseline を実行せよ」

③ SSM Agent → S3 (aws-patch-manager-<region>-xxx)
   「パッチ適用スクリプト(AWS-RunPatchBaseline の実行コード)をダウンロード」
   → インスタンスプロファイルの認証情報で署名付きリクエスト ✅

④ SSM Agent → S3 (patch-baseline-snapshot-<region>)
   「パッチベースライン(どのパッチを当てるかのルール定義)を取得」
   → インスタンスプロファイルの認証情報で署名付きリクエスト ✅

⑤ SSM Agent が OS の yum/dnf を呼び出し

⑥ yum → S3 (amazonlinux-2-repos-<region>)
   「RPM パッケージをダウンロード」
   → ★ anonymous HTTP(署名なし)★ ← ここが失敗する

⑦ SSM Agent → ssmmessages endpoint
   「パッチ適用結果を報告」

実践

事前準備

環境を構築します

┌─────────────────────────────────────────────────────────────────────────────┐
│ VPC: vpc-XXXXXXXXXXXXXXXXX (10.0.0.0/16)                                    │
│ ※ Internet Gateway なし / NAT Gateway なし                                    │
│                                                                             │
│  ┌────────────────────────────────────────────────────────────────────────┐ │
│  │ Private Subnet: subnet-XXXXXXXXXXXXXXXXX (10.0.0.0/16, ap-northeast-1a)│ │
│  │                                                                        │ │
│  │  ┌──────────────────┐      ┌──────────────────────────────────────┐   │ │
│  │  │ EC2              │      │ Interface Endpoints (ENI)            │   │ │
│  │  │ i-XXXXXXXX│      │                                      │   │ │
│  │  │                  │ 443  │ eni: ssm.ap-northeast-1              │   │ │
│  │  │ Amazon Linux 2   │─────→│ eni: ssmmessages.ap-northeast-1      │   │ │
│  │  │ SSM Agent        │      │ eni: ec2messages.ap-northeast-1      │   │ │
│  │  │ t3.micro         │      │                                      │   │ │
│  │  │                  │      │ SG: inbound 443 from 10.0.0.0/16     │   │ │
│  │  └────────┬─────────┘      └──────────────────────────────────────┘   │ │
│  │           │                                                            │ │
│  └───────────┼────────────────────────────────────────────────────────────┘ │
│              │                                                              │
│              │ Route Table → prefix list (S3)                               │
│              ▼                                                              │
│  ┌─────────────────────────────────────────────────────────────────┐       │
│  │ S3 Gateway Endpoint: vpce-XXXXXXXXXXXXXXXXX                     │       │
│  │                                                                 │       │
│  │ Policy:                                                         │       │
│  │   Allow s3:* IF aws:PrincipalAccount == XXXXXXXXXXXX            │       │
│  │                                                                 │       │
│  │   → IAM 認証付きリクエスト(SSM Agent)  ✅ 通る                    │       │
│  │   → anonymous リクエスト(yum)          ❌ 拒否される              │       │
│  └─────────────────────────────────────────────────────────────────┘       │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘
                              │
                              ▼
                ┌───────────────────────────┐
                │ Amazon S3                 │
                │                           │
                │ • aws-patch-manager-*     │ ← IAM認証 ✅
                │ • patch-baseline-snapshot-*│ ← IAM認証 ✅
                │ • amazonlinux-2-repos-*   │ ← anonymous ❌
                └───────────────────────────┘

1.CDKテンプレートを作成します
2.CDKデプロイします

npx cdk deploy

3.リソースを確認します。(内容は簡略化しています)

-- VPC

aws ec2 describe-vpcs --vpc-ids vpc-065e465889045133c --region ap-northeast-1 --output json

{
  "VpcId": "vpc-065e465889045133c",
  "CidrBlock": "10.0.0.0/16",
  "IsDefault": false,
  "State": "available"
}


-- サブネット
aws ec2 describe-subnets --filters \"Name=vpc-id,Values=vpc-065e465889045133c\" --region ap-northeast-1 --output json
{
  "SubnetId": "subnet-0fe02d4a96b4b602b",
  "CidrBlock": "10.0.0.0/16",
  "AvailabilityZone": "ap-northeast-1a",
  "MapPublicIpOnLaunch": false
}

---GW
aws ec2 describe-internet-gateways --filters \"Name=attachment.vpc-id,Values=vpc-065e465889045133c\" --region ap-northeast-1 --output json

aws ec2 describe-nat-gateways --filter \"Name=vpc-id,Values=vpc-065e465889045133c\" --region ap-northeast-1 --output json


{ "InternetGateways": [] }
{ "NatGateways": [] }
---VPCe

aws ec2 describe-vpc-endpoints --filters \"Name=vpc-id,Values=vpc-065e465889045133c\" --region ap-northeast-1 --output json

[
  {
    "VpcEndpointId": "vpce-XXXXXXXXXXXXXXXXXX",
    "ServiceName": "com.amazonaws.ap-northeast-1.s3",
    "VpcEndpointType": "Gateway",
    "State": "available",
    "PolicyDocument": {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Principal": { "AWS": "*" },
          "Action": "s3:*",
          "Resource": "*",
          "Condition": {
            "StringEquals": {
              "aws:PrincipalAccount": "{アカウントID}"
            }
          }
        }
      ]
    }
  },
  {
    "VpcEndpointId": "vpce-XXXXXXXXXXXXXXXXXX",
    "ServiceName": "com.amazonaws.ap-northeast-1.ssm",
    "VpcEndpointType": "Interface",
    "State": "available",
    "PrivateDnsEnabled": true
  },
  {
    "VpcEndpointId": "vpce-XXXXXXXXXXXXXXXXXX",
    "ServiceName": "com.amazonaws.ap-northeast-1.ssmmessages",
    "VpcEndpointType": "Interface",
    "State": "available",
    "PrivateDnsEnabled": true
  },
  {
    "VpcEndpointId": "vpce-XXXXXXXXXXXXXXXXXX",
    "ServiceName": "com.amazonaws.ap-northeast-1.ec2messages",
    "VpcEndpointType": "Interface",
    "State": "available",
    "PrivateDnsEnabled": true
  }
]

Phase 1: 原因の特定

1-1. EC2 上で yum を実行して切り分け

まず EC2 に Session Manager で接続し、yum を実行して事象を再現します。

  1. AWSにサインインし、EC2に移動します

  2. 作成されたEC2にSSMで接続します
    image.png

  3. sudo yum update --securityを実行します。(想定通り、失敗します)

sh-4.2$ sudo yum update --security
Loaded plugins: extras_suggestions, langpacks, priorities, update-motd
Could not retrieve mirrorlist https://amazonlinux-2-repos-ap-northeast-1.s3.dualstack.ap-northeast-1.amazonaws.com/2/core/latest/x86_64/mirror.list error was
14: HTTPS Error 403 - Forbidden


 One of the configured repositories failed (Unknown),
 and yum doesn't have enough cached data to continue. At this point the only
 safe thing yum can do is fail. There are a few ways to work "fix" this:

     1. Contact the upstream for the repository and get them to fix the problem.

     2. Reconfigure the baseurl/etc. for the repository, to point to a working
        upstream. This is most often useful if you are using a newer
        distribution release than is supported by the repository (and the
        packages for the previous distribution release still work).

     3. Run the command with the repository temporarily disabled
            yum --disablerepo=<repoid> ...

     4. Disable the repository permanently, so yum won't use it by default. Yum
        will then just ignore the repository until you permanently enable it
        again or use --enablerepo for temporary usage:

            yum-config-manager --disable <repoid>
        or
            subscription-manager repos --disable=<repoid>

     5. Configure the failing repository to be skipped, if it is unavailable.
        Note that yum will try to contact the repo. when it runs most commands,
        so will have to try and fail each time (and thus. yum will be be much
        slower). If it is a very temporary problem though, this is often a nice
        compromise:

            yum-config-manager --save --setopt=<repoid>.skip_if_unavailable=true

Cannot find a valid baseurl for repo: amzn2-core/2/x86_64
sh-4.2$

https://amazonlinux-2-repos-ap-northeast-1.s3.dualstack.ap-northeast-1.amazonaws.com/2/core/latest/x86_64/mirror.listから、S3 上のリポジトリにアクセスできていないことがわかります。
この VPC はインターネット疎通がないため、S3 へのアクセスは Gateway Endpoint 経由のはずです。

1-2. S3 Gateway Endpoint Policy を確認

現状の設定を特定します

aws ec2 describe-vpc-endpoints \
  --filters "Name=service-name,Values=com.amazonaws.ap-northeast-1.s3" \
  --query "VpcEndpoints[*].{VpcId:VpcId,VpcEndpointId:VpcEndpointId,PolicyDocument:PolicyDocument}"

確認結果(dms-network-vpc に紐づく Endpoint):

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:*",
      "Resource": "*",
      "Condition": {
        "StringEquals": {
          "aws:PrincipalAccount": "XXXXXXXXXXXX"
        }
      }
    }
  ]
}

aws:PrincipalAccount 条件があります。yum は AWS 署名なし(anonymous)でリポジトリにアクセスするため、 この条件で拒否されている可能性が高いと仮説を立てます。

1-3.仮説の検証:条件を一時的に外して yum を再実行

Endpoint Policy から条件を外して再度 yum を実行します。

1.下記のコマンドで条件を外します

aws ec2 modify-vpc-endpoint --vpc-endpoint-id vpce-XXXXXXXXXXX --policy-document \"{\\\"Version\\\":\\\"2012-10-17\\\",\\\"Statement\\\":[{\\\"Effect\\\":\\\"Allow\\\",\\\"Principal\\\":\\\"*\\\",\\\"Action\\\":\\\"s3:*\\\",\\\"Resource\\\":\\\"*\\\"}]}\" --region ap-northeast-1
{
}

2.変更後の状態を確認します。aws:PrincipalAccount 条件が外れています。

aws ec2 describe-vpc-endpoints --vpc-endpoint-ids vpce-03e623bfd28c4da25 --query \"VpcEndpoints[0].PolicyDocument\" --region ap-northeast-1

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

3.再びセッションマネージャーで試します。成功しました

$ sudo yum update --security
sh-4.2$ sudo yum update --security
Loaded plugins: extras_suggestions, langpacks, priorities, update-motd
amzn2-core                                                                                                                                                                                  | 3.6 kB  00:00:00
amzn2extra-docker                                                                                                                                                                           | 2.9 kB  00:00:00
amzn2extra-kernel-5.10                                                                                                                                                                      | 3.0 kB  00:00:00
(1/7): amzn2-core/2/x86_64/group_gz                                                                                                                                                         | 2.7 kB  00:00:00
(2/7): amzn2-core/2/x86_64/updateinfo                                                                                                                                                       | 1.3 MB  00:00:00
(3/7): amzn2extra-docker/2/x86_64/updateinfo                                                                                                                                                |  36 kB  00:00:00
(4/7): amzn2extra-kernel-5.10/2/x86_64/updateinfo                                                                                                                                           | 201 kB  00:00:00
(5/7): amzn2extra-docker/2/x86_64/primary_db                                                                                                                                                | 162 kB  00:00:00
(6/7): amzn2extra-kernel-5.10/2/x86_64/primary_db                                                                                                                                           |  46 MB  00:00:00
(7/7): amzn2-core/2/x86_64/primary_db                                                                                                                                                       |  85 MB  00:00:01
No packages needed for security; 0 packages available
No packages marked for update
sh-4.2$

→ 成功。Endpoint Policy の aws:PrincipalAccount 条件が原因と確定しました。

1-4. なぜこのポリシーで失敗するのか

リクエスト元 認証方式 aws:PrincipalAccount 評価 結果
SSM Agent(スクリプト取得) IAM 認証(SigV4 署名あり) {AWSアカウントID} → 条件一致 ✅ 許可
yum(パッケージ取得) anonymous(署名なし) Principal 情報なし → 条件評価不能 ❌ 暗黙的 Deny

Amazon Linux のパッケージリポジトリは S3 上のパブリックバケットにホストされています。yum update は AWS SDK を使わず素の HTTP リクエストでパッケージを取得するため、AWS 署名が付きません。

Phase 2: 対策の実施

2-1. 対策案の比較

内容 メリット デメリット
① VPC を変更 IGW/NATGWを保有するVPC に移行 標準構成に統一できる。許されるなら 既存作業への影響大
② Endpoint Policy を修正 anonymous アクセスを許可する Statement を追加 影響範囲が小さい Endpoint Policy の管理が必要

今回は 案② を採用します。

2-2. 修正後の Endpoint Policy

既存の条件付き Statement はそのまま残し、yum リポジトリへの anonymous アクセスのみ許可する Statement を追加します。

  1. CDKを修正し、デプロイします
  2. 設定変更後の確認をします。条件付き + リポジトリ許可の 2 Statement 構成になっています。
aws ec2 describe-vpc-endpoints --vpc-endpoint-ids vpce-XXXXXXXXXX --query \"VpcEndpoints[0].PolicyDocument\" --region ap-northeast-1

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowAccountPrincipals",
      "Effect": "Allow",
      "Principal": { "AWS": "*" },
      "Action": "s3:*",
      "Resource": "*",
      "Condition": {
        "StringEquals": {
          "aws:PrincipalAccount": "アカウントID"
        }
      }
    },
    {
      "Sid": "AllowAnonymousPatchRepoAccess",
      "Effect": "Allow",
      "Principal": { "AWS": "*" },
      "Action": "s3:GetObject",
      "Resource": [
        "arn:aws:s3:::al2023-repos-ap-northeast-1-de612dc2/*",
        "arn:aws:s3:::amazonlinux-2-repos-ap-northeast-1/*"
      ]
    }
  ]
}

Phase 3: 動作確認

  1. コマンドを打って到達するか確認します。問題なく通りました
sh-4.2$ sudo yum update --security
Loaded plugins: extras_suggestions, langpacks, priorities, update-motd
amzn2-core                                                                                                                                                                                  | 3.6 kB  00:00:00
No packages needed for security; 0 packages available
No packages marked for update
sh-4.2$

ポイント:

  • 既存の aws:PrincipalAccount 条件はそのまま維持
  • 追加 Statement は s3:GetObject のみ、リポジトリバケットに限定
  • OS が Amazon Linux 2 なら amazonlinux-2-repos-*、AL2023 なら al2023-repos-* が必要

考察

なぜ anonymous リクエストになるのか

SSM Patch Manager の処理は 2 層に分かれています。

  1. SSM Agent 層: AWS SDK(Go)を使って S3 からスクリプトやベースラインを取得する。IAM 認証付き。
  2. OS 層: SSM Agent が yum update 等の OS コマンドを呼び出す。yum は AWS SDK を使わず、/etc/yum.repos.d/ に定義された URL に素の HTTP でアクセスする。

この 2 層構造のため、同じ S3 Gateway Endpoint を通るにもかかわらず、認証方式が異なります。Endpoint Policy で aws:PrincipalAccount を使うと、OS 層のアクセスだけが拒否されるという非直感的な挙動になります。

Data Exfiltration リスクについて

aws:PrincipalAccount 条件を完全に外すと、VPC 内の侵害されたインスタンスが外部の S3 バケットにデータを送信できるリスクがあります。今回の対策では条件付き Statement を残し、リポジトリバケットへの GetObject のみを追加許可しているため、Data Exfiltration 対策は維持されます

SSM Agent の通信まとめ

通信先 プロトコル 認証 VPC Endpoint タイプ
ssm endpoint HTTPS/443 IAM (SigV4) Interface
ec2messages endpoint HTTPS/443 IAM (SigV4) Interface
ssmmessages endpoint HTTPS/443 (WebSocket) IAM (SigV4) Interface
S3(SSM 関連バケット) HTTPS/443 IAM (SigV4) Gateway
S3(OS リポジトリ) HTTP or HTTPS anonymous Gateway

参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?