外形監視に AWS Synthetics Canary を導入する場合に Cognito 認証と組み合わせる方法について調査した内容を備忘録として残しておく

方法

  1. CloudWatch > Synthetics Canary を選択
  2. 「Canary を作成」ボタンを押下
  3. 以下を設定
    • 設計図を使用する
    • 設計図: API Canary
    • Canary ビルダー: 任意の Canary 名
    • Amazon API Gateway API を使用中: チェックを入れる
    • API を選択: API Gateway から API とステージを選択
      • 使用している API, ステージを選択
    • Host Name: 使用している Host Name を指定
    • HTTP リクエスト
      • HTTP リクエストを追加ボタンを押下
        • 監視したい API の情報を設定
    • スクリプトエディタ: スクリプトを参照
    • スケジュール: 任意のスケジュールを選択
    • アクセス許可
      • IAM ロール: 既存のロールを選択
  4. 「保存」ボタンを押下

実際のコード

スクリプト

基本的にはデフォルトのままで良いが Request Header に Cognito の ID トークンを付与する必要がある

const synthetics = require('Synthetics');
const log = require('SyntheticsLogger');
const syntheticsConfiguration = synthetics.getConfiguration();
const AWS = require('aws-sdk'); // 追加

const apiCanaryBlueprint = async function () {
    const response = getIdToken(); // 追加
    const bearerToken = (await response).AuthenticationResult.IdToken; // 追加

    syntheticsConfiguration.setConfig({
        restrictedHeaders: [], // Value of these headers will be redacted from logs and reports
        restrictedUrlParameters: [] // Values of these url parameters will be redacted from logs and reports
    });

    // Handle validation for positive scenario
    const validateSuccessful = async function(res) {
        return new Promise((resolve, reject) => {
            if (res.statusCode < 200 || res.statusCode > 299) {
                throw new Error(res.statusCode + ' ' + res.statusMessage);
            }

            let responseBody = '';
            res.on('data', (d) => {
                responseBody += d;
            });

            res.on('end', () => {
                // Add validation on 'responseBody' here if required.
                resolve();
            });
        });
    };


    // Set request option for 検証 https://api.xxx-xxx.com
    let requestOptionsStep1 = {
        hostname: 'api.xxx-xxx.com',
        method: 'GET',
        path: '/users/xxx',
        port: '443',
        protocol: 'https:',
        body: "",
        headers: {
            'Authorization': 'Bearer ' + bearerToken, // 追加
        }
    };
    requestOptionsStep1['headers']['User-Agent'] = [synthetics.getCanaryUserAgentString(), requestOptionsStep1['headers']['User-Agent']].join(' ');

    // Set step config option for 検証 https://api.xxx-xxx.com
   let stepConfig1 = {
        includeRequestHeaders: true,
        includeResponseHeaders: true,
        restrictedHeaders: ['X-Amz-Security-Token', 'Authorization'], // Restricted header values do not appear in report generated.
        includeRequestBody: true,
        includeResponseBody: true,
        continueOnHttpStepFailure: true
    };

    // 修正 Value contains non-ASCII characters. になるので 検証 => 削除
    await synthetics.executeHttpStep('https://api.xxx-xxx.com', requestOptionsStep1, validateSuccessful, stepConfig1);
};

exports.handler = async () => {
    return await apiCanaryBlueprint();
};

// 以下の関数追加
// ID トークン取得
async function getIdToken() {
    const secretsManager = new AWS.SecretsManager({
        region: {RegionName},
    });
    const sec_response = await secretsManager
        .getSecretValue({
            SecretId: {SecretId},
        })
        .promise();

    const secretObject = JSON.parse(sec_response.SecretString);

    try {
        const cognito = new AWS.CognitoIdentityServiceProvider();
        return await cognito.initiateAuth({
          AuthFlow: 'USER_PASSWORD_AUTH',
          ClientId: secretObject.client_id,
          AuthParameters: {
            USERNAME: secretObject.email,
            PASSWORD: secretObject.password,
          },
        }).promise();
    } catch (err) {
        throw err;
    }
}

ポリシー

追加部分のみ抜粋
cognito-idp:InitiateAuth へのアクセス許可が必要
※Secret Manager は必要に応じて追加

{
    "Version": "2012-10-17",
    "Statement": [
        ・・・
        {
            "Effect": "Allow",
            "Action": [
                "secretsmanager:GetResourcePolicy",
                "secretsmanager:GetSecretValue",
                "secretsmanager:DescribeSecret",
                "secretsmanager:ListSecretVersionIds"
            ],
            "Resource": [
                "arn:aws:secretsmanager:ap-northeast-1:xxxxxxxxxxxx:secret:*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "cognito-idp:InitiateAuth"
            ],
            "Resource": [
                "arn:aws:cognito-idp:ap-northeast-1:xxxxxxxxxxxx:userpool/ap-northeast-1_xxxxxxxxx"
            ]
        }
    ]
}