プライベートサブネットからKMSへ安全にアクセスする ── VPCエンドポイントによる経路の閉じ込め

目次

概要

プライベートサブネットに置いたEC2インスタンスから、KMSで暗号化・復号を行いたい。しかしKMSはVPCの中にあるサービスではなく、kms.ap-northeast-1.amazonaws.com というパブリックなエンドポイントを持つリージョンサービスです。何も手当てをしなければ、プライベートサブネットからはこのエンドポイントに到達できません。

素直な解決策としてNATゲートウェイを置く方法がありますが、これは「インターネットへの出口を作る」ことでもあります。KMSにアクセスしたいだけなのに、インスタンスからインターネット全体へ通信できる経路ができてしまい、しかもNATゲートウェイは時間課金とデータ処理料金がかかります。

インターフェイスVPCエンドポイント(AWS PrivateLink)を使うと、KMSへの通信をAWSのネットワーク内で完結させられます。インターネットを経由せず、経路も特定のサービスだけに限定できます。さらにKMSのキーポリシーで「このVPCエンドポイント経由でなければ使わせない」という条件まで書けます。

この記事では、インターネットゲートウェイもNATも持たないVPCを舞台に、KMSエンドポイントが無い状態では通信が失敗することを確認したうえで、エンドポイントを作って疎通させ、最後にキーポリシーで経路を強制します。

NATゲートウェイ経由とインターフェイスVPCエンドポイント経由の比較(インターネットを経由するかどうか、料金と経路の違い)
NATゲートウェイ経由とインターフェイスVPCエンドポイント経由の比較(インターネットを経由するかどうか、料金と経路の違い)
クラウドおさる

KMS は VPC の外にパブリックなエンドポイントを持つリージョンサービスなので、出口の無いプライベートサブネットからは届かぬのでござる。この記事では、エンドポイントが無いと失敗し、作ると通り、キーポリシーで経路まで縛れるところを、弊猿と一緒に順に確かめるでござるな。

この記事のメリット

  • ゲートウェイ型とインターフェイス型のVPCエンドポイントの違いを説明できるようになる
  • KMSへのアクセス経路として、NATゲートウェイとVPCエンドポイントのどちらを選ぶか判断できるようになる
  • プライベートDNSが有効なエンドポイントで、アプリケーションを変更せずに経路だけ差し替えられることを理解できる
  • キーポリシーの aws:sourceVpce 条件で、KMSキーの利用経路を強制できるようになる
  • SCS試験で「プライベートサブネットからAWSサービスへ安全にアクセスする方法」を問う設問に正確に答えられるようになる

執筆者とキャラクター紹介

執筆者:土肥(とひ)

株式会社ジェニュインの代表であり元エンジニア。これまでオンプレミスと AWS 環境のインフラ業務を担当。AWS 認定資格の学習で得た知識を、ハンズオンとともに解説するのが得意。直近では新人の教育も行っており、これから技術を学ぶ方が納得できる解説を心がけています。基礎編・Cloud Practitioner の記事では、解説役としても登場します。

土肥

クラウドおさる

クラウドおさる

はじめましてでござる。弊猿はクラウドおさる。IT インフラが異常に発達した猿山から、雲に乗って地上の AWS を学びに降りてきたのでござる。

クラウドおさる

基礎編や Cloud Practitioner の記事では、とひさんに素朴な疑問をぶつける役でござるな。Security - Specialty などの記事では、要所でワンポイントや注意点をひとこと添えるでござる。

クラウドおさる

猿山にもデータセンターや監視当番があって、地上で起きることはだいたい猿山にも同じ仕組みがあるのでござる。例え話がつい猿山に寄るのは、弊猿の癖でござる。

技術解説

VPCエンドポイントの2つの型

VPCエンドポイントには型が2つあり、KMSで使うのはインターフェイス型です。

型実体対応サービス料金
ゲートウェイ型ルートテーブルに追加されるターゲットS3、DynamoDB のみ無料
インターフェイス型(PrivateLink)サブネット内に作られるENI(プライベートIP)KMS、Systems Manager、Secrets Manager など多数エンドポイント1つあたりの時間課金 + データ処理料金

