Created
June 24, 2024 21:42
-
-
Save numtel/7492ee983bcf27501411c9e4769786a5 to your computer and use it in GitHub Desktop.
S3 bucket with Cloudfront and a lambda for uploads with basic auth
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Invoke using a command like this: | |
curl -X POST "https://klho5x33jfupy46mmaeayx5lbi0ywrdm.lambda-url.us-west-2.on.aws/" \ | |
-H "Content-Type: application/json" \ | |
-d '{ | |
"file": "Rk9PQkFSMjAwMAo=", | |
"filename": "foo.txt", | |
"secret": "foobar2000" | |
}' |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
AWSTemplateFormatVersion: '2010-09-09' | |
Description: Template to create an S3 bucket, CloudFront, and a Lambda function for file uploads. | |
Parameters: | |
BucketName: | |
Type: String | |
Description: Name of the S3 bucket to be created. | |
Default: mybucket | |
CloudfrontCNAME: | |
Type: String | |
Description: Name of the S3 bucket to be created. (See todo below to enable this) | |
Default: snark-artifacts.pse.dev | |
AuthSecret: | |
Type: String | |
Description: To be passed by the client (example simple auth) | |
Default: foobar2000 | |
Resources: | |
S3Bucket: | |
Type: AWS::S3::Bucket | |
Properties: | |
PublicAccessBlockConfiguration: | |
BlockPublicAcls: false | |
BlockPublicPolicy: false | |
IgnorePublicAcls: false | |
RestrictPublicBuckets: false | |
OwnershipControls: | |
Rules: | |
- ObjectOwnership: BucketOwnerEnforced | |
BucketName: !Ref BucketName | |
S3BucketPolicy: | |
Type: AWS::S3::BucketPolicy | |
Properties: | |
Bucket: !Ref S3Bucket | |
PolicyDocument: | |
Statement: | |
- Sid: PublicGetObject | |
Effect: Allow | |
Principal: "*" | |
Action: s3:GetObject | |
Resource: !Sub 'arn:aws:s3:::${S3Bucket}/*' | |
LambdaExecutionRole: | |
Type: AWS::IAM::Role | |
Properties: | |
AssumeRolePolicyDocument: | |
Version: '2012-10-17' | |
Statement: | |
- Effect: Allow | |
Principal: | |
Service: lambda.amazonaws.com | |
Action: sts:AssumeRole | |
Policies: | |
- PolicyName: "LambdaExecutionPolicy" | |
PolicyDocument: | |
Version: "2012-10-17" | |
Statement: | |
- Effect: "Allow" | |
Action: | |
- "logs:CreateLogGroup" | |
- "logs:CreateLogStream" | |
- "logs:PutLogEvents" | |
Resource: "*" | |
- PolicyName: LambdaS3Access | |
PolicyDocument: | |
Version: '2012-10-17' | |
Statement: | |
- Effect: Allow | |
Action: | |
- s3:PutObject | |
Resource: !Sub 'arn:aws:s3:::${S3Bucket}/*' | |
LambdaFunction: | |
Type: AWS::Lambda::Function | |
Properties: | |
Code: | |
ZipFile: | | |
const { S3Client, PutObjectCommand } = require('@aws-sdk/client-s3'); | |
const s3Client = new S3Client({ region: process.env.AWS_REGION }); | |
exports.handler = async (event) => { | |
const data = JSON.parse(event.body); | |
// TODO make this auth more secure | |
if(data.secret !== process.env.AUTH_SECRET) { | |
return { | |
statusCode: 400, | |
body: JSON.stringify({ message: 'Invalid Auth Secret' }), | |
}; | |
} | |
const base64Data = Buffer.from(data.file, 'base64'); | |
const params = { | |
Bucket: process.env.BUCKET, | |
Key: data.filename, | |
Body: base64Data, | |
ContentEncoding: 'base64', | |
ContentType: 'application/octet-stream' | |
}; | |
try { | |
const response = await s3Client.send(new PutObjectCommand(params)); | |
return { | |
statusCode: 200, | |
body: JSON.stringify({ message: 'File uploaded successfully', response }), | |
}; | |
} catch (err) { | |
return { | |
statusCode: 500, | |
body: JSON.stringify({ message: 'Failed to upload file', error: err.message }), | |
}; | |
} | |
}; | |
Handler: index.handler | |
Role: !GetAtt LambdaExecutionRole.Arn | |
Runtime: nodejs20.x | |
MemorySize: 128 # Adjust as needed | |
Timeout: 60 # Adjust as needed | |
Environment: | |
Variables: | |
BUCKET: !Ref S3Bucket | |
AUTH_SECRET: !Ref AuthSecret | |
LambdaUrl: | |
Type: AWS::Lambda::Url | |
Properties: | |
AuthType: NONE | |
TargetFunctionArn: !GetAtt LambdaFunction.Arn | |
Cors: | |
AllowOrigins: | |
- '*' | |
LambdaFunctionUrlPermission: | |
Type: AWS::Lambda::Permission | |
Properties: | |
Action: "lambda:InvokeFunctionUrl" | |
FunctionName: !Ref LambdaFunction | |
Principal: "*" | |
FunctionUrlAuthType: NONE | |
CloudFrontDistribution: | |
Type: AWS::CloudFront::Distribution | |
Properties: | |
DistributionConfig: | |
Origins: | |
- Id: S3Origin | |
DomainName: !GetAtt S3Bucket.DomainName | |
S3OriginConfig: | |
OriginAccessIdentity: "" | |
DefaultCacheBehavior: | |
TargetOriginId: S3Origin | |
ViewerProtocolPolicy: redirect-to-https | |
AllowedMethods: [GET, HEAD] | |
CachedMethods: [GET, HEAD] | |
ForwardedValues: | |
QueryString: false | |
Cookies: | |
Forward: none | |
Enabled: true | |
# To Enable CNAME, see the docs | |
# https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/CNAMEs.html#alternate-domain-names-requirements | |
# Aliases: | |
# - !Ref CloudfrontCNAME | |
Outputs: | |
LambdaFunctionUrl: | |
Description: URL for the Lambda function | |
Value: !GetAtt LambdaUrl.FunctionUrl | |
CloudFrontURL: | |
Description: HTTPS URL to the CloudFront distribution | |
Value: !Sub 'https://${CloudFrontDistribution.DomainName}' |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment