【実務・中級編】 プライベートDNS名(Private DNS)を用いたVPCエンドポイントの名前解決の仕組み – クラウド&コンテナネットワーク実践ガイド

はじめに:なぜ、パブリックなはずのAWSサービス名がプライベートIPに化けるのか?

こんにちは。クラウドインフラの現場を渡り歩き、数々のネットワークトラブルの夜を越えてきたシニアSREの私です。

Webアプリケーションのモダナイゼーションが進み、多くのシステムがコンテナ化やマイクロサービス化の洗礼を受けています。Amazon EKSをはじめとするKubernetesクラスターから、Amazon S3やDynamoDB、Secrets Managerといったマネージドサービスをセキュアに叩く――いまやこれはモダンなクラウドアーキテクトにとっての「基本中の基本」です。

ここで、皆さんはこんな疑問を持ったことはないでしょうか?

> 「あれ?インターネットに出られない完全に隔離されたプライベートサブネット(いわゆる非公開環境)にあるPodから、なぜ s3.us-east-1.amazonaws.com というパブリックなDNS名を引いて、インターネット経由ではなくVPC内部のプライベートIPと通信できるんだ?」

この魔法のような現象の裏側を支えている主役こそが、今回深掘りする 「Interface型VPCエンドポイント(AWS PrivateLink)におけるプライベートDNSの名前解決の仕組み」 です。

教科書的なドキュメントを読めば「プライベートホストゾーンが自動生成されます」とサラッと書いてありますが、現場のエンジニアが知りたいのはそこではありません。
パケットがDNSクエリを発行した瞬間から、Route 53の裏側で何が起き、どうやってエンドポイントネットワークインターフェイス(ENI)のプライベートIPにたどり着くのか。そして、もし名前解決や疎通でハマったとき、私たちはどうやってその泥沼から抜け出せばいいのか。

今回は、そのすべてを現場目線で徹底的に紐解いていきます。

—

1. 基礎知識:Interface型VPCエンドポイントとプライベートDNSの正体

まず、前提を揃えましょう。VPCエンドポイントには大きく分けて Gateway型 と Interface型 の2種類があります。

  • Gateway型: S3やDynamoDB専用で、ルーティングテーブルの経路書き換えによってパケットをルーティングする古典的かつ強力な仕組み(料金無料)。
  • Interface型: ENI(Elastic Network Interface)をVPC内に生やし、AWS PrivateLinkを介してマネージドサービスや他社のAPIへプライベートにアクセスする仕組み(料金有料・ENI単位)。

今回ターゲットにするのは、後者の Interface型VPCエンドポイント です。

プライベートDNS名(Private DNS)がもたらす魔法

Interface型エンドポイントを作成する際、マネジメントコンソールやTerraformで Enable Private DNS(プライベートDNS名)というチェックボックス、あるいは設定値を見たことがあるはずです。

これを有効にすると、AWS側で何が起きるか?
AWSは、そのサービスが本来持っているパブリックなDNS名(例: secretsmanager.us-east-1.amazonaws.com)に対する Amazon Route 53 のプライベートホストゾーン(Private Hosted Zone) を自動的に作成し、あなたのVPCにアタッチします。

これにより、アプリケーションコード側の一切の変更(エンドポイントURLの書き換えなど)を行わずに、既存のAWS SDKや標準的なHTTPクライアントが発する名前解決要求を、そのままVPC内のエンドポイントENIのプライベートIPへとハイジャック(正常な意味での名前解決の書き換え)できるのです。

—

2. 通信の裏側:パケットとDNSクエリがたどるリアルな旅路

では、コンテナ(Pod)からS3などのサービスへリクエストが飛ぶ瞬間、ネットワーク内では一体何が起きているのでしょうか。そのシーケンスを順に追ってみましょう。

[Kubernetes Pod / アプリケーション]
       │
       │ 1. DNSクエリ送信 ("s3.us-east-1.amazonaws.com を教えて")
       ▼
[VPC DNSResolver (AmazonProvidedDNS / 169.254.169.253)]
       │
       │ 2. VPCにアタッチされたPrivate Hosted Zoneをチェック
       ▼
[Route 53 Private Hosted Zone (AWS管理)]
       │
       │ 3. エンドポイントENIのプライベートIPを返却 (例: 10.0.1.50)
       ▼
[VPC DNSResolver]
       │
       │ 4. PodへIPアドレスを返却
       ▼
[Kubernetes Pod]
       │
       │ 5. TCP 3-wayハンドシェイク & HTTPS通信
       ▼
