SAMでLambda関数にBasic認証をかける


AWS SAMで作成したLambda関数にBasic認証をかける方法についてのメモです。

マネジメントコンソールでの操作についての記事はいくつかありましたが、 SAM CLIのみで設定する方法が見つからなかったので試してみました。


環境

$ docker --version
Docker version 20.10.5, build 55c4c88
$ aws --version
aws-cli/2.1.34 Python/3.8.8 Linux/5.4.0-52-generic exe/x86_64.ubuntu.20 prompt/off
$ sam --version
SAM CLI, version 1.21.1

Lambda関数はzip形式で作成しました。


構成

SAMのHelloWorldのテンプレートで作成したディレクトリをベースに 下記の構成にしました。

$ tree .
.
├── README.md
├── basic_auth_function
│   ├── app.py
│   └── requirements.txt
├── hello_world_function
│   ├── app.py
│   └── requirements.txt
├── samconfig.toml
└── template.yaml

Basic認証処理を行うLambda関数

Basic認証を行うLambda関数です。
ユーザー名、パスワードはテキトーです。
basic_auth_function/app.py:

import json
import base64

def lambda_handler(event, context):
    user_id = 'adminxx'
    password = 'passwordxx'

    auth_str = 'Basic ' + base64.b64encode(f"{user_id}:{password}".encode("utf-8")).decode("ascii")
    
    auth_header = event['headers']['Authorization']
    
    if auth_str != auth_header:
        raise Exception('Unauthorized')
    
    return {
      'principalId': user_id,
      'policyDocument': {
        'Version': '2012-10-17',
        'Statement': [
          {
            'Action': 'execute-api:Invoke',
            'Effect': 'Allow',
            'Resource': event['methodArn']
          }
        ]
      }
    }

認証処理をかけるAPI

hello worldを返すだけです。
hello_world_function/app.py:

import json

def lambda_handler(event, context):
    return {
        "statusCode": 200,
        "body": json.dumps({
            "message": "hello world",
        }),
    }

SAMのTemplateファイル

template.yaml:

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  hello-world-auth
  Sample SAM Template for hello-world-auth

Globals:
  Function:
    Timeout: 3

Resources:
  HelloWorldApi:
    Type: AWS::Serverless::Api
    Properties:
      StageName: Prod
      GatewayResponses:
        UNAUTHORIZED:
          ResponseParameters:
            Headers:
              WWW-Authenticate: "'Basic'"
      Auth:
        AddDefaultAuthorizerToCorsPreflight: false
        DefaultAuthorizer: LambdaRequestAuthorizer
        Authorizers:
          LambdaRequestAuthorizer:
            FunctionPayloadType: REQUEST
            FunctionArn: !GetAtt BasicAuthFunction.Arn
            Identity:
              Headers:
                - Authorization
              ReauthorizeEvery: 300

  HelloWorldFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: hello_world_function/
      Handler: app.lambda_handler
      Runtime: python3.8
      Events:
        GetRoot:
          Type: Api
          Properties:
            RestApiId: !Ref HelloWorldApi
            Path: /hello
            Method: get

  BasicAuthFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: basic_auth_function/
      Handler: app.lambda_handler
      Runtime: python3.8

Outputs:
  HelloWorldApi:
    Description: "API Gateway endpoint URL for Prod stage for Hello World function"
    Value: !Sub "https://${HelloWorldApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/"
  HelloWorldFunction:
    Description: "Hello World Lambda Function ARN"
    Value: !GetAtt HelloWorldFunction.Arn
  HelloWorldFunctionIamRole:
    Description: "Implicit IAM Role created for Hello World function"
    Value: !GetAtt HelloWorldFunctionRole.Arn

  • HelloWorldApi: HelloWorldFunctionを実行するためのエンドポイント。
    API GatewayのオーソライザーとしてBasicAuthFunctionを設定し、Basic認証します。
  • HelloWorldFunction: hello worldを返すAPI、Basic認証された場合に実行されます。
  • BasicAuthFunction: Basic認証処理。

実行結果

認証なしでアクセスした場合

$ curl -i https://XXXX.execute-api.ap-northeast-1.amazonaws.com/Prod/hello
HTTP/2 401
content-type: application/json
:
www-authenticate: Basic
:

{"message":"Unauthorized"}

Basic認証のオプション(-u ユーザー名:パスワード)をつけてアクセスした場合

$ curl -i -u adminxx:passwordxx https://XXXX.execute-api.ap-northeast-1.amazonaws.com/Prod/hello
HTTP/2 200
content-type: application/json
:

{"message": "hello world"}

Basic認証情報をつけた場合のみアクセスできるようになりました。
ブラウザでアクセスした場合は、ユーザー名、パスワードの入力を求めるポップアップが表示されます。


ハマったこと

  • template.yamlを変更した後に、sam buildを実行せずに、sam deployを実行していたため、変更反映されてないことがありました。。
    sam deploysam buildで作成した.aws-samディレクトリ配下のファイルを見ているため、template.yamlを編集したあとは、sam buildが必要でした。

参考URL

AWS Serverless Application Model Developer Guide
Lambda authorizer examples
https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-controlling-access-to-apis-lambda-authorizer.html

API GatewayにLambda(Node.js)でBasic認証かける
https://dev.classmethod.jp/articles/apigateway-lambda-basicauth/#toc-6

Amazon API GatewayにBasic認証をかける方法
https://qiita.com/diaphragm/items/b87d700ef36fa62c1aa8

API Gateway + Lambda Authorizer + Lambdaプロキシ統合 + AWS SAM CLIを組み合わせたときのCORS設定
https://qiita.com/kter/items/c2732247db919a5fd51f


AWS  Lambda  SAM  Python 

See also