My Note

自己理解のためのブログ

GoでSlackにMessageを投稿する( slack-go/slack )

今回は下記に書いたSlack APIでメッセージをPostする内容の更新版になります。

yhidetoshi.hatenablog.com

今回利用するライブラリは slack-go/slackです。

スター数も多いですしさまざまな機能が実装されているので今後はこちらを使っていこうと思います。

github.com

今回のコードを作成するにあたり参考にしたのは以下です。

package main

import (
    "fmt"
    "os"

    "github.com/slack-go/slack"
)

var (
    SLACK_TOKEN = os.Getenv("SLACK_TOKEN")
    CHANNEL_ID  = os.Getenv("CHANNEL_ID")
)

func main() {
    api := slack.New(SLACK_TOKEN)
    attachment := slack.Attachment{
        Pretext:    "This is slack post test by Go",
        Title:      "title",
        Color:      "#36a64f",
        AuthorName: "author_name",
        AuthorIcon: "https://placeimg.com/16/16/people",
        MarkdownIn: []string{"`textTomarkdown`"},
        Text:       "hello world `textTomarkdown`",
        ThumbURL:   "http://placekitten.com/g/200/200",
        FooterIcon: "https://platform.slack-edge.com/img/default_application_icon.png",

        Fields: []slack.AttachmentField{
            slack.AttachmentField{
                Title: "Item1",
                Value: "this is value of item1",
                Short: false,
            }, slack.AttachmentField{
                Title: "Item2",
                Value: "this is value of item2",
                Short: true,
            }, slack.AttachmentField{
                Title: "Item3",
                Value: "```" + "this is value of item3" + "```",
                Short: false,
            },
        },
    }

    channelID, timestamp, err := api.PostMessage(
        CHANNEL_ID,
        slack.MsgOptionText("This is Title", false),
        slack.MsgOptionAttachments(attachment),
        slack.MsgOptionAsUser(true),
    )
    if err != nil {
        fmt.Printf("%s\n", err)
        return
    }
    fmt.Printf("Message successfully sent to channel %s at %s", channelID, timestamp)
}

必要な接続情報は環境変数化しているので適宜設定してください。 OAuthTokenの確認はこちらの記事に記載しています。

【アップデート】GuardDutyの結果をLambda ( Go ) でSlackに通知する - My Note

投稿するチャンネルIDは各チャンネルの詳細で確認できます。

export SLACK_TOKEN=xoxb-***
export CHANNEL_ID=***

実行した結果

f:id:yhidetoshi:20211217161002p:plain

【アップデート】GuardDutyの結果をLambda ( Go ) でSlackに通知する

はじめに

今回は以前に書いた "GuardDutyの結果をLambda ( Go ) でSlackに通知する" 内容をアップデートしました。 主な目的はslack通知に利用するライブラリを新たに検証したかった事と、CloudwatchEventからEventBridgeに変わり内容を確認したかったためです。

yhidetoshi.hatenablog.com

主なアップデート内容は以下の通りです。

  • Slack通知をwebhookからSlackAppを作成して通知するように変更
  • Goのパッケージ構成を変更
  • slack通知に利用するライブラリを slack-go を利用するように変更
  • CloudwatchEventからEventBridgeへ変更

SlackAppを利用する

  • Create New App f:id:yhidetoshi:20211217133010p:plain

  • workspace を指定する f:id:yhidetoshi:20211217133035p:plain

  • App Manifest を以下に設定しました。 bot のスコープは特定チャンネルに書き込みできればいいので chat:write を設定。

_metadata:
  major_version: 1
  minor_version: 1
display_information:
  name: notice
features:
  app_home:
    home_tab_enabled: false
    messages_tab_enabled: false
    messages_tab_read_only_enabled: false
  bot_user:
    display_name: notice
    always_online: true
oauth_config:
  scopes:
    bot:
      - chat:write
  • Install to Workspace を実行する f:id:yhidetoshi:20211217133235p:plain

  • Bot User OAuth Token を確認。このトークンを利用する。 f:id:yhidetoshi:20211217133400p:plain

パッケージ構成

mainとhandlerとslackの処理でパッケージを分離しました。前回はmain.goだけで処理を書いていたので 目的別に分離した形になります。(もっとキレイに分けれると思いますが今回はパッケージを分けて実行してみるのが目的のためにひとまずこれで。)

