--- AWSTemplateFormatVersion: "2010-09-09" Description: "Serve static web content from private S3 bucket through HTTPS with HTTP Basic Auth. CloudFront distribution used to proxy private S3 content and mandate HTTPS-only traffic. CloudFront Functions used to implement HTTP Basic Auth with static username and password" Parameters: Base64UserPass: Type: String NoEcho: true Resources: SiteBucket: Type: "AWS::S3::Bucket" Properties: AccessControl: Private SiteBucketPolicy: Type: "AWS::S3::BucketPolicy" Properties: Bucket: !Ref SiteBucket PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: "cloudfront.amazonaws.com" Action: 's3:GetObject' Resource: !Sub "${SiteBucket.Arn}/*" # CF Distribution ARNs have to be manually constructed Condition: StringEquals: AWS:SourceArn: !Sub "arn:aws:cloudfront::${AWS::AccountId}:distribution/${SiteDistribution}" SiteDistribution: Type: "AWS::CloudFront::Distribution" Properties: DistributionConfig: Origins: - DomainName: !GetAtt SiteBucket.RegionalDomainName Id: SiteBucketWebsite # This is necessary even thou we don't use OAI for access control. S3OriginConfig: OriginAccessIdentity: "" OriginAccessControlId: !GetAtt SiteOriginAccessControl.Id Enabled: true DefaultRootObject: index.html DefaultCacheBehavior: CachePolicyId: 4135ea2d-6df8-44a3-9df3-4b5a84be39ad TargetOriginId: SiteBucketWebsite AllowedMethods: - HEAD - GET - OPTIONS Compress: true ViewerProtocolPolicy: redirect-to-https FunctionAssociations: - EventType: viewer-request FunctionARN: !GetAtt SiteAuthFunction.FunctionMetadata.FunctionARN PriceClass: PriceClass_100 SiteOriginAccessControl: Type: "AWS::CloudFront::OriginAccessControl" Properties: OriginAccessControlConfig: Name: "Static Site Origin Access Control" OriginAccessControlOriginType: s3 SigningBehavior: always SigningProtocol: sigv4 SiteAuthFunction: Type: AWS::CloudFront::Function Properties: AutoPublish: true FunctionCode: !Sub " function handler(event) { var authHeaders = event.request.headers.authorization; var expected = 'Basic ${Base64UserPass}'; if (authHeaders && authHeaders.value === expected) { return event.request; } var response = { statusCode: 401, statusDescription: 'Unauthorized', headers: { 'www-authenticate': { value: 'Basic realm=\"Enter credentials for this super secure site\"', }, }, }; return response; }" FunctionConfig: Comment: Add HTTP Basic authentication to CloudFront Runtime: cloudfront-js-1.0 Name: BasicAuthFn Outputs: StaticWebsiteHostname: Value: !GetAtt SiteDistribution.DomainName