はじめに
ようやくです。待望の機能がリリースされました!
Amazon Cognito User Pools が AWS PrivateLinkでのプライベート接続をサポート。
これまで、VPC 内のリソースから Cognito を利用した認証を行う場合、パブリックインターネット経由のアクセスが前提でしたが、今回のアップデートにより、VPC からの Cognito 認証・管理をインターネットを経由せずに実現できるようになりました、というアナウンスがありました。
実際の画面ではエンドポイントの作成時に、Interface型のCognitoサービスが選択できるようになっています。

何がかわったか?
これで、インターネットアクセスは不要になる!?と思ったのですが、あらためてちゃんと見ていきましょう。
Cognito User Pools のエンドポイントへ Interface VPC Endpoint(AWS PrivateLink)で接続できるようになりました。これにより、バックエンドからの管理操作やローカルユーザーのサインイン処理をパブリックインターネットを経由せずに実行可能です。ただし、ホステッド UI(ブラウザでの OAuth 認可コードフロー)やクライアント資格情報フロー、外部 IdP を使ったフェデレーションは PrivateLink 経由ではサポートされないため、ブラウザ側の通常ログインフローは従来どおりパブリック経路が必要です
AWS公式 What's New の内容を要約すると、ざっくり以下のようになります。
VPC内のECS/EKS/Lambda/EC2などのバックエンドからCognitoの管理や一部認証処理をインターネットを経由せずに実現できる、と。
一方で、次のようなブラウザ中心の認証フローは PrivateLink 経由にならないと書いてありました。
- Hosted UI を使った OAuth 2.0 Authorization Code フロー
- OAuth の Client Credentials フロー(トークンエンドポイント)
- 外部 IdP(Azure AD / Okta など)との SAML / OIDC フェデレーション
ここを誤解すると「全部閉域にできるはず」と期待して設計してしまいそうです。
具体的なシナリオ①
やりたいこと
- 社内業務システム(ブラウザでアクセス)
- アクセス経路は閉域網( VPN / Direct Connect 経由)のみ
- ポリシーとして「ブラウザからインターネットに出さない」
- 認証は Cognito Hosted UI(+ Azure AD などの外部 IdP)で統合管理したい
実際に起きること
- Hosted UI の
/oauth2/authorize//oauth2/tokenにブラウザから到達する必要がある - これらは PrivateLink の対象外 であり、最終的にはパブリックな Cognito のエンドポイントに到達する必要がある
結果
- 社内ブラウザがインターネットに出られない環境(もしくは閉域網のみの要件)では、Hosted UI にそもそもアクセスできない
- プロキシやファイアウォールでドメイン単位の例外許可が必要になり、「完全閉域」という要件は満たせなくなる
具体的なシナリオ②
やりたいこと
- ブラウザ → 業務アプリ
- 業務アプリ → Cognito Hosted UI
- Cognito → Azure AD / Okta 等の外部 IdP(SAML / OIDC)
- IdP 認証後、再び Cognito から アプリにトークンを返す
実際に起きること
- Cognito Hosted UI も Azure AD / Okta もインターネット上のサービス
- PrivateLink は「VPC → Cognito API」の閉域化であり、以下の通信はインターネット経由となる
- ブラウザ → Cognito Hosted UI
- Cognito Hosted UI → 外部 IdP
結果
- 「フェデレーションを含め、認証関連トラフィックをすべて閉域に」という要件には合わない
- ネットワーク/セキュリティ側と「どのドメインをどこまで許可するか」の調整や設計が必要となる
ではどう設計すべきかなのでしょう?
バックエンドだけ閉域化
- バックエンド(ECS/EKS/Lambda/EC2)は Interface VPC Endpoint 経由で Cognito API を叩く。これは今回の新機能で対応が可能です
- ユーザー管理、トークン検証、管理者操作など
- ブラウザからの認証フロー(Hosted UI / フェデレーション)は従来どおりインターネット経由として、以下の対策を行いながら対応する
- Cognito Custom Domain
- AWS WAF
- CloudFront 経由
- IP 制限や各種シグナルを用いた防御 など
これは「バックエンドの閉域化」と「ブラウザ/外部 IdP のパブリック前提」を切り分けた設計となります。
認証方式そのものを変える
- 要件として「どうしても閉域完結が必要」で、ブラウザのインターネット接続も認められない場合は以下の方法が考えられます
- Hosted UI や標準 OAuth フローを前提にするのをやめる
- Cognito Admin API(
AdminInitiateAuthなど)を用いた 自前のログイン API を構成する
ただし、これはアプリケーション側の実装・責務が重くなり、保守性とリスクを増やすため、要件とコストを慎重に比較する必要があるといえます。
実装編
CloudFormation による Interface VPC Endpoint 作成をやってみましょう。
前提となる既存リソース
- VPC
- マルチ AZ のサブネット
- エンドポイント用のセキュリティグループ(HTTPS を許可)
CloudFormationサンプルテンプレート
AWSTemplateFormatVersion: '2010-09-09'
Description: >
Create an Interface VPC Endpoint (AWS PrivateLink) for Amazon Cognito User Pools (cognito-idp).
Parameters:
VpcId:
Type: AWS::EC2::VPC::Id
Description: VPC ID where the interface endpoint will be created.
SubnetIds:
Type: List<AWS::EC2::Subnet::Id>
Description: Subnets (in different AZs) for the interface endpoint ENIs.
SecurityGroupIds:
Type: List<AWS::EC2::SecurityGroup::Id>
Description: Security Group(s) to attach to the endpoint ENIs.
Resources:
CognitoVpcEndpoint:
Type: AWS::EC2::VPCEndpoint
Properties:
VpcId: !Ref VpcId
ServiceName: !Sub "com.amazonaws.${AWS::Region}.cognito-idp"
VpcEndpointType: Interface
SubnetIds: !Ref SubnetIds
SecurityGroupIds: !Ref SecurityGroupIds
PrivateDnsEnabled: true
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Principal: "*"
Action:
- "cognito-idp:AdminCreateUser"
- "cognito-idp:AdminGetUser"
- "cognito-idp:AdminInitiateAuth"
- "cognito-idp:AdminRespondToAuthChallenge"
- "cognito-idp:ListUserPools"
- "cognito-idp:DescribeUserPool"
- "cognito-idp:ListUsers"
Resource: "*"
Outputs:
VpcEndpointId:
Description: VPC Endpoint ID for Cognito (PrivateLink)
Value: !Ref CognitoVpcEndpoint
VpcEndpointDnsEntries:
Description: DNS entries for the endpoint
Value: !Join [", ", !GetAtt CognitoVpcEndpoint.DnsEntries]
サンプルテンプレートの解説
- Parameters:セクション
スタック作成時に外から与える値の定義です。- VpcId
エンドポイントを作成する対象VPCを指定します - SubnetIds
1つ以上のサブネットIDを指定します
複数 AZ にまたがるサブネットを指定することで、可用性を確保できます - SecurityGroupIds
エンドポイントENIにアタッチするセキュリティグループのIDを指定します
- VpcId
- Resources:セクション
生成するリソースのプロパティです。- Properties
- VpcId: !Ref VpcId
- ServiceName: !Sub "com.amazonaws.${AWS::Region}.cognito-idp"
- VpcEndpointType: Interface
- SubnetIds: !Ref SubnetIds
- SecurityGroupIds: !Ref SecurityGroupIds
- PrivateDnsEnabled: true
- PolicyDocument
VPC エンドポイントポリシー(このエンドポイント経由で、どの API を誰に許可するか)を定義します。- Principal: "*"
だれでも(任意の IAM プリンシパル)このエンドポイント経由でアクセス可能
通常はアカウントやロールに絞り込む必要あり - Action
このエンドポイントを経由して許可されている Cognito API アクションの一覧 - Resource: "*"
すべての Cognito リソース(全ユーザープール)が対象
実運用では特定のユーザープール ARN に絞る必要あり
- Principal: "*"
- Properties
- Outputs:セクション
スタック作成後にコンソールや describe-stacks で参照できる出力です。- VpcEndpointId
作成した VPC Endpoint の ID - VpcEndpointDnsEntries
!GetAtt CognitoVpcEndpoint.DnsEntriesでエンドポイントに紐づく DNS 名(リージョナルエイリアスや ENI の FQDN 等)のリストが取得
- VpcEndpointId
コスト
インターフェイスエンドポイントの料金は、時間課金+データ処理課金となります。
東京リージョンの場合、
時間課金 $0.014/h
処理課金 1PBまで$0.01/GB
単価は 2025年11月時点のものです。最新情報は公式料金ページを確認してください。
まとめ
Cognito User Pools の PrivateLink 対応は、「バックエンドからの Cognito API 呼び出しを閉域化する」 ためには非常に有効といえます。
ただ一方で、Hosted UI をはじめてとして、外部 IdP(SAML / OIDC)とのフェデレーションは PrivateLink の対象外であり、認証フロー全体の完全閉域化の手段とはならないことがわかりました。
したがって設計上は、バックエンドの管理処理・トークン検証を PrivateLink で実現してセキュリティとネットワーク制御を強化しつつ、ブラウザ側や外部 IdP との経路はパブリック前提で WAF 等で防御という役割を考慮した設計が重要となります。
同じように「一部だけ PrivateLink で閉じられる」サービスは他にもあるので、「どのトラフィックが閉じられて、どこがパブリック前提なのか」 を意識して設計しておくとよいかと思います。