LoginSignup
11
8

More than 5 years have passed since last update.

Serverless framework + API Gateway + Lambda で `--stage` の値を判別してステージング環境に基本認証

Last updated at Posted at 2019-02-24

Serverless framework を使うと sls deploy --stage production とか sls deploy --stage staging みたいな感じで、本番とステージングを切り替えてデプロイするが、この --stage の値を判別してステージングへのデプロイだけ基本認証をかけるやり方。

serverless.yml

serverless.yml
functions:
  app:
    handler: handler.app
    events: ${self:custom.events.${opt:stage,'staging'}}
  authorizer:
    handler: lib/authorizer.handler


resources:
  Resources:
    GatewayResponse:
      Type: 'AWS::ApiGateway::GatewayResponse'
      Properties:
        ResponseParameters:
          gatewayresponse.header.WWW-Authenticate: "'Basic'"
        ResponseType: UNAUTHORIZED
        RestApiId:
          Ref: 'ApiGatewayRestApi'
        StatusCode: '401'

custom:
  events:
    production:
      - http:
          path: /
          method: get
      - http:
          path: /{any+}
          method: get
    staging:
      - http:
          path: /
          method: get
          authorizer:
            name: authorizer
            resultTtlInSeconds: 0
            identitySource: method.request.header.Authorization
            type: request
      - http:
          path: /{any+}
          method: get
          authorizer:
            name: authorizer
            resultTtlInSeconds: 0
            identitySource: method.request.header.Authorization
            type: request

  • functions.app.events の値 ${self:custom.events.${opt:stage,'staging'}} は、下にある custom.events.* の値を読み込むための記述。
  • functions.authorizer には認証用のハンドラを指定する。ハンドラの処理は後述。
  • resources.Resources.GatewayResponse 以下の記述は基本認証を行うためのレスポンスヘッダーを返すための設定。
  • custom 以下の記述は URL の設定及び基本認証を行うためのカスタムオーソライザーの設定。

認証用ハンドラ

'use strict';

exports.handler = (event, context, callback) => {
  const authorizationHeader = event.headers.Authorization

  if (!authorizationHeader) return callback('Unauthorized')

  const encodedCreds = authorizationHeader.split(' ')[1]
  const plainCreds = (new Buffer(encodedCreds, 'base64')).toString().split(':')
  const username = plainCreds[0]
  const password = plainCreds[1]

  if (!(username === 'admin' && password === 'secret')) return callback('Unauthorized')

  const authResponse = buildAllowAllPolicy(event, username);
  callback(null, authResponse)
}

const buildAllowAllPolicy = (event, principalId) => {
  const tmp = event.methodArn.split(':')
  const apiGatewayArnTmp = tmp[5].split('/')
  const awsAccountId = tmp[4]
  const awsRegion = tmp[3]
  const restApiId = apiGatewayArnTmp[0]
  const stage = apiGatewayArnTmp[1]
  const apiArn = `arn:aws:execute-api:${awsRegion}:${awsAccountId}:${restApiId}/${stage}/*/*`
  const policy = {
    principalId: principalId,
    policyDocument: {
      Version: '2012-10-17',
      Statement: [
        {
          Action: 'execute-api:Invoke',
          Effect: 'Allow',
          Resource: [apiArn]
        }
      ]
    }
  }
  return policy
}

ソースコードは以下の記事を参考にしました。

ユーザー名は admin、パスワードは secret で、ソースコード内に決め打ちで入ってますので注意。

11
8
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
11
8