インターフェイス型は「サブネットの中にそのサービス専用の入り口(ENI)ができる」と考えると理解しやすくなります。通信はそのENI宛てに行われ、AWSのネットワーク内でサービスに転送されます。

インターフェイスVPCエンドポイントの構成(プライベートサブネット内のENIとプライベートDNSによる名前解決の流れ)
インターフェイスVPCエンドポイントの構成(プライベートサブネット内のENIとプライベートDNSによる名前解決の流れ)

プライベートDNSがアプリを変えずに済ませる

インターフェイスエンドポイントを作るとき「プライベートDNS名を有効にする」を選ぶと、VPC内での kms.ap-northeast-1.amazonaws.com の名前解決が、エンドポイントのプライベートIPに変わります。

これが重要なのは、アプリケーションやAWS CLIの設定を一切変更しなくてよいという点です。コードはこれまでどおり通常のエンドポイント名で呼び出し、名前解決の結果だけが変わります。

プライベートDNSを使うには、VPCの enableDnsSupport と enableDnsHostnames が両方とも有効である必要があります。

NATゲートウェイと比べたときの利点

観点NATゲートウェイインターフェイスVPCエンドポイント
通信経路インターネットを経由するAWSネットワーク内で完結する
到達範囲インターネット全体に出られるそのサービスにだけ到達する
料金時間課金 + データ処理料金(比較的高い)時間課金 + データ処理料金(NATより低い)
監査経路の制御はルートテーブル・SG頼みエンドポイントポリシーで許可するAPIやリソースを絞れる

「インターネットに出さずに特定のサービスだけ使わせたい」という要件では、VPCエンドポイントが答えになります。設問に「コスト効率よく」「インターネットを経由せずに」といった条件が付いている場合はほぼこれです。

クラウドおさる

NAT ゲートウェイはインターネット全体への出口になるが、インターフェイスエンドポイントはそのサービスにだけ届く入り口でござる。猿山でたとえるなら、隣のバナナ倉庫へ行くだけのために山門を開け放つか、倉庫直通の通路を 1 本掘るかの違いでござるな。「インターネットを経由せずに」と言われたら、こちらを思い出すのでござる。

エンドポイントポリシーとキーポリシー

経路を絞る仕組みは2段あります。混同しやすいので整理します。

仕組み書く場所効き方
エンドポイントポリシーVPCエンドポイントそのエンドポイントを通るリクエストに対して、許可するAPIやリソースを制限する
キーポリシーの条件KMSキーそのキーへのリクエストに対して、どの経路から来たかで許可・拒否する

キーポリシー側で経路を強制するときは、aws:sourceVpce(エンドポイントID)または aws:sourceVpc(VPC ID)の条件キーを使います。

{
  "Sid": "Restrict usage to my VPC endpoint",
  "Effect": "Deny",
  "Principal": "*",
  "Action": [
    "kms:Encrypt",
    "kms:Decrypt",
    "kms:ReEncrypt*",
    "kms:GenerateDataKey*"
  ],
  "Resource": "*",
  "Condition": {
    "StringNotEquals": {
      "aws:sourceVpce": "vpce-1234abcdf5678c90a"
    }
  }
}

このポリシーには副作用があります。指定したエンドポイント以外からのリクエストがすべて拒否されるため、マネジメントコンソールからの操作や、ユーザーに代わってKMSを呼び出す他のAWSサービス(S3のSSE-KMSなど)からのリクエストも失敗します。本番のキーに適用する場合は、統合サービスからの経路を別のステートメントで許可しておく必要があります。

もう1点、VPCエンドポイント経由のリクエストでは aws:sourceIP 条件キーが効きません。送信元IPで絞っているつもりが素通りになるため、経路を絞るなら必ず aws:sourceVpce か aws:sourceVpc を使います。

フリーランス案件
クラウドおさる
クラウドキャリアフリーランス

