CloudFormationでALBのBasic認証を実装してみました


この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので十分ご注意ください。

前提

・Cfnを使って東京リージョンでテスト用のVPCを作成します(CloudFormationテンプレート1)
・VPC作成時に下記の情報をエクスポートします。(CloudFormationテンプレート2)
①VPC
②PublicSubnet1
③PublicSubnet2
・Cfnを使って東京リージョンでALBを作成します。(上記のエクスポートした情報をインポートします)
手動でALBのBasic認証を実装したい場合、下記の記事を参照

Lambda(Node.js)でALBのBasic認証を実装してみました

CloudFormationテンプレート

CloudFormationテンプレート1:VPC関連

VPC.yaml

AWSTemplateFormatVersion: '2010-09-09'
Description: 'VPC For BasicAuth Test'
Parameters:
  VpcName:
    Type: String
    Description: "name of the Vpc"
  PublicSubnet1Name:
    Type: String
    Description: "PublicSubnet1Name"
  PublicSubnet2Name:
    Type: String
    Description: "PublicSubnet2Name"

Mappings:
  SubnetConfig:
    VPC:
      CidrBlock: 10.0.0.0/16
    Public1:
      CidrBlock: 10.0.0.0/24
      AvailabilityZone: ap-northeast-1a
    Public2:
      CidrBlock: 10.0.1.0/24
      AvailabilityZone:  ap-northeast-1c

Resources:
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      EnableDnsSupport: true
      EnableDnsHostnames: true
      CidrBlock: !FindInMap [SubnetConfig, VPC, CidrBlock]
      Tags:
      - Key: Name
        Value: !Ref VpcName
  PublicSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref "VPC"
      CidrBlock: !FindInMap ["SubnetConfig", "Public1", "CidrBlock"]
      AvailabilityZone: !FindInMap ["SubnetConfig", "Public1", "AvailabilityZone"]
      Tags:
      - Key: Name
        Value: !Ref PublicSubnet1Name
  PublicSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref "VPC"
      CidrBlock: !FindInMap ["SubnetConfig", "Public2", "CidrBlock"]
      AvailabilityZone: !FindInMap ["SubnetConfig", "Public2", "AvailabilityZone"]
      Tags:
      - Key: Name
        Value: !Ref PublicSubnet2Name
  InternetGateway:
    Type: AWS::EC2::InternetGateway
  GatewayAttachement:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref VPC
      InternetGatewayId: !Ref InternetGateway
  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref "VPC"
  PublicRoute:
    Type: AWS::EC2::Route
    DependsOn: GatewayAttachement
    Properties:
      RouteTableId: !Ref "PublicRouteTable"
      DestinationCidrBlock: "0.0.0.0/0"
      GatewayId: !Ref "InternetGateway"
  PublicSubnetRouteTableAssociation1:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnet1
      RouteTableId: !Ref PublicRouteTable
  PublicSubnetRouteTableAssociation2:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnet2
      RouteTableId: !Ref PublicRouteTable

Outputs:
# ----------------------------------------------------------------------------#
# VPC Outputs
# ----------------------------------------------------------------------------#
  VPC:
    Value: !Ref VPC
    Export:
      Name: !Ref VpcName

# ----------------------------------------------------------------------------#
# Subnet Outputs
# ----------------------------------------------------------------------------#
#PublicSubnet01
  PublicSubnet1:
    Value: !Ref PublicSubnet1
    Export:
      Name: !Ref PublicSubnet1Name
#PublicSubnet02
  PublicSubnet2:
    Value: !Ref PublicSubnet2
    Export:
      Name: !Ref PublicSubnet2Name

CloudFormationテンプレート2:ALBのBasic認証関連

ALB-BasicAuth.yaml

AWSTemplateFormatVersion: '2010-09-09'
Description: 'ALB BasicAuth'
Parameters:
  ALBName:
    Type: String
    Description: "ALBName"
  Username:
    Default: ""
    Type: String
  Password:
    Default: ""
    Type: String

