Amazon API Gateway の VPC エンドポイントを利用する際の落とし穴 ~AWSサービスエンドポイントの提供体系について~

こんにちは。SCSKのふくちーぬです。

皆さんは、プライベート(閉域網)環境下での AWS Lambda や Amazon API Gateway を利用したことありますでしょうか。VPC Lambdaに関する詳細な解説や設計ポイントを知りたい方は、以下の記事から先にご覧いただければ幸いです。

今回は、Amazon API Gateway の VPC エンドポイントを利用する際の落とし穴について解説します。

AWSサービスエンドポイントの種類

AWSでは、各種サービスにおいてサービスへのリクエストを受け付けるエンドポイントを用意しています。以下のように、大きく2種類に分かれています。

・全ての機能を有するサービスエンドポイント

例:Lambda, CloudWatch, CloudFormation …etc

・機能を分割したサービスエンドポイント

例:API Gateway, App Sync, Bedrock …etc

API Gatewayのサービスエンドポイントの提供体系

API Gatewayではサービスエンドポイントとしてコントロールプレーン/データプレーンの2種を用意しています。

API Gatewayにおいては、APIの呼び出し機能を有するデータプレーンのみVPCエンドポイントとして提供されます。

・com.amazonaws.<リージョン>.execute-api

つまり、API の作成・管理機能を有するコントロールプレーンはインターネット経由の通信ではないと利用できません。

・apigateway.<リージョン>.amazonaws.com

結論

  • API GatewayのVPCエンドポイントでは、APIの呼び出し機能のみ利用可能である
  • プライベート(閉域網)環境にてAWSサービスを利用する際は、各種AWSサービスエンドポイントが、VPCエンドポイントとして提供されているか確認すること

検証

今回は、VPC LambdaにてNAT Gateway経由でAPI Gatewayのエンドポイント一覧を取得します。

アーキテクチャ

  • VPC Lambdaは、プライベートサブネットに配置します。
  • VPC Lambdaは、NAT Gateway経由でAPI Gatewayのコントールプレーンにアクセスします。

CloudFormationテンプレート

下記のリソースを作成するCloudFormationテンプレートです。

  • VPC
  • インターネットゲートウェイ
  • NATゲートウェイ
  • パブリックサブネット、プライベートサブネット
  • ルートテーブル
  • セキュリティグループ
  • Lambda
  • IAMロール

以下のテンプレートをデプロイしてください。

AWSTemplateFormatVersion: 2010-09-09
Description: cfn vpc lambda
Parameters:
  ResourceName:
    Type: String   
Resources:
# ------------------------------------------------------------#
# VPC
# ------------------------------------------------------------#
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags:
        - Key: Name
          Value: !Sub "${ResourceName}-VPC"          
# ------------------------------------------------------------#
# Subnet
# ------------------------------------------------------------#
  PublicSubnet1a:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 10.0.0.0/24
      AvailabilityZone: ap-northeast-1a
      MapPublicIpOnLaunch: false
      Tags:
        - Key: Name
          Value: !Sub "${ResourceName}-PublicSubnet-1a"
  PublicSubnet1c:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 10.0.1.0/24
      AvailabilityZone: ap-northeast-1a
      MapPublicIpOnLaunch: false
      Tags:
        - Key: Name
          Value: !Sub "${ResourceName}-PublicSubnet-1c"          
  PrivateSubnet1a:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 10.0.2.0/24
      AvailabilityZone: ap-northeast-1a
      MapPublicIpOnLaunch: false
      Tags:
        - Key: Name
          Value: !Sub "${ResourceName}-PrivateSubnet-1a"
  PrivateSubnet1c:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 10.0.3.0/24
      AvailabilityZone: ap-northeast-1c
      MapPublicIpOnLaunch: false
      Tags:
        - Key: Name
          Value: !Sub "${ResourceName}-PrivateSubnet-1c"
# ------------------------------------------------------------#
# InternetGateway
# ------------------------------------------------------------#
  InternetGateway: 
    Type: "AWS::EC2::InternetGateway"
    Properties: 
      Tags: 
        - Key: Name
          Value: !Sub "${ResourceName}-igw"
  InternetGatewayAttachment: 
    Type: "AWS::EC2::VPCGatewayAttachment"
    Properties: 
      InternetGatewayId: !Ref InternetGateway
      VpcId: !Ref VPC 