guardduty-lambda
├── README.md
├── go.mod
├── go.sum
├── handler ( handlerの処理を実装 )
 │   └── handler.go
├── main.go ( handlerの呼び出し )
├── notification
 │  └── slack.go ( slack通知の処理を実装 )
└── serverless.yml
  • go modコマンド
go mod init guardduty-lambda
  • main.go (一部抜粋)
package main

import (
    "guardduty-lambda/handler"
    "github.com/aws/aws-lambda-go/lambda"
)
  • handler/handler.go (一部抜粋)
package handler

import (
    "guardduty-lambda/notification"
         ・・・
)
  • notification/slack.go (一部抜粋)
package notification

import (
    "github.com/slack-go/slack"
         ・・・
)

EventBridgeについて

前回の記事ではCloudWatchEventで設定していました。 $serverless deploy で 自動作成されますが EventBridgeを確認します。

  • serveless.yamlの一部抜粋
functions:
  slack:
    handler: main
    role: GuardDutyLambda
    timeout: 30
    description: GuardDutyLambda slack notice
    memorySize: 128
    environment:
      SLACK_TOKEN: ${opt:slacktoken}
      CHANNEL_ID: ${opt:channelid}
      THRESHOLD: ${opt:threshold}
    events:
      - eventBridge:
          pattern:
            source:
              - aws.guardduty
            detail-type:
              - GuardDuty Finding

f:id:yhidetoshi:20211217140559p:plain

  • 作成されたイベントパターン
{
  "detail-type": ["GuardDuty Finding"],
  "source": ["aws.guardduty"]
}
  • イベントパターンを試すために確認するテストデータ(自分でコンソールにコピペして確認)
{
    "version": "0",
    "id": "111111111-11111-caae-111111111",
    "detail-type": "GuardDuty Finding",
    "source": "aws.guardduty",
    "account": "12345678910",
    "time": "2021-12-16T01:10:00Z",
    "region": "ap-northeast-1",
    "resources": [],
    "detail": {
        "schemaVersion": "2.0",
        "accountId": "XXX",
        "region": "ap-northeast-1",
        "type": "UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration",
        "resource": {
            "resourceType": "AccessKey",
            "accessKeyDetails": {
                "accessKeyId": "GeneratedFindingAccessKeyId",
                "principalId": "GeneratedFindingPrincipalId",
                "userType": "IAMUser",
                "userName": "GeneratedFindingUserName"
            }
        },
        "severity": 8,
        "title": "Credentials for instance role GeneratedFindingUserName used from external IP address.",
        "description": "Credentials created exclusively for an EC2 instance using instance role GeneratedFindingUserName have been used from external IP address 198.51.100.0."
    }
}

Slack通知について

前回の記事では、このslackのライブラリを利用していました。

github.com

今回はこちらの方がスター数もかなり多く多機能で活発にコミットされているため利用しました。

github.com

ソースコードは下記にあります。 https://github.com/yhidetoshi/GuardDutyAlertSlack/blob/main/notification/slack.go

  • Slackへの通知結果

f:id:yhidetoshi:20211217142039p:plain

さいごに

以前に作成したAWS GuardDutyの通知についてアップデートを行いました。 slack-goについては利用しやすくメッセージをpostする以外にも色々と機能が実装されているのでこれからはこのライブラリを 積極的に活用していこうと思います。

ソースコードは下記のGitHubにあります。 github.com

Lambda@Edge( Nodejs )でリダイレクト設定をする

はじめに

今回はLambda@Edge( Python3 )を使ってリダイレクト設定しました。 本記事ではLambda@Edgeについてしか触れないので、構成やCloudFront、Lambda@Edgeの設定については下記の記事に記載しています。 下記の記事はPython3で実装しており、本記事は一部のパターンを省略していますがその記事のNodejs版になります。

yhidetoshi.hatenablog.com

Lambda@Edgeについて

コードと動作確認

今回は動作確認にLambdaのテスト機能を使ってテストイベントを利用して確認しました。

すべてのリクエストを別ホストにリダイレクトする場合

  • redirect.js
'use strict';

var REDIRECT_TARGET_URL = 'http://fuga.cloudfront.net';

exports.handler = (event, context, callback) => {
    const request = event.Records[0].cf.request;


    var redirectResponse = {
        status: '301',
        statusDescription: 'Moved Permanently',
        body: 'Authentication Failed',
        headers: {
            'location': [
                {
                    key: 'Location',
                    value: REDIRECT_TARGET_URL
                }
            ]
        },
    }
    callback(null, redirectResponse);
}

