API Gatewayのカスタムアクセスログにクエリ文字列やヘッダー情報を出力してみる

2022.05.12

いわさです。

API Gatewayでカスタムアクセスログを出力際には、ログ変数を使うことで必要な情報を出力することが出来ます。
出力可能な項目については以下にまとまっているのですが、リクエストヘッダーやクエリ文字列をログ出力することは出来ません。

そして、先日以下のような記事を紹介させて頂きました。
ここでは、カスタムオーソライザーを使って、コンテキスト情報を追加することで標準のログ変数でサポートされていない情報を追加しました。

本日はこの方法を応用し、標準のログ変数で対応出来ないリクエストヘッダーとクエリ文字列を、API Gatewayのカスタムアクセスログに出力してみました。

カスタムオーソライザーを作成

Lambdaカスタムオーソライザーをリクエストパラメータベースで作成します。
トークンベースでは、Lambdaイベントで扱える情報が少ないためです。

ここで、以下のようなヘッダー、クエリパラメータ付きのリクエストを送信すると、カスタムオーソライザーでは以下の形式で情報を取得することが出来ます。

curl -H "hogeheader1:headervalue1" -H "hogeheader2:headervalue2" "https://jx6vgoj9e8.execute-api.ap-northeast-1.amazonaws.com/fuga-stage?hogeparam1=hogevalue1&hogeparam2=hogevalue2"
{
    "type": "REQUEST",
    "methodArn": "arn:aws:execute-api:ap-northeast-1:123456789012:jx6vgoj9e8/fuga-stage/GET/",
    "resource": "/",
    "path": "/",
    "httpMethod": "GET",
    "headers": {
        "accept": "*/*",
        "hogeheader1": "headervalue1",
        "hogeheader2": "headervalue2",
        "Host": "jx6vgoj9e8.execute-api.ap-northeast-1.amazonaws.com",
        "user-agent": "curl/7.64.1",
        "X-Amzn-Trace-Id": "Root=1-6279c421-26a3ff7e3f1dec0e12332956",
        "X-Forwarded-For": "203.0.113.1",
        "X-Forwarded-Port": "443",
        "X-Forwarded-Proto": "https"
    },
    "multiValueHeaders": {
        "accept": [
            "*/*"
        ],
        "hogeheader1": [
            "headervalue1"
        ],
        "hogeheader2": [
            "headervalue2"
        ],
        "Host": [
            "jx6vgoj9e8.execute-api.ap-northeast-1.amazonaws.com"
        ],
        "user-agent": [
            "curl/7.64.1"
        ],
        "X-Amzn-Trace-Id": [
            "Root=1-6279c421-26a3ff7e3f1dec0e12332956"
        ],
        "X-Forwarded-For": [
            "203.0.113.1"
        ],
        "X-Forwarded-Port": [
            "443"
        ],
        "X-Forwarded-Proto": [
            "https"
        ]
    },
    "queryStringParameters": {
        "hogeparam2": "hogevalue2",
        "hogeparam1": "hogevalue1"
    },
    "multiValueQueryStringParameters": {
        "hogeparam2": [
            "hogevalue2"
        ],
        "hogeparam1": [
            "hogevalue1"
        ]
    },
    "pathParameters": {},
    "stageVariables": {},
    "requestContext": {
        "resourceId": "r98fkm8nuh",
        "resourcePath": "/",
        "httpMethod": "GET",
        "extendedRequestId": "R4uVOGqiNjMF2mw=",
        "requestTime": "10/May/2022:01:47:13 +0000",
        "path": "/fuga-stage",
        "accountId": "123456789012",
        "protocol": "HTTP/1.1",
        "stage": "fuga-stage",
        "domainPrefix": "jx6vgoj9e8",
        "requestTimeEpoch": 1652147233237,
        "requestId": "490a7cbb-6ff7-4d95-bd0e-903a1b96d1b9",
        "identity": {
            "cognitoIdentityPoolId": null,
            "accountId": null,
            "cognitoIdentityId": null,
            "caller": null,
            "sourceIp": "203.0.113.1",
            "principalOrgId": null,
            "accessKey": null,
            "cognitoAuthenticationType": null,
            "cognitoAuthenticationProvider": null,
            "userArn": null,
            "userAgent": "curl/7.64.1",
            "user": null
        },
        "domainName": "jx6vgoj9e8.execute-api.ap-northeast-1.amazonaws.com",
        "apiId": "jx6vgoj9e8"
    }
}

あとは、必要な情報を関数の返却値のContextプロパティに設定すると、カスタムアクセスログのフォーマットでは$context.authorizer.hogehogeでアクセスすることが出来るようになります。

lambda_function.py

import json, datetime

def lambda_handler(event, context):
    print(json.dumps(event))
    return {
        'principalId': 'hoge',
        'policyDocument': {
            'Version': '2012-10-17',
            'Statement': [{
                'Action': 'execute-api:Invoke',
                'Effect': 'Allow',
                'Resource': event['methodArn']
            }]
        },
        'context': {
            'proto': event['headers']['X-Forwarded-Proto'],
            'host': event['headers']['Host'],
            'path': event['requestContext']['path'],
            'queryString': json.dumps(event['queryStringParameters']),
            'headers': json.dumps(event['headers'])
        }
    }

カスタムアクセスログの設定とログの確認

さいごに、API Gatewyaのステージでカスタムアクセスログのフォーマットに以下のように設定を行いました。

{
    "requestId": "$context.requestId",
    "proto": "$context.authorizer.proto",
    "host": "$context.authorizer.host",
    "path": "$context.authorizer.path",
    "queryString": "$context.authorizer.queryString",
    "headers": "$context.authorizer.headers"
}

最終的に取得出来たカスタムアクセスログは以下のようになります。
JSONダンプしているのエスケープされてしまっていますが、ヘッダーとクエリ文字列をログへ出力することが出来ました。

{
    "requestId": "fb35b359-2558-466d-ba1f-7279faa50348",
    "proto": "https",
    "host": "jx6vgoj9e8.execute-api.ap-northeast-1.amazonaws.com",
    "path": "/fuga-stage",
    "queryString": "{"hogeparam2": "hogevalue2", "hogeparam1": "hogevalue1"}",
    "headers": "{"accept": "*/*", "hogeheader1": "headervalue1", "hogeheader2": "headervalue2", "Host": "jx6vgoj9e8.execute-api.ap-northeast-1.amazonaws.com", "user-agent": "curl/7.64.1", "X-Amzn-Trace-Id": "Root=1-6279e8e8-72cf80b0166f96947162b45e", "X-Forwarded-For": "203.0.113.1", "X-Forwarded-Port": "443", "X-Forwarded-Proto": "https"}" 
}

さいごに

前回の記事に引き続き無理やり出力してみました。
ご注意頂きたいのは、この記事はアクセスログにヘッダーやクエリ文字列を出力することを推奨するものではないという点と、カスタム認証のためでなくコンテキストを追加するためだけにLambdaオーソライザーを使用しているという点です。

セキュリティ面やオーソライザーの正しい使い方などは無視して、出力出来るのか出来ないのかを試したものになりますので、参考にする際はご注意ください。