[VPCエンドポイント ENI (IP: 10.0.1.50)]
       │
       │ 6. AWS PrivateLinkバックボーン網
       ▼
[AWS マネージドサービス (S3等)]

1. アプリケーションからのDNSクエリ

Pod内のコードが https://secretsmanager.us-east-1.amazonaws.com/ に対してHTTPSリクエストを投げようとすると、OSの resolver はまず /etc/resolv.conf に記述されたVPCのDNSサーバー(AmazonProvidedDNS、通常はVPCネットワークCIDRのベースIP + 2、または 169.254.169.253)へDNSクエリを投げます。

2. Route 53 Private Hosted Zone による名前解決の横取り

VPCのDNSリゾルバーは、そのVPCに関連付けられているRoute 53のプライベートホストゾーンを検索します。
もし Enable Private DNS が有効であれば、そこには secretsmanager.us-east-1.amazonaws.com というレコードが登録されています。
結果として、パブリックなIPアドレスではなく、VPC内のサブネットに配置されたInterface型エンドポイントのENIに割り当てられたプライベートIPアドレス(例: 10.0.1.50) が返されます。

3. プライベートIPを宛先としたパケットの送出

Podは返ってきたプライベートIP宛てにTCP 3-wayハンドシェイク(SYN)を始めます。パケットはサブネットのルートテーブルに従い、VPCエンドポイントのENIへと吸い込まれていきます。
ここから先は、インターネットの荒波に出ることなく、AWSの強固なプライベートバックボーン網を通ってサービスのホストへと到達します。完璧なセキュア通信の完成です。

—

3. 実務で役立つ設定例:Terraformでの安全な構築

口で言うのは簡単ですが、実際にこれをインフラストラクチャ・アイズ・コード(IaC)で構築する際、いくつかのハマりポイントがあります。
ここではTerraformを用いて、プライベートDNSを有効化したInterface型VPCエンドポイントを構築する典型的なコード例を示します。

# セキュリティグループの定義
# エンドポイントENIへのインバウンド通信(HTTPS: 443)を許可する
resource "aws_security_group" "vpc_endpoint_sg" {
  name        ="vpc-endpoint-secretsmanager-sg"
  description = "Security group for Secrets Manager VPC Endpoint"
  vpc_id      = var.vpc_id

  ingress {
    description = "Allow HTTPS from VPC CIDR"
    from_port   = 443
    to_port     = 443
    protocol    ="tcp"
    cidr_blocks = [var.vpc_cidr] # 自VPCからのトラフィックのみを許可
  }

  egress {
    description = "Allow all outbound traffic"
    from_port   = 0
    to_port     = 0
    protocol    ="-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
    Name = "vpc-endpoint-secretsmanager-sg"
  }
}

# Interface型VPCエンドポイントの定義
resource "aws_vpc_endpoint" "secretsmanager" {
  vpc_id              = var.vpc_id
  service_name        = "com.amazonaws.${var.aws_region}.secretsmanager"
  vpc_endpoint_type   = "Interface"
  subnet_ids          = var.private_subnet_ids # プライベートサブネットのID群を指定
  security_group_ids  = [aws_security_group.vpc_endpoint_sg.id]

  # 【重要】プライベートDNS名を有効化するフラグ
  # これにより、AWS側がRoute 53のプライベートホストゾーンを自動的にVPCに関連付けます
  private_dns_enabled = true

  tags = {
    Name = "secretsmanager-vpc-endpoint"
  }
}

設計上の超重要チェックポイント

VPCの作成時に、以下の2つのVPC属性が必ず true になっている必要があります。これが false だと、プライベートDNSが機能せず、名前解決がパブリックIPを向いてしまい接続エラーになります。

  • enable_dns_hostnames = true
  • enable_dns_support = true

Terraformの aws_vpc リソースではデフォルトで有効なことが多いですが、既存のカスタムVPCをインポートした場合などは必ず確認してください。

—

4. アプリケーションコードからの検証と実践的Tips

インフラを構築したら、本当にプライベートDNSが機能しているか、アプリケーションやデバッグ用コンテナから検証する必要があります。
現場のSREがよく使う確認手順とコードスニペットをいくつか紹介します。

トラブルシューティングの第一歩:DNSの逆引き・正引き確認

まずは、プライベートサブネット内で動く踏み台サーバーやEKSのデバッグPodにログインし、名前解決が期待通りか確認します。

# digコマンドで名前解決を確認
$ dig secretsmanager.us-east-1.amazonaws.com