動作確認

  • テストイベントデータ
    • host.value = hoge.cloudfront.net
{
    "Records": [
        {
            "cf": {
                "config": {
                    "distributionId": "EXAMPLE"
                },
                "request": {
                    "headers": {
                        "host": [
                            {
                                "key": "Host",
                                "value": "hoge.cloudfront.net"
                            }
                        ],
                        "user-name": [
                            {
                                "key": "User-Name",
                                "value": "CloudFront"
                            }
                        ]
                    },
                    "clientIp": "0.0.0.0",
                    "uri": "/index.html",
                    "method": "GET"
                },
                "response": {
                    "status": "200",
                    "statusDescription": "OK",
                    "headers": {
                        "x-cache": [
                            {
                                "key": "X-Cache",
                                "value": "Hello from Cloudfront"
                            }
                        ]
                    }
                }
            }
        }
    ]
}
  • 実行結果
{
  "status": "301",
  "statusDescription": "Moved Permanently",
  "body": "Authentication Failed",
  "headers": {
    "location": [
      {
        "key": "Location",
        "value": "http://fuga.cloudfront.net"
      }
    ]
  }
}
  • 結果を確認
    • status301 に。
    • locationhttp://fuga.cloudfront.net に。

特定のパス配下のアクセスを同じホストの別パスにリダイレクトする (/v1/ -> /v2)/

hoge.cloudfront.net/v1/index.html --> hoge.cloudfront.net/v2/index.html

  • redirect.js
'use strict';

var PATH_PATTERN = '^\/v1\/';

exports.handler = (event, context, callback) => {
    const request = event.Records[0].cf.request;
    const headers = request.headers;
    const host = headers.host[0].value;
    const uri = request.uri;

    const redirectURL = 'http://' + host + '/v2/index.html'

    if (uri.match(PATH_PATTERN)) {
        var redirectResponse = {
            status: '301',
            statusDescription: 'Moved Permanently',
            body: 'Authentication Failed',
            headers: {
                'location': [
                    {
                        key: 'Location',
                        value: redirectURL
                    }
                ]
            },
        }
        callback(null, redirectResponse);
    }
    callback(null, request)
}

動作確認

  • テストイベントデータ
    • host.value = hoge.cloudfront.net
    • uri = /v1/index.html
{
  "Records": [
    {
      "cf": {
        "config": {
          "distributionId": "EXAMPLE"
        },
        "request": {
          "headers": {
            "host": [
              {
                "key": "Host",
                "value": "hoge.cloudfront.net"
              }
            ],
            "user-name": [
              {
                "key": "User-Name",
                "value": "CloudFront"
              }
            ]
          },
          "clientIp": "0.0.0.0",
          "uri": "/v1/index.html",
          "method": "GET"
        },
        "response": {
          "status": "200",
          "statusDescription": "OK",
          "headers": {
            "x-cache": [
              {
                "key": "X-Cache",
                "value": "Hello from Cloudfront"
              }
            ]
          }
        }
      }
    }
  ]
}
  • 結果を確認
    • status301 に。
    • locationhttp://hoge.cloudfront.net/v2/index.html に。

ソースコードは以下のGitHubにおいてあります。

github.com

CloudFrontとLambda@Edge ( Nodejs )とS3で静的ページにIPアドレス制限とBasic認証を設定する

はじめに

今回はLambda@Edge( Nodejs14 )を使ってBasic認証とIP制限を設定しました。 本記事ではLambda@Edgeについてしか触れないので、構成やCloudFront、Lambda@Edgeの設定については下記の記事に記載しています。 下記の記事はPython3で実装しており、本記事はそれのNodejs版になります。

yhidetoshi.hatenablog.com

Lambda@Edgeについて

Basic認証とIP制限について

  • allowUsersに許可する User:Password を定義する
  • allowIpBasic認証を必要としない IPアドレスを定義する

Nodejsでbase64デコードする処理は以下のサイトを参考にしています。 docs.aws.amazon.com

  • basicauth.js
'use strict';

var errorResponse = {
    status: '401',
    statusDescription: 'Unauthorized',
    body: 'Authentication Failed',
    headers: {
        'www-authenticate': [
            {
                key: 'WWW-Authenticate', value: 'Basic'
            }
        ]
    },
}

var allowIp = ['X.X.X.X', 'X.X.X.X']

