LoginSignup
10
11

More than 3 years have passed since last update.

CloudFront + Lambda@EdgeでBasic認証するためのCloudFormationテンプレート

Posted at

Lambda@EdgeでBasic認証したいと思ったが手でポチポチ作るのも面倒なのでCloudFormationのテンプレート化した。
Lambda@Edge用のLambda関数はus-east-1で作る必要があるためテンプレートは2つに別れてます

Lambda関数を追加するテンプレート

  • このテンプレートはus-east-1で実行する必要があります
  • Lambda@edgeはエイリアス使えないが、SAMでLambdaのバージョン発行したかったのでAutoPublishAliasを指定してバージョン作ってます
  • コード内のauthUserauthPassは任意の値を入れて実行する
AWSTemplateFormatVersion: 2010-09-09
Transform: "AWS::Serverless-2016-10-31"

Resources:
  LambdaRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: basic-auth-lambda-role
      Path: /
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - edgelambda.amazonaws.com
                - lambda.amazonaws.com
            Action:
              - sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole

  LambdaFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: basic-auth
      Handler: index.handler
      Runtime: nodejs8.10
      MemorySize: 128
      Timeout: 5
      Role: !GetAtt LambdaRole.Arn
      AutoPublishAlias: prod
      InlineCode: |
        exports.handler = (event, context, callback) => {
            const request = event.Records[0].cf.request;
            const headers = request.headers;
            const authUser = 'user';
            const authPass = 'pass';
            const authString = 'Basic ' + new Buffer(`${authUser}:${authPass}`).toString('base64');
            if (typeof headers.authorization == 'undefined' || headers.authorization[0].value != authString) {
                const response = {
                    status: '401',
                    statusDescription: 'Unauthorized',
                    body: 'Unauthorized',
                    headers: {
                        'www-authenticate': [{ key: 'WWW-Authenticate', value:'Basic' }],
                    },
                };
                callback(null, response);
            } else {
                callback(null, request);
            }
        };

  LogGroup:
    Type: AWS::Logs::LogGroup
    DependsOn:
      - LambdaFunction
    Properties:
      RetentionInDays: 1
      LogGroupName: !Sub "/aws/lambda/${LambdaFunction}"

CloudFrontの設定をするテンプレート

  • S3 + CloudFrontの構成になります
  • このテンプレートはどのリージョンで実行してもいいですが今回のは別リージョンで実行する想定で作ってます
  • LambdaFunctionAssociationsがLambda@Edge使うための設定です
    • これを消せばBasic認証が外れます
    • ここでLambda関数名とバージョンが必要になるのでParameterから受け取るようにしました
  • アクセス時にLambdaのログが/aws/lambda/us-east-1.関数名に出力されます
    • あとで削除したときにゴミ残るのでココでロググループを作ってしまいます
AWSTemplateFormatVersion: 2010-09-09

Parameters:
  BucketName:
    Type: String
  LambdaEdgeFunctionName:
    Type: String
  LambdaEdgeFunctionVersion:
    Type: String

Resources:
  S3Bucket:
    Type: AWS::S3::Bucket
    Properties:
      AccessControl: PublicRead
      BucketName: !Ref BucketName
      WebsiteConfiguration:
        IndexDocument: index.html
        ErrorDocument: error.html

  S3BucketPolicy:
    Type: AWS::S3::BucketPolicy
    DependsOn:
      - S3Bucket
      - CloudFrontOriginAccessIdentity
    Properties:
      Bucket: !Ref S3Bucket
      PolicyDocument:
        Version: 2008-10-17
        Statement:
          - Action:
              - s3:GetObject
            Effect: Allow
            Resource: !Sub "${S3Bucket.Arn}/*"
            Principal:
              AWS: !Sub "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity ${CloudFrontOriginAccessIdentity}"

  CloudFrontOriginAccessIdentity:
    Type: AWS::CloudFront::CloudFrontOriginAccessIdentity
    Properties:
      CloudFrontOriginAccessIdentityConfig:
        Comment: "access identity"

  CloudFrontDistribution:
    Type: AWS::CloudFront::Distribution
    DependsOn:
      - S3Bucket
      - CloudFrontOriginAccessIdentity
    Properties:
      DistributionConfig:
        Enabled: true
        DefaultCacheBehavior:
          AllowedMethods:
            - HEAD
            - GET
          CachedMethods:
            - HEAD
            - GET
          DefaultTTL: 0
          MaxTTL: 0
          MinTTL: 0
          TargetOriginId: !Sub "${BucketName}-Origin"
          ViewerProtocolPolicy: redirect-to-https
          ForwardedValues:
            QueryString: false
          LambdaFunctionAssociations:
            - EventType: viewer-request
              LambdaFunctionARN: !Sub "arn:aws:lambda:us-east-1:${AWS::AccountId}:function:${LambdaEdgeFunctionName}:${LambdaEdgeFunctionVersion}"
        IPV6Enabled: true
        HttpVersion: http2
        DefaultRootObject: index.html
        ViewerCertificate:
          CloudFrontDefaultCertificate: true
        Origins:
          - Id: !Sub "${BucketName}-Origin"
            DomainName: !Sub "${BucketName}.s3.${AWS::Region}.amazonaws.com"
            S3OriginConfig:
              OriginAccessIdentity: !Sub "origin-access-identity/cloudfront/${CloudFrontOriginAccessIdentity}"
        CustomErrorResponses:
          - ErrorCachingMinTTL: 0
            ErrorCode: 403

  LogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      RetentionInDays: 1
      LogGroupName: !Sub "/aws/lambda/us-east-1.${LambdaEdgeFunctionName}"

確認

  • 作成されたCloudFront DistributionのDomain Nameを叩いて以下のようになればOK
  • あとはLambdaに設定したUser/Passを入れてログインできればOK

スクリーンショット 2019-08-12 18.02.29.png

10
11
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
11