Resources:
  LoadBalancerSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Access to the LoadBalancer from Internet
      VpcId: !ImportValue kobayashi-Vpc01
  LoadBalancerSecurityGroupIngressHTTP:
    Type: "AWS::EC2::SecurityGroupIngress"
    Properties:
      Description: HTTP
      GroupId: !Ref LoadBalancerSecurityGroup
      CidrIp: 0.0.0.0/0
      FromPort: 80
      ToPort: 80
      IpProtocol: tcp

  LoadBalancer:
    Type: 'AWS::ElasticLoadBalancingV2::LoadBalancer'
    Properties:
      Subnets:
        - !ImportValue kobayashi-PublicSubnet01
        - !ImportValue kobayashi-PublicSubnet02
      SecurityGroups:
        - !Ref LoadBalancerSecurityGroup
      Tags:
        - Key: Name
          Value: !Ref ALBName

  LoadBalancerListener:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      DefaultActions:
        - Type: forward
          TargetGroupArn:
            !Ref LoadBalancerTargetGroupUnauthorized
      LoadBalancerArn: !Ref LoadBalancer
      Port: 80
      Protocol: HTTP
  LoadBalancerListenerAuthorizedRule:
    Type: AWS::ElasticLoadBalancingV2::ListenerRule
    Properties:
      Actions:
      - Type: fixed-response
        FixedResponseConfig:
          StatusCode: 200
          MessageBody: Authorized
          ContentType: text/plain
      Conditions:
      - Field: http-header
        HttpHeaderConfig:
          HttpHeaderName: Authorization
          Values:
            - Fn::Join:
                - " "
                - - "Basic"
                  - Fn::Base64: !Sub ${Username}:${Password}
      ListenerArn: !Ref LoadBalancerListener
      Priority: 10

  LoadBalancerTargetGroupUnauthorized:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      TargetType: lambda
      Targets:
        - Id: !GetAtt LambdaSendUnauthrized.Arn
    DependsOn:
      - LambdaPermissionSendUnauthrized
  LambdaSendUnauthrized:
    Type: AWS::Lambda::Function
    Properties:
      Code:
        ZipFile: |+
          const debug = (key, object) => { console.log(`DEBUG: ${key}\n`, JSON.stringify(object)); }

          exports.handler = async (event, context) => {
            console.log("INFO: request Recieved.\nEvent:\n", JSON.stringify(event));

            return {
                statusCode: 401,
                statusDescription: '401 Unauthorized',
                body: 'Unauthorized',
                isBase64Encoded: false,
                headers: {
                    'WWW-Authenticate': 'Basic',
                    'Content-Type': 'text/html'
                }
            };
          };
      Handler: index.handler
      Role: !GetAtt LambdaSendUnauthrizedExecutionRole.Arn
      Runtime: "nodejs14.x"
      MemorySize: 128
      Timeout: 15
  LambdaSendUnauthrizedExecutionRole:
    Type: "AWS::IAM::Role"
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
            Action:
              - "sts:AssumeRole"
      Path: /
      ManagedPolicyArns:
        - "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
  LambdaPermissionSendUnauthrized:
    Type: AWS::Lambda::Permission
    Properties:
      Action: lambda:InvokeFunction
      FunctionName: !GetAtt LambdaSendUnauthrized.Arn
      Principal: elasticloadbalancing.amazonaws.com

Outputs:
  LoadBalancerDNSName:
    Value: !GetAtt LoadBalancer.DNSName

CloudFromation スタック作成例

下記の画面を参照

ALBののBasic認証を使ってみます

下記の画面を参照

まとめ

AWSコンソール上でもALBのBasic認証を実装できますが、今回、CloudFormationを使って実装してみました。

開発中などで検証期間が終わったら、すぐ削除する場合、対象Stackを選択して「削除」ボタンを押すと関連するリソースがすべて削除されます。

Last modified: 2024-02-07

Author