var allowUsers = {
    "admin": "pass1",
    "dev": "pass2"
}


exports.handler = (event, context, callback) => {
    const request = event.Records[0].cf.request;
    const headers = request.headers;
    const clientIp = request.clientIp

    if (validateClientIp(clientIp)) {
        callback(null, request)
    }

    if (! typeof headers.authorization) {
        callback(null, errorResponse)
    }

    let encodeAuth = headers.authorization[0].value.split(" ");
    let decodeAuth = Buffer.from(encodeAuth[1], 'base64').toString('ascii').split(":");

    const requestUser = decodeAuth[0];
    const requestPassword = decodeAuth[1];
    if (validateAuth(requestUser, requestPassword)) {
        callback(null, request)
    } else {
        callback(null, errorResponse)
    }
};

function validateAuth(user, password) {
    let exist_flag = false
    for (let key in allowUsers) {
        if (user === key && password === allowUsers[key]) {
            exist_flag = true
            return true;
        }
    }
    if (exist_flag === false) {
        return false;
    }
}

function validateClientIp(clientIp) {
    if (allowIp.indexOf(clientIp) !== -1) {
        return true
    } else {
        return false
    }
}

動作確認について

CloudFrontを用意してクライアントから動作確認しなくても、CloudFrontから受け取る eventデータを Lambda@Edgeの テストイベント に設定して確認ができます。 動作確認で設定するのは、 clientIpIPアドレスを、authorizationuser:password をbasic64をエンコードした値を動作確認したい内容に応じて適宜設定します。

echo -n dev:pass2 | base64
ZGV2OnBhc3My
{
    "Records": [
        {
            "cf": {
                "config": {
                    "distributionId": "EXAMPLE"
                },
                "request": {
                    "headers": {
                        "host": [
                            {
                                "key": "Host",
                                "value": "d123.cf.net"
                            }
                        ],
                        "authorization": [
                            {
                                "value": "Basic ZGV2OnBhc3My"
                            }
                        ],
                        "user-name": [
                            {
                                "key": "User-Name",
                                "value": "CloudFront"
                            }
                        ]
                    },
                    "clientIp": "0.0.0.0",
                    "uri": "/index.html",
                    "method": "GET"
                },
                "response": {
                    "status": "200",
                    "statusDescription": "OK",
                    "headers": {
                        "x-cache": [
                            {
                                "key": "X-Cache",
                                "value": "Hello from Cloudfront"
                            }
                        ]
                    }
                }
            }
        }
    ]
}

ソースコードGitHubにあります。

github.com

CloudFrontとLambda@Edge(Python3)とS3でリダイレクト設定をする

はじめに

今回はCloudFrontとLambda@Edgeでリダイレクトを設定しました。 CloudFront、Lambda@Edge、IAMに関しては下記の前回の記事を参照ください。

yhidetoshi.hatenablog.com

構成

f:id:yhidetoshi:20211209130510p:plain

リダイレクト元と先のサイトをそれぞれ簡易な静的ページをCloudFrontとS3で用意しました。 S3のバケットにそれぞれ以下のパスで用意。*) hoge, fugaは記事用に修正したものです。

  • サイトA
    • CloudFrontA
      • hoge.cloudfront.net
    • S3(Origin)
      • hoge-cloudfront-net
    • Lambda@Edge(リダイレクト用)
  • サイトB
    • CloudFrontB
      • fuga.cloudfront.net/
    • S3(Origin)
      • fuga-cloudfront-net

それぞれのS3バケットのパス

hoge-cloudfront-net /
      index.html
      /v1/index.html
      /v2/index.html

fuga-cloudfront-net /
      index.html
      /v1/index.html
      /v2/index.html

リダイレクト設定(Lambda@Edge)

パターン1: すべてのリクエストを別ホストにリダイレクト

REDIRECT_TARGET_HOST = 'fuga.cloudfront.net'


def lambda_handler(event, context):
    redirect_response = {
        'status': '301',
        'statusDescription': 'Moved Permanently',
        'headers': {
            'location': [
                {
                    'key': 'Location',
                    'value': f'http://{REDIRECT_TARGET_HOST}'
                }
            ]
        }
    }
    return redirect_response

パターン2: 特定のパス配下のアクセスを同じホストの別パスにリダイレクト

- hoge.cloudfront.net/v1/index.html --> hoge.cloudfront.net/v2/index.html
import re