# 期待される出力のイメージ:
# ANSWER SECTION:
# secretsmanager.us-east-1.amazonaws.com. 60 IN A 10.0.1.50
# secretsmanager.us-east-1.amazonaws.com. 60 IN A 10.0.2.75

もしここで返ってくるIPアドレスが、AWSのパブリックIP帯(例: 52.x.x.x など)であれば、プライベートDNSの設定(private_dns_enabled やVPCのDNS設定)が漏れています。ENIのプライベートIP(例: 10.0.x.x)が返ってきていることが成功の証です。

Python (Boto3) によるSecrets Managerへのアクセス例

プライベートDNSが正しく機能していれば、PythonのAWS SDK(Boto3)は特別なエンドポイントURLを指定しなくても、自動的にVPCエンドポイント経由で通信を行います。

import boto3
from botocore.exceptions import ClientError

def get_secret_value(secret_name: str):
    """
    Secrets Managerからシークレットを取得する関数。
    VPCエンドポイント経由であれば、インターネット出力を一切せずに安全に取得できる。
    """
    # リージョンを指定してクライアントを初期化
    # ※エンドポイントURL(endpoint_url)のハードコーディングは不要!
    session = boto3.session.Session()
    client = session.client(
        service_name='secretsmanager',
        region_name='us-east-1'
    )

    try:
        response = client.get_secret_value(SecretId=secret_name)
        print("シークレットの取得に成功しました。")
        return response.get('SecretString')
        
    except ClientError as e:
        print(f"API呼び出しに失敗しました: {e}")
        raise e

if __name__ == "__main__":
    # テスト実行
    # secret_value = get_secret_value("my-production-db-credential")
    pass

—

5. 現場でありがちな「ハマりポイント」と泥臭いデバッグ手法

最後に、数々の現場でエンジニアたちが涙を飲んできた「プライベートDNS関連のよくあるトラブルと解決策」をシェアします。

ハマりポイント 1: オンプレミスや別VPCからの名前解決(Route 53 Resolver / Hybrid Cloud)

「VPC内からはうまくいくのに、AWS Direct ConnectやVPNで繋がったオンプレミス環境、あるいはトランジットゲートウェイ(TGW)経由の別VPCから名前解決すると、パブリックIPが返ってきて接続できない!」という相談を本当によく受けます。

原因:
Route 53のプライベートホストゾーン(自動生成されたもの)は、そのVPCの内部からしか見えないという制約があります。別VPCやオンプレミスからは、デフォルトのパブリックDNSが引かれてしまうのです。

対策:

  • 別VPCからの場合: VPCピアリングやTGWを使っている場合、コンシューマーVPC側でRoute 53 Resolverのプライベートホストゾーンの関連付け(VPCアソシエーション)を追加するか、Route 53 Resolverのインバウンド・アウトバウンドエンドポイントを活用した名前解決の転送設計が必要です。
  • オンプレミスの場合: オンプレミスのDNSサーバーから、該当のAWSサービス名に対するクエリを、AWS側のRoute 53 Resolver(Inbound Endpoint)へフォワードし、適切なプライベートIPを返すような条件付きフォワーディングの設定を行います。

ハマりポイント 2: カスタムホストゾーンとの競合

自社で既に amazonaws.com やサービス固有のドメインに対するプライベートホストゾーンを手動で作成していた場合、AWSが自動生成するプライベートホストゾーンと名前空間が衝突し、予期せぬ名前解決エラーを引き起こすことがあります。

対策:
AWSマネージドのサービスに対しては、可能な限りAWSが自動生成するプライベートホストゾーンの仕組み(private_dns_enabled = true)に任せ、手動での競合するホストゾーンの作成は避けるのが鉄則です。

—

おわりに

Interface型VPCエンドポイントにおけるプライベートDNSの名前解決の仕組みは、一見すると「AWSが勝手によしなにしてくれる魔法の機能」に見えます。しかし、その裏側では、VPCのDNSリゾルバーとRoute 53のプライベートホストゾーン、そしてENIが緻密に連携してパケットの行先をコントロールしています。

この仕組みの解像度を上げておくことで、万が一のネットワーク疎通トラブル(「なぜかタイムアウトする」「パブリックに出ていこうとしてセキュリティグループに弾かれる」など)に直面した際にも、DNSの正引き確認からENIのセキュリティグループ、ルートテーブルの検証まで、迷いなく最短ルートで原因を特定できるようになります。

皆さんのクラウドアーキテクチャが、よりセキュアで堅牢なものになることを願っています。それでは、良きSREライフを!

コメント

タイトルとURLをコピーしました