AWS の実務経験があるなら、単価を上げにいく

AWS に特化したフリーランスエージェントです。以下は取り扱い案件の一例です。設計・SRE・セキュリティ・生成 AI の領域で、専門を掛け合わせるほど単価が上がります。

横にスクロールできます。掲載時点の情報のため、募集が終了している場合があります。条件の近い案件をご紹介しますので、お気軽にご相談ください。

実践

インターネットゲートウェイもNATも持たないVPCで、KMSへのアクセスがエンドポイントの有無でどう変わるかを確認します。

前提条件

  • リージョン: ap-northeast-1(東京)
  • 以下のリソースが作成済みであること
リソース種別リソース名用途
VPCkms-vpce-lab-vpc検証用VPC(CIDR: 10.20.0.0/16。IGW・NATなし)
プライベートサブネットkms-vpce-lab-private-subnetEC2とエンドポイントを配置(CIDR: 10.20.1.0/24)
セキュリティグループkms-vpce-lab-endpoint-sgエンドポイント用(VPC内からのHTTPSを許可)
セキュリティグループkms-vpce-lab-instance-sgEC2用(インバウンドなし)
VPCエンドポイントssm / ssmmessages / ec2messagesSession Managerで接続するため
EC2インスタンスkms-vpce-lab-instance疎通確認用。SSMとKMSの権限を持つロールを付与済み

[!NOTE] 前提環境のTerraform資材は assets/54_q91_kms-vpc-endpoint-private-access にあります。インターフェイスエンドポイントは1つあたり時間課金が発生するため、検証後は削除してください。

このハンズオンで作成するリソースは以下のとおりです。

リソース種別リソース名用途
VPCエンドポイントcom.amazonaws.ap-northeast-1.kmsKMSへのプライベート経路
KMSカスタマー管理キーvpce-lab-key経路の強制を確認するためのキー

手順1: エンドポイントが無い状態で疎通を確認する

AWSマネジメントコンソールにログインし、リージョンが ap-northeast-1(東京)であることを確認します。

上部の検索バーに EC2 と入力し、表示された「EC2」を選択します。左側ナビゲーションの「インスタンス」→「インスタンス」を選択し、kms-vpce-lab-instance を選択して「接続」をクリックします。

「接続方法を選択」で「最大限のセキュリティ」(SSM セッションマネージャー)のカードを選び、「接続」をクリックします。ブラウザ上にターミナルが開きます。

インスタンスへの接続画面(SSM セッションマネージャーを選択した状態)
インスタンスへの接続画面(SSM セッションマネージャーを選択した状態)

ターミナルで次のコマンドを実行します。タイムアウトとリトライ回数を指定して、待たされずに失敗を確認します。

AWS_MAX_ATTEMPTS=1 aws kms list-keys --region ap-northeast-1 \
    --cli-connect-timeout 5 --cli-read-timeout 5

[!NOTE] --cli-connect-timeout だけでは、接続に失敗するたびにAWS CLIがリトライするため結局待たされます。AWS_MAX_ATTEMPTS=1 を付けて1回で諦めさせます。

接続がタイムアウトして失敗することを確認します。このVPCにはインターネットへの出口が無く、KMSのパブリックエンドポイントに到達できないためです。

KMS APIの呼び出しがタイムアウトで失敗した状態のターミナル
KMS APIの呼び出しがタイムアウトで失敗した状態のターミナル

手順2: KMS用のインターフェイスエンドポイントを作成する

上部の検索バーに VPC と入力し、表示された「VPC」を選択します。

左側ナビゲーションの「エンドポイント」を選択し、「エンドポイントを作成」をクリックします。