PATH_PATTERN = '^\/v1\/'


def lambda_handler(event, context):
    request = event['Records'][0]['cf']['request']
    headers = request['headers']

    host = headers['host'][0]['value']
    uri = request['uri']

    if (re.match(PATH_PATTERN, uri)):
        redirect_response = {
            'status': '301',
            'statusDescription': 'Moved Permanently',
            'headers': {
                'location': [
                    {
                        'key': 'Location',
                        'value': f'http://{host}/v2/index.html'
                    }
                ]
            }
        }
        return redirect_response
    return request

パターン3: 特定のパス配下のアクセスを別ホストにリダイレクト(同じパス)

- hoge.cloudfront.net/index.html --> hoge.cloudfront.net/index.html
- hoge.cloudfront.net/v1/index.html --> fuga.cloudfront.net/v1/index.html
- hoge.cloudfront.net/v2/index.html --> fuga.cloudfront.net/v2/index.html
import re

PATH_PATTERN = '^\/v1\/|^\/v2\/'
REDIRECT_TARGET_HOST = 'fuga.cloudfront.net'


def lambda_handler(event, context):
    request = event['Records'][0]['cf']['request']
    uri = request['uri']

    if (re.match(PATH_PATTERN, uri)):
        redirect_response = {
            'status': '301',
            'statusDescription': 'Moved Permanently',
            'headers': {
                'location': [
                    {
                        'key': 'Location',
                        'value': f'http://{REDIRECT_TARGET_HOST}{uri}'
                    }
                ]
            }
        }
        return redirect_response
    return request

パターン4: ホスト名指定の場合はリダイレクトしない(同じパス)

- hoge.cloudfront.net --> hoge.cloudfront.net
- hoge.cloudfront.net/index.html --> fuga.cloudfront.net/index.html
REDIRECT_TARGET_HOST = 'fuga.cloudfront.net'

def lambda_handler(event, context):
    request = event['Records'][0]['cf']['request']
    headers = request['headers']

    if headers['host'][0]['value'] == REDIRECT_TARGET_HOST:
        return request

    uri = request['uri']

    redirect_response = {
        'status': '301',
        'statusDescription': 'Moved Permanently',
        'headers': {
            'location': [
                {
                    'key': 'Location',
                    'value': f'http://{REDIRECT_TARGET_HOST}{uri}'
                }
            ]
        }
    }

    return redirect_response[]

ソースコードは下記のGitHubにあります。

github.com

CloudFrontとLambda@Edge ( Python3 )とS3で静的ページにIPアドレス制限とBasic認証を設定する

はじめに

今回はCloudFrontとLambda@Edge ( Python3.8 )を使ってオリジンに対してIP制限とBasic認証を設定しました。 LambdaEdgeとIAMについてはServerlessFrameworkを利用してデプロイしています。

構成

f:id:yhidetoshi:20211206170730p:plain

CloudFrontについて

Basic認証とIP制限を設定するためにCloudFrontとLambda@Edge(us-east-1:バージニア北部)を連携する。 CloudFrontには以下の機能があるので利用する。

CloudFront ディストリビューションの各キャッシュ動作に、特定の CloudFront イベントの発生時に Lambda 関数を実行させるトリガー (関連付け) を 4 つまで追加できます。

ビューワーリクエス

CloudFront がビューワーからリクエストを受け取ると、リクエストされたオブジェクトが CloudFront キャッシュにあるかどうかを確認する前に関数が実行されます。

Basic認証IPアドレス制限には、CloudFrontがキャッシュを確認する前に実行する必要があるので、Viewer Request で連携します。 公式のドキュメントは下記です。 docs.aws.amazon.com

Lambda@Edgeについて

sls deployすると以下のIAMロールとポリシーが作成される。

f:id:yhidetoshi:20211207104302p:plain

  • serverless.yml
service: basicauth
frameworkVersion: ">=2.68.0"

provider:
  name: aws
  stage: dev
  runtime: python3.8
  region: us-east-1
  lambdaHashingVersion: 20201221

functions:
  function:
    handler: basic_auth.lambda_handler
    role: LambdaEdgeBasicAuth
    timeout: 5
    description: LambdaEdge Basic Auth Python3
    memorySize: 128

