[AWS SAM]API Gateway + Lambdaでipアドレスを取得する方法

# はじめに
API GatewayとLambdaを使用して、リクエスト元のIPアドレスを取得する方法を紹介します。
AWS Serverless Application Model（SAM）のテンプレートを使用して、API Gatewayを設定し、OpenAPI（Swagger）を使用してAPIのスキーマを定義します。Lambda関数をデプロイして、API Gatewayに統合します。そして、Lambda関数のコードで、リクエスト元のIPアドレスを取得します。

普通にリクエストを送ってもAPI Gatewayを通してしまうとリクエスト元のIPアドレス取れないので、マッピングテンプレートを使います。その定義も含めてSAMテンプレートに記述します。


# SAM template
まず、SAMテンプレートを使用して、API GatewayとLambda関数を設定します。以下は、SAMテンプレートの例です。

```yaml:template.yaml
Resources:
  MyApi:
    Type: AWS::Serverless::Api
    Properties:
      StageName: prod
      OpenApiVersion: 3.0.2
      DefinitionBody:
        Fn::Transform:
          Name: AWS::Include
          Parameters:
            Location: ./apigateway.yaml
  MyFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: .
      Handler: app.lambda_handler
      Runtime: python3.8
      Policies:
        - AmazonAPIGatewayInvokeFullAccess
      Environment:
        Variables:
          STAGE: prod
      Events:
        MyApiEvent:
          Type: Api
          Properties:
            RestApiId: !Ref MyApi
            Path: /myresource
            Method: GET
```

このSAMテンプレートでは、AWS::Serverless::ApiリソースとAWS::Serverless::Functionリソースが定義されています。API Gatewayの定義には、OpenAPIスキーマが使用されています。x-amazon-apigateway-integrationオブジェクトで、Lambda関数との統合が定義されています。Lambda関数は、AWS::Serverless::Functionリソースで定義され、API Gatewayによって呼び出されます。このLambda関数のコードで、リクエスト元のIPアドレスを取得します。

# OpenAPI
SAMテンプレートのAPI Gateway定義には、OpenAPI（Swagger）を使用しています。OpenAPIは、APIのスキーマを定義するためのフレームワークです。以下は、このSAMテンプレートで使用されるOpenAPIの例です。

```yaml: apigateway.yaml
swagger: "2.0"
info:
  title: "My API"
paths:
  /myresource:
    get:
      responses:
        '200':
          description: "Success"
          content:
            application/json:
              schema:
                type: object
                properties:
                  content:
                    type: string
      x-amazon-apigateway-integration:
        uri:
          Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunction.Arn}/invocations
        passthroughBehavior: when_no_templates
        httpMethod: GET
        requestTemplates:
          application/json: '{"body": $input.json("$"), "headers": { "X-Forwarded-For": "$context.identity.sourceIp" }}'
        type: aws_proxy
```

このOpenAPIスキーマでは、`/myresource`エンドポイントの`GET`リクエストが定義されています。このエンドポイントは、API GatewayからLambda関数に統合されています。`x-amazon-apigateway-integration`オブジェクトで、Lambda関数の呼び出し方法が定義されています。味噌は`"headers": { "X-Forwarded-For": "$context.identity.sourceIp"`で、ここでリクエスト元のIPアドレスを渡しています。

Lambda側で`event.requestContext.identity.sourceIp`で取得できます。

# まとめ
SAM templateって意外に資料が少なくて困ること多いので備忘録的に残しておきます。
ちなみにこの記事はChatGPT3.5使って書いて、ちょっと直しただけなので結構すぐ書けました（便利）
最初はマッピングテンプレートだけコンソールで追加しようと思ったんですが、マッピングテンプレートを追加する場所がわからず、結局templateで書くことにしました。

# 参考文献

- [Working with API Gateway extensions to OpenAPI](https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-swagger-extensions.html)
- [API Gateway + Lambda でアクセス元IPアドレスを取得するのに手こずった話](https://zenn.dev/m_iskw/articles/de6050749299fb)
- [AWS SAMでAPI Gateway、Lambda、Layersをサクッとデプロイできるようにする](https://qiita.com/shimajiri/items/e22b18188051c2199582)