以下を設定します。

  • 名前タグ: kms-vpce-lab-kms
  • タイプ: AWS のサービス
  • サービス: 検索ボックスに kms と入力し、com.amazonaws.ap-northeast-1.kms を選択(kms-fips ではない方)
  • VPC: kms-vpce-lab-vpc
  • 「プライベート DNS 名を有効化」: 既定でチェックが入っています。入っていることを確認します
  • サブネット: アベイラビリティーゾーンの行にチェックを入れてから、「サブネットを選択」のドロップダウンで kms-vpce-lab-private-subnet を選ぶ(2段階の操作です)
  • セキュリティグループ: kms-vpce-lab-endpoint-sg
  • ポリシー: フルアクセス(後の手順で絞ります)
VPCエンドポイント作成画面(KMSサービスとVPC・DNS名の有効化を設定した状態)
VPCエンドポイント作成画面(KMSサービスとVPC・DNS名の有効化を設定した状態)
VPCエンドポイント作成画面のサブネットとセキュリティグループの選択
VPCエンドポイント作成画面のサブネットとセキュリティグループの選択

ページ下部の「エンドポイントを作成」をクリックします。

エンドポイントのステータスが「使用可能」になるまで数分待ちます。

作成されたKMSエンドポイントの詳細画面(ステータスが使用可能になった状態)
作成されたKMSエンドポイントの詳細画面(ステータスが使用可能になった状態)

手順3: エンドポイント経由で疎通することを確認する

手順1のターミナルに戻り、同じコマンドを再度実行します。

aws kms list-keys --region ap-northeast-1 --cli-connect-timeout 5 --cli-read-timeout 5

今度はキーの一覧が返ることを確認します。アプリケーション側の設定は何も変えていません。プライベートDNSによって kms.ap-northeast-1.amazonaws.com の名前解決がエンドポイントのプライベートIPに変わったためです。

名前解決の結果も確認しておきます。

dig +short kms.ap-northeast-1.amazonaws.com

10.20.1.x というVPC内のプライベートIPが返ることを確認します。

エンドポイント作成後にKMS APIが成功し、名前解決がプライベートIPになっている状態のターミナル
エンドポイント作成後にKMS APIが成功し、名前解決がプライベートIPになっている状態のターミナル
クラウドおさる

コマンドは何も変えていないのに、エンドポイントを作っただけで list-keys が通ったでござるな。プライベート DNS で kms.ap-northeast-1.amazonaws.com の名前解決が VPC 内のプライベート IP に変わった、というのが dig の結果からも分かるのでござる。

手順4: 経路を強制するキーポリシーを設定する

上部の検索バーに KMS と入力し、「Key Management Service」を選択します。

「カスタマー管理型のキー」から「キーの作成」で、エイリアス vpce-lab-key の対称キーを作成します。キーの管理者・使用者には自分のIAMユーザー(またはロール)と、kms-vpce-lab-instance-role を指定します。

作成したキーを開き、「キーポリシー」タブで「編集」をクリックします。既存のステートメントの末尾に、次のステートメントを追加します。vpce-xxxxxxxx は手順2で作成したエンドポイントのIDに置き換えます。

{
  "Sid": "DenyAccessOutsideVpce",
  "Effect": "Deny",
  "Principal": "*",
  "Action": [
    "kms:Encrypt",
    "kms:Decrypt",
    "kms:ReEncrypt*",
    "kms:GenerateDataKey*"
  ],
  "Resource": "*",
  "Condition": {
    "StringNotEquals": {
      "aws:sourceVpce": "vpce-xxxxxxxx"
    }
  }
}
キーポリシー編集画面(sourceVpce 条件のDenyステートメントを追加した状態)
キーポリシー編集画面(sourceVpce 条件のDenyステートメントを追加した状態)

「変更の保存」をクリックします。

手順5: 経路によって結果が変わることを確認する

まずEC2のターミナル(エンドポイント経由)から暗号化を実行します。<キーID> は作成したキーのIDに置き換えます。

aws kms encrypt --key-id <キーID> --plaintext "$(echo -n 'hello' | base64)" --region ap-northeast-1

暗号文が返ることを確認します。

次に、同じ操作をマネジメントコンソールから試します。KMSのキー詳細画面には暗号化を試す機能が無いため、ここではCloudShellまたは手元の端末のAWS CLIから同じコマンドを実行します。