resources:
  Resources:
    LambdaEdgeBasicAuth:
      Type: AWS::IAM::Role
      Properties:
        RoleName: LambdaEdgeBasicAuth
        AssumeRolePolicyDocument:
          Version: '2012-10-17'
          Statement:
            - Effect: Allow
              Principal:
                Service:
                  - lambda.amazonaws.com
                  - edgelambda.amazonaws.com
              Action: sts:AssumeRole
        Policies:
           - PolicyName: LambdaEdgeBasicAuth
             PolicyDocument:
               Version: '2012-10-17'
               Statement:
                 - Effect: Allow
                   Action:
                     - "lambda:GetFunction"
                     - "lambda:EnableReplication*" 
                     - "logs:CreateLogGroup"
                     - "logs:CreateLogStream"
                     - "logs:PutLogEvents"
                   Resource: "*"

コードについて( Python3 )

コードについては以下のとおり。

  • basic_auth.py
"""
- 許可IPアドレス元の場合は認証なし
- 許可されたIPアドレス以外の場合はBasic認証を実施
"""

import base64

ALLOW_USERS = [
    {
        "user": "admin",
        "password": "pass1"
    }, {
        "user": "dev",
        "password": "pass2"
    }
]

ALLOW_IP = ['X.X.X.X', 'X.X.X.X']

ERROR_RESPONSE_AUTH = {
    'status': '401',
    'statusDescription': 'Unauthorized',
    'body': 'Authentication Failed',
    'headers': {
            'www-authenticate': [
                {
                    'key': 'WWW-Authenticate',
                    'value': 'Basic Authentication'
                }
            ]
    }
}


def lambda_handler(event, context):
    request = event['Records'][0]['cf']['request']
    headers = request['headers']
    client_ip = request['clientIp']

    if validate_client_ip(client_ip):
        return request

    # Authorizationヘッダーの有無をチェック
    if 'authorization' not in headers:
        return ERROR_RESPONSE_AUTH

    encode_auth = headers['authorization'][0]['value'].split(" ")
    decode_auth = base64.b64decode(encode_auth[1]).decode().split(":")
    (user, password) = (decode_auth[0], decode_auth[1])

    if validate_auth(user, password):
        return request
    else:
        return ERROR_RESPONSE_AUTH


def validate_auth(user, password):
    exist_flag = False
    for allow_user in ALLOW_USERS:
        if user == allow_user.get('user') and password == allow_user.get('password'):
            exist_flag = True
            return True

    if exist_flag == False:
        return False


def validate_client_ip(client_ip):
    print('CLIENT_IP=%s' % client_ip)
    if client_ip in ALLOW_IP:
        return True
    else:
        return False

  • (参考)Lambdaで受け取るCloudFrontのイベント構造について docs.aws.amazon.com

CloudFrontとS3について

詳しくは触れませんが、オリジンにS3を指定してCloudFrontからS3へのアクセス許可を設定するために OAIを作成します。 CloudFrontの設定時に "バケットポリシーを自動で更新する" にチェックをいれると、オリジンに指定したS3のバケットポリシーが以下のように自動で更新されます。

