Lambda@EdgeでBasic認証したいと思ったが手でポチポチ作るのも面倒なのでCloudFormationのテンプレート化した。
Lambda@Edge用のLambda関数はus-east-1
で作る必要があるためテンプレートは2つに別れてます
Lambda関数を追加するテンプレート
- このテンプレートは
us-east-1
で実行する必要があります - Lambda@edgeはエイリアス使えないが、SAMでLambdaのバージョン発行したかったので
AutoPublishAlias
を指定してバージョン作ってます - コード内の
authUser
とauthPass
は任意の値を入れて実行する
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