はじめに
GenUの閉域モードは、各種リソースにVPCエンドポイント経由で接続します。オンプレミス端末からは直接VPCエンドポイントにアクセスする処理があり、VPCエンドポイントの名前解決が必要になります。オンプレミスのDNSサーバで該当ドメインに関して、フォワード設定を行いますが、仕組み上、フォワード先がVPCエンドポイントの集約アカウントとなります。今回はGenUの構成を2つ作成し、名前解決の動作を確認しながら、どのような構成が考えられるか検証を行います。
共有リソースVPCのVPCエンドポイントを経由してGenUへ接続する構成は、下記@takeda_h(takeda_h)さんの記事で紹介されています。この記事ではGenUのCDKを改修し、API Gatewayのリソースポリシーに共有リソースVPCのVPCエンドポイントを追加しています。今回はCDKを改修せず、API GatewayのVPCエンドポイントだけを環境ごとに分ける方式を検証しました。
検証環境
| VPC | CIDR | 内容 |
|---|---|---|
| オンプレミス想定VPC | 10.0.0.0/16 | EC2(Windows Server)。DNSサーバーの役割を追加し、DNSサーバーを兼用 |
| GenU環境A(アカウントA) | 10.1.0.0/16 | GenUの閉域モード |
| GenU環境B(アカウントB) | 10.2.0.0/16 | GenUの閉域モード |
3つのVPCはTransit Gatewayで接続しています。GenUはpackages/cdk/cdk.jsonの下記4項目だけを既定値から変更してデプロイしています。
| 項目 | 設定値 |
|---|---|
| closedNetworkMode | true |
| closedNetworkVpcIpv4Cidr | 環境Aは10.1.0.0/16、環境Bは10.2.0.0/16
|
| modelRegion | ap-northeast-1 |
| modelIds | ["jp.amazon.nova-2-lite-v1:0"] |
① GenUの閉域モード環境を1つ構築する
GenUを1環境デプロイした基本構成は下記の通りです。
名前解決の転送先と通信の流れは下記の通りです。
水色がオンプレミス側で設定するもの、緑がGenUがデプロイするリソースです。
クライアントPCのDNSサーバーに条件付きフォワーダーを設定し、GenUの利用に必要なDNS名を環境Aのインバウンドエンドポイントへ転送します。名前解決が必要なエンドポイントの一覧は、GenUのドキュメントに記載されています。
EC2のDNSサーバーを自分自身に変更する
今回EC2サーバを確認用クライアント兼DNSサーバとして使用します。EC2にDNSサーバーの役割を追加し、優先DNSサーバーを自分自身(127.0.0.1)に変更します。
EC2はFleet Manager経由で操作しているため、ssm.ap-northeast-1.amazonaws.comとssmmessages.ap-northeast-1.amazonaws.comはオンプレミス想定VPCのDNS(10.0.0.2)へ転送しています。このエンドポイントの名前解決が出来ないとFleet Managerで接続出来なくなります。
DNSサーバーにALBのDNS名の条件付きフォワーダーを設定する
ALBのDNS名はClosedNetworkStackの出力ClosedNetworkWebUrlで確認できます。
DNSマネージャーの条件付きフォワーダーに、ALBのDNS名と環境Aのインバウンドエンドポイントを登録します。
登録後、ALBのDNS名にアクセスするとGenUのサインイン画面が表示されます。
ただし、ALBのDNS名だけではアカウントの作成やユーザ認証時に「A network error has occurred.」と表示されます。CognitoのDNS名を名前解決できていないためです。
DNSサーバーにGenUが利用するサービスの条件付きフォワーダーを設定する
ここからはコマンドでドキュメントの一覧にあるDNS名をまとめて登録します。API GatewayのDNS名はGenerativeAiUseCasesStackの出力ApiEndpointで確認できます。
$resolverA = @("10.1.x.x","10.1.y.y") # 環境Aのインバウンドエンドポイント
$zones = @(
"aaaaaaaaaa.execute-api.ap-northeast-1.amazonaws.com", # API Gateway
"cognito-idp.ap-northeast-1.amazonaws.com",
"cognito-identity.ap-northeast-1.amazonaws.com",
"s3.ap-northeast-1.amazonaws.com",
"lambda.ap-northeast-1.amazonaws.com",
"transcribe.ap-northeast-1.amazonaws.com",
"transcribestreaming.ap-northeast-1.amazonaws.com",
"polly.ap-northeast-1.amazonaws.com",
"bedrock-agentcore.ap-northeast-1.amazonaws.com"
)
foreach ($z in $zones) {
Add-DnsServerConditionalForwarderZone -Name $z -MasterServers $resolverA
}
上記設定後、サインインしてチャットの応答まで表示されることを確認しました。
② GenUの閉域モード環境を2つ構築する
下記にもう一つGenUの環境をデプロイしました。
環境Bを構築し、ALBのDNS名だけを環境Bのインバウンドエンドポイントへ転送します。API GatewayのDNS名とそれ以外のサービス名は、環境Aのインバウンドエンドポイントへ転送します。
$resolverB = @("10.2.x.x","10.2.y.y") # 環境Bのインバウンドエンドポイント
Add-DnsServerConditionalForwarderZone `
-Name "internal-closed-close-zzzzzzzzzzzz-zzzzzzzzz.ap-northeast-1.elb.amazonaws.com" `
-MasterServers $resolverB
Add-DnsServerConditionalForwarderZone `
-Name "bbbbbbbbbb.execute-api.ap-northeast-1.amazonaws.com" `
-MasterServers $resolverA
環境BのAPI GatewayのDNS名も環境Aのインバウンドエンドポイントで名前解決でき、環境AのAPI GatewayのVPCエンドポイントのIPアドレスが返ります。
Cognitoへの通信が環境AのVPCエンドポイントを経由することを確認する
環境BのALBのURLにアクセスし、サインインとチャットの応答まで表示されることを確認しました。環境BのCloudTrailには、サインイン時のInitiateAuthが下記のように記録されています。
{
"eventSource": "cognito-idp.amazonaws.com",
"eventName": "InitiateAuth",
"sourceIPAddress": "10.0.x.x",
"recipientAccountId": "222222222222",
"vpcEndpointId": "vpce-xxxxxxxxxxxxxxxxx",
"vpcEndpointAccountId": "111111111111",
"tlsDetails": {
"clientProvidedHostHeader": "cognito-idp.ap-northeast-1.amazonaws.com"
}
}
recipientAccountIdが環境BのアカウントID、vpcEndpointAccountIdが環境AのアカウントIDです。環境AのVPCエンドポイントを経由して、環境BのCognitoのユーザープールにリクエストが届いています。環境BのCognitoのユーザープールにも、作成したユーザーが登録されていました。
チャットの読み上げで実行されるPollyのSynthesizeSpeechも、同じく環境AのVPCエンドポイントを経由して環境Bで記録されました。
API Gatewayへの通信が拒否されることを確認する
一方で、API Gatewayを経由する処理である、チャット履歴の読み込みとファイルの添付は失敗しました。ブラウザの開発者ツールで確認すると、API Gatewayへのリクエストに403が返っています。
GenUのCDKでは、API Gatewayのリソースポリシーが下記のように定義されています(packages/cdk/lib/construct/api.ts)。
policy: vpc
? new PolicyDocument({
statements: [apiGatewayVpcEndpoint!].map(
(e: InterfaceVpcEndpoint) => {
return new PolicyStatement({
effect: Effect.ALLOW,
principals: [new AnyPrincipal()],
actions: ['execute-api:Invoke'],
resources: ['execute-api:/*'],
conditions: {
StringEquals: {
'aws:SourceVpce': e.vpcEndpointId,
},
},
});
}
),
})
: undefined,
許可されているのは、その環境自身が作成したAPI GatewayのVPCエンドポイントだけです。環境AのVPCエンドポイントを経由するとaws:SourceVpceが環境AのVPCエンドポイントIDになるため、環境BのAPI Gatewayでは許可されません。
③ API GatewayのVPCエンドポイントを環境ごとに分けて名前解決する
構成は下記の通りです。API GatewayのみアカウントB経由で接続します。
名前解決の転送先と通信の流れは下記の通りです。
環境BのAPI GatewayのDNS名だけ、転送先を環境Bのインバウンドエンドポイントに変更します。
Remove-DnsServerZone -Name "bbbbbbbbbb.execute-api.ap-northeast-1.amazonaws.com" -Force
Add-DnsServerConditionalForwarderZone `
-Name "bbbbbbbbbb.execute-api.ap-northeast-1.amazonaws.com" `
-MasterServers $resolverB
変更後は、環境BのAPI GatewayのVPCエンドポイントのIPアドレス(10.2.x.x)が返ります。また、環境Bの画面からファイルを添付し、チャットの応答まで表示されることを確認しました。
署名付きURLの発行は環境BのAPI Gatewayを経由します。アップロード先のS3は、条件付きフォワーダーの設定上、環境AのS3のVPCエンドポイントを経由します。
④ VPCエンドポイントを集約VPCにまとめ、API Gatewayだけ環境ごとに分ける
構成は下記の通りです。基本的な通信を集約VPCのVPCエンドポイント経由としつつ、ALBとAPI Gatewayの通信をGenUアカウント経由とします。
名前解決の転送先と通信の流れは下記の通りです。
ALBとAPI GatewayのDNS名を各GenU環境のインバウンドエンドポイントへ転送し、それ以外のサービス名を1つのVPCのインバウンドエンドポイントへ転送します。今回の検証では、環境Aがこの集約VPCの役割になっています。
| DNS名 | 転送先 |
|---|---|
| ALBのDNS名 | 各GenU環境のインバウンドエンドポイント |
API GatewayのDNS名(<API ID>.execute-api.ap-northeast-1.amazonaws.com) |
各GenU環境のインバウンドエンドポイント |
| cognito-idp、cognito-identity、lambda、polly、s3 | 集約VPCのインバウンドエンドポイント |
おわりに
GenUの閉域モード環境を2つ構築し、API Gateway以外のVPCエンドポイントを1つのVPCにまとめられることを確認しました。Cognito、Lambda、Pollyは、別のアカウントのVPCエンドポイントを経由しても、各環境のリソースにリクエストが届きます。API Gatewayはリソースポリシーで自環境のVPCエンドポイントだけを許可しているため、API GatewayのDNS名を環境ごとのインバウンドエンドポイントへ転送することで、CDKを改修せずに利用できました。



