aws kms encrypt --key-id <キーID> --plaintext "$(echo -n 'hello' | base64)" --region ap-northeast-1

AccessDeniedException で拒否されることを確認します。同じIAM権限を持っていても、指定したVPCエンドポイントを経由していないためキーポリシーのDenyが効きます。

VPCエンドポイント外からの暗号化がAccessDeniedで拒否された状態
VPCエンドポイント外からの暗号化がAccessDeniedで拒否された状態
クラウドおさる

aws:sourceVpce の Deny を入れると、指定したエンドポイント以外からのリクエストはコンソールや S3 の SSE-KMS のような統合サービスからでも拒否されるのでござる。それと、VPC エンドポイント経由では aws:sourceIP が効かぬので、経路を絞るなら aws:sourceVpce か aws:sourceVpc を使うでござるぞ。

手順6: エンドポイントポリシーで絞る(任意)

VPCの「エンドポイント」画面で作成したエンドポイントを選択し、「ポリシー」タブの「ポリシーを編集」をクリックします。

「カスタム」を選び、次のように特定のキーだけを許可するポリシーに変更できます。

{
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": "*",
      "Action": ["kms:Encrypt", "kms:Decrypt", "kms:DescribeKey"],
      "Resource": "arn:aws:kms:ap-northeast-1:111122223333:key/<キーID>"
    }
  ]
}

エンドポイントポリシーはIAMポリシーやキーポリシーを置き換えるものではなく、その経路を通るリクエストに対する上限として働きます。3つが揃って初めて許可されます。

エンドポイントポリシーの編集画面(特定のキーのみを許可した状態)
エンドポイントポリシーの編集画面(特定のキーのみを許可した状態)
クラウドおさる

プライベートサブネットから AWS サービスへ安全に届かせたいとき、NAT ゲートウェイとインターフェイスエンドポイントのどちらを選ぶか、という観点が問われるのでござる。ゲートウェイ型はS3とDynamoDBのみという区別や、経路を縛る条件キーの選び方も、取り違えやすいところでござるな。

まとめ

論点結論
プライベートサブネットからKMSへインターフェイスVPCエンドポイント(PrivateLink)を使う
NATゲートウェイとの違いインターネットに出ないこと、到達範囲がそのサービスに限られること、料金が抑えられること
アプリの変更プライベートDNSを有効にすれば不要。名前解決だけが変わる
経路の強制キーポリシーの aws:sourceVpce / aws:sourceVpc 条件で行う
注意点VPCエンドポイント経由では aws:sourceIP が効かない。経路を絞ると統合サービスやコンソールからの操作も拒否される
  • ゲートウェイ型はS3とDynamoDBのみ。KMSを含む多くのサービスはインターフェイス型を使う
  • エンドポイントポリシー・IAMポリシー・キーポリシーは重ねて評価される。どれかで拒否されればアクセスできない
  • 検証後はインターフェイスエンドポイントを削除する(時間課金が続くため)

参照先

フリーランス案件
クラウドおさる
クラウドキャリアフリーランス

AWS の実務経験があるなら、単価を上げにいく

AWS に特化したフリーランスエージェントです。以下は取り扱い案件の一例です。設計・SRE・セキュリティ・生成 AI の領域で、専門を掛け合わせるほど単価が上がります。

横にスクロールできます。掲載時点の情報のため、募集が終了している場合があります。条件の近い案件をご紹介しますので、お気軽にご相談ください。

この記事が気に入ったら
フォローしてね!

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

株式会社ジェニュイン(https://genuine-pt.jp)の代表取締役.

2018年〜インフラエンジニアとしてキャリアをスタートし、オンプレミスのネットワーク・サーバ環境で3年半、クラウド環境で4年半の8年間エンジニアとして従事。
2021年に佐藤氏の創業した株式会社Luxyを引き継ぎ、代表に就任。
その後アガルートグループ内の株式会社ジェニュインと合併。
合併後同社代表に就任。

目次