{
    "Version": "2008-10-17",
    "Id": "PolicyForCloudFrontPrivateContent",
    "Statement": [
        {
            "Sid": "1",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity {$CLOUDFRONT_DISTRIBUTION_ID}"
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::${ORIGIN_S3_BUCKET_NAME}/*"
        }
    ]
}

Behaviorの設定

  • CachePolicy
    • Basic認証とIP制限なのでキャッシュはDisabledに設定
  • LambdaEdgeの関連付け
    • Viewer-Request に設定

f:id:yhidetoshi:20211207110707p:plain

  • テストページに表示するページ
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
  </head>
  <body>
    <p>CloudFront LambdaEdge BasicAuth Test</p>
  </body>
</html>

動作確認

Lambdaの機能を使ったテストイベント

CloudFrontと連携する前にこのテストを使って動作確認をすることができます。 イベントはプルダウンで事前に用意されているので色々なパターンを利用できます。 今回はBasic認証を確認したかったので、 cloudfront-access-request-in-response を利用して authorization のブロックを追加して確認しました。

echo -n dev:pass2 | base64
ZGV2OnBhc3My
"authorization": [
     {
           "value": "Basic ZGV2OnBhc3My"
      }
],

f:id:yhidetoshi:20211207112320p:plain

{
    "Records": [
        {
            "cf": {
                "config": {
                    "distributionId": "EXAMPLE"
                },
                "request": {
                    "headers": {
                        "host": [
                            {
                                "key": "Host",
                                "value": "d123.cf.net"
                            }
                        ],
                        "authorization": [
                            {
                                "value": "Basic ZGV2OnBhc3My"
                            }
                        ],
                        "user-name": [
                            {
                                "key": "User-Name",
                                "value": "CloudFront"
                            }
                        ]
                    },
                    "clientIp": "0.0.0.0",
                    "uri": "/index.html",
                    "method": "GET"
                },
                "response": {
                    "status": "200",
                    "statusDescription": "OK",
                    "headers": {
                        "x-cache": [
                            {
                                "key": "X-Cache",
                                "value": "Hello from Cloudfront"
                            }
                        ]
                    }
                }
            }
        }
    ]
}

クライアントからの実行

■ パターン1 adminユーザの場合
❯ curl -u admin:pass1 http://{$CLOUDFRONT_DISTRIBUTION_ID}.cloudfront.net/index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
  </head>
  <body>
    <p>CloudFront S3 LambdaEdge BasicAuth Test</p>
  </body>
</html>%

■ パターン2 devユーザの場合
❯ curl -u dev:pass2 http://{$CLOUDFRONT_DISTRIBUTION_ID}.cloudfront.net/index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
  </head>
  <body>
    <p>CloudFront S3 LambdaEdge BasicAuth Test</p>
  </body>
</html>

■ パターン3 パスワードを間違えた場合
❯ curl -u dev:pass http://{$CLOUDFRONT_DISTRIBUTION_ID}.cloudfront.net/index.html
Authentication Failed

さいごに

サーバレス環境(CloudFront + Lambda@Edge)を利用してサイトのBasic認証とIP制限を検証しました。 今回は静的ページで確認しましたが,SPAの環境でも活用できる仕組みなので利用ケースは多いと思います。 Basic認証のID/PASSをハードコーディングしていますが、よりセキュアにするためにもSSMのパラメータストアから取得してもいいと思います。

ソースコード一式は以下のGitHubにあります。

github.com

OSSの go-datadog-api にPullRequestを送った

この記事がはてブロの100本目の記事になります!!ついに!という気持ちです。 今回は、Datadogに触れる機会が増えてきてDatadogのAPIレファレンスを見ていて何かAPIを実装したいと思って以下のGitHubリポジトリにPRを送りました。

github.com

このリポジトリは DatadogのGitHubがForkしていて、コントリビューション先が↑のリポジトリになっていました。

github.com

There are certainly some, but presently no known major bugs. If you do find something that doesn't work as expected, please file an issue on Github: https://github.com/zorkian/go-datadog-api/issues Thanks in advance! And, as always, patches welcome!

今回実装したのIは Organizations を GetするAPIです。

docs.datadoghq.com

OSSにPRを送るときにはリポジトリをForkしてPRを出していきますが、詳しい方法については、以前に書いた記事を参考にしています。

yhidetoshi.hatenablog.com

今回は OrganizationのGet APIを実装するために、以下のコードを作成しました。

  • datadog-accessors.go
  • organizations.go
  • organizations_test.go
  • tests/fixtures/organizations_response.json

https://github.com/zorkian/go-datadog-api/pull/327

f:id:yhidetoshi:20200815124122p:plain

Hey 👋 this client library has pretty much stopped active development. I suggest you check out the new datadog-api-client-go library [1] if you need this functionality. Despite datadog-api-client-go is still in beta, it's already fairly stable and we don't expect large changes to land at this point and it does support the functionality you're looking for - the Go code examples at the API docs page you referenced will show you the basic usage of this new library. [1] https://github.com/DataDog/datadog-api-client-go/

Google翻訳すると。

こんにちは👋このクライアントライブラリは、アクティブな開発をほぼ停止しています。この機能が必要な場合は、新しいdatadog-api-client-goライブラリ[1]をチェックすることをお勧めします。 datadog-api-client-goはまだベータ版ですが、すでにかなり安定しており、現時点で大きな変更が行われることはないと予想されており、探している機能をサポートしています-APIドキュメントのGoコード例参照したページには、この新しいライブラリの基本的な使用法が表示されます。 [1] https://github.com/DataDog/datadog-api-client-go/

もうこのリポジトリでは新規の追加実装はやっていないという事なので、これからは https://github.com/DataDog/datadog-api-client-go/ を利用してほしいと。 残念ですが・・。 とうい事で今回送ったPRは取り込まれることはありませんでした。