# ------------------------------------------------------------#
# NATGateway
# ------------------------------------------------------------#
  EIP: 
    Type: "AWS::EC2::EIP"
    Properties: 
      Domain: vpc
  NATGateway: 
    Type: "AWS::EC2::NatGateway"
    Properties: 
      AllocationId: !GetAtt EIP.AllocationId 
      SubnetId: !Ref PublicSubnet1a
      Tags: 
        - Key: Name
          Value: !Sub "${ResourceName}-natgw"
# ------------------------------------------------------------#
# RouteTable
# ------------------------------------------------------------#
  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub "${ResourceName}-PublicRouteTable"
  PublicRoute: 
    Type: "AWS::EC2::Route"
    Properties: 
      RouteTableId: !Ref PublicRouteTable
      DestinationCidrBlock: "0.0.0.0/0"
      GatewayId: !Ref InternetGateway 
  PublicSubnet1aAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnet1a
      RouteTableId: !Ref PublicRouteTable 
  PublicSubnet1cAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnet1c
      RouteTableId: !Ref PublicRouteTable
  PrivateRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub "${ResourceName}-PrivateRouteTable"
  PrivateRoute: 
    Type: "AWS::EC2::Route"
    Properties: 
      RouteTableId: !Ref PrivateRouteTable
      DestinationCidrBlock: "0.0.0.0/0"
      NatGatewayId: !Ref NATGateway
  PrivateSubnet1aAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PrivateSubnet1a
      RouteTableId: !Ref PrivateRouteTable
  PrivateSubnet1cAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PrivateSubnet1c
      RouteTableId: !Ref PrivateRouteTable 
# ------------------------------------------------------------#
# Security Group
# ------------------------------------------------------------#
  LambdaSecurityGrouptoFull:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: !Sub "${ResourceName}-outbound-full-sg"
      GroupDescription: Security Group for Lambda to full
      VpcId: !Ref VPC
      SecurityGroupEgress:
        - IpProtocol: -1
          CidrIp: 0.0.0.0/0
# ------------------------------------------------------------#
# Lambda
# ------------------------------------------------------------#
  LambdaFunction:
    Type: 'AWS::Lambda::Function'
    Properties:
      Handler: index.lambda_handler
      Role: !GetAtt LambdaExecutionRole.Arn
      FunctionName: !Sub "${ResourceName}-get-api"
      Runtime: python3.12
      Timeout: 3
      MemorySize: 128
      Code:
        ZipFile: |
          import boto3
          import json
          from datetime import date, datetime

          client_api = boto3.client('apigateway')

          def json_serial(obj):
              # 日付型の場合には、文字列に変換します
              if isinstance(obj, (datetime, date)):
                  return obj.isoformat()
              # 上記以外はサポート対象外
              raise TypeError("Type %s not serializable" % type(obj))

          def lambda_handler(event, context):
              response = client_api.get_rest_apis()
              response_serial = json.dumps(response, default=json_serial)
              print(response_serial)        
              return response_serial
      VpcConfig:
        SecurityGroupIds:
          - !Ref LambdaSecurityGrouptoFull
        SubnetIds:
          - !Ref PrivateSubnet1a
          - !Ref PrivateSubnet1c
  FunctionLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Sub "/aws/lambda/${LambdaFunction}"            
# ------------------------------------------------------------#
# IAM Role
# ------------------------------------------------------------#
  LambdaExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub "${ResourceName}-IRL-LAMBDA"
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: 'Allow'
            Principal:
              Service: 'lambda.amazonaws.com'
            Action: 'sts:AssumeRole'
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
        - arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole 
        - !GetAtt LambdaPolicy.PolicyArn        
  LambdaPolicy:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      ManagedPolicyName: !Sub ${ResourceName}-lambda-policy
      Description: IAM Managed Policy with APIGateway GET Access
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: 'Allow'
            Action:
              - 'apigateway:GET'              
            Resource:
              - '*' 

API情報取得の確認

Lambdaにて、テストイベントを発行します。

以下のように、ロググループ内にてAPI情報が取得できていれば成功です。

最後に

いかがだったでしょうか。

各種AWSサービスがどのような形でサービスエンドポイントを提供しているか解説しました。API GatewayのVPCエンドポイントの利用用途としては、プライベート型API Gatewayの呼び出しに使用できることをご留意ください。

閉域網環境でAWSを利用する際は、各種AWSサービスがVPCエンドポイントをサポートしているか確認していただければ幸いです。

本記事が皆様のお役にたてば幸いです。

ではサウナラ~🔥

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