Builds a serverless infrastructure in AWS for hosting a static website protected with Basic Authentication and published on a subdomain registered via Route 53
MIT License
This repository contains a collection of Bash scripts and a choice of either a Terraform module or a set of CloudFormation templates that build a serverless infrastructure in AWS to host a static website protected with Basic Authentication. The static website is published on a subdomain registered in Route 53.
A live example can be found at https://serverless-static-website-with-basic-auth.dumrauf.uk/ using the demo username
guest
and passwordletmein
. Note that access to the underlying S3 bucket hosting the static website is denied.
The master branch in this repository is compliant with Terraform v0.12; a legacy version that is compatible with Terraform v0.11 is available on branch [email protected].
Before you can use the tools in this repository out of the box, you need
If Terraform is the tool of choice then you also need
After creating the serverless infrastructure in AWS you get
Using the tools in this repository helps you avoid having
All entry points are Bash scrips located in the scripts
folder.
Unless you are happy with the demo username guest
and password
letmein
,
swap out the username-credentials dictionary const credentials
in file lambda-at-edge-code/index.js
with your own.
See the FAQs section about updating passwords at a later time in case changes are not reflected.
The entire serverless infrastructure can be created via
scripts/create_static_serverless_website.sh <parameter_1> ... <parameter_n>
where the parameters differ between CloudFormation and Terraform and additional setup may be required.
As for CloudFormation, the entire serverless infrastructure can be created via
scripts/create_static_serverless_website.sh <website-directory> <subdomain> <domain> <hosted-zone-id> <acm-certificate-arn> <profile>
An example invocation may look like
scripts/create_static_serverless_website.sh static-website-content/ static-website mydomain.uk Z23ABC4XYZL05B "arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012" default
Note that you need to replace the example values with yours in order for the script to work.
Under the bonnet, the script calls
bootstrap_serverless_repo.sh
create_serverless_infrastructure.sh.sh
upload_website_to_s3_bucket.sh
creating and uploaded the resources as indicated by the corresponding name.
As for Terraform, the input variables for the example website static-website.example.com
are definied in Terraform/settings/static-website.example.com.tfvars
as
region = "us-east-1"
shared_credentials_file = "/path/to/.aws/credentials"
profile = "default"
hosted_zone_id = "Z23ABC4XYZL05B"
subdomain_name = "static"
domain_name = "example.com"
acm_certificate_arn = "arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012"
log_bucket_domain_name = "<your-log-bucket-domain>"
Note that you need to replace the example values with yours in order for Terraform to work.
With the Terraform configuration done, the entire serverless infrastructure can be created via
scripts/create_static_serverless_website.sh <website-directory> <profile> <workspace-name>
Here, the <workspace-name>
has to match the name of the input variables file in settings/
when neglecting the .tfvars
extension (in this case static-website.example.com
)
An example invocation may look like
scripts/create_static_serverless_website.sh static-website-content/ default static-website.example.com
Note that you need to replace the example values with yours in order for the script to work.
Under the bonnet, the script calls
create_serverless_infrastructure.sh.sh
upload_website_to_s3_bucket.sh
The local static website contents can be synced with the corresponding S3 bucket serving as the CloudFront origin via
scripts/upload_website_to_s3_bucket.sh <website-directory> <profile>
If your static website is located at ../static-website-content/
, sync it with the corresponding S3 bucket using profile default
via
scripts/upload_website_to_s3_bucket.sh "../static-website-content/" default
creating and uploaded the resources as indicated by the corresponding name.
By default, an IAM user is also created who is only allowed to
Using this least-privileged user's access keys minimises your potential attack surface and is highly recommended. Note that API access keys are not generated by default but can easily be obtained from the AWS console.
After syncing the static website with the S3 bucket, the CloudFront distribution will most likely keep a cached copy of the old static website until it expires.
This process can be expedited by invalidating the cache via
scripts/invalidate_cloudfront_chache.sh <profile> <paths>
The entire CloudFront distribution can be invalidated using profile default
via
scripts/invalidate_cloudfront_chache.sh default '/*'
Here, note the single quotes around '/*'
in order to avoid parameter expansion in Bash.
Note that invalidations can incur costs.
Again, the details differ when it comes to CloudFormation versus Terraform. Here, Terraform seems to simplify things a little.
In the case of CloudFormation, the Bash scripts essentially kick off two CloudFormation templates, namely
bootstrap_serverless_code_repository.yaml
andserverless_static_website_with_basic_auth.yaml
In the case of Terraform, the Bash scripts first switches to the workspace provided in the input or creates it if it doesn't exist. Afterwards, the Bash scripts essentially kick off a simple Terraform configuration in main.tf
which utilises the serverless-static-website-with-basic-auth
module.
The Serverless Code Repository template is a CloudFormation specific implementation. Here, the bootstrap_serverless_code_repository.yaml
creates a private S3 bucket which enforces encryption and acts as a serverless code repository. Another option would be to provide the code inline in the CloudFormation template but no matter how the code editor is set up, a good chunk of the template is always being marked as either plain text or plain wrong.
The serverless_static_website_with_basic_auth.yaml
template as well as the serverless-static-website-with-basic-auth
module creates
When using Route 53 as the domain registrar, a default hosted zone is usually created. This hosted zone contains four dedicated name servers. As of December 2017, creating a new hosted zone which uses specific name servers (namely the ones from the default hosted zone) is currently not possible via CloudFormation.
As of December 2017, CloudFormation only allows email validation for ACM certificates it issues; DNS validation is not an option even if the domain is registered via Route 53.
Moreover, the entire stack remains in the CREATE_IN_PROGRESS
state until the certificate has been validated which can introduce long delays.
However, the AWS console allows to create an ACM certificate and add a record set to the corresponding hosted zone in Route 53 with one click.
Here, the problem is that new versions are not automagically published even if the underlying code has changed. For this, the name of the version has to changed in the corresponding CloudFormation template.
Manually change the name of the BasicAuthAtEdgeLambdaVersion
and all its uses. Another redeploy should fix the problem.
As of December 2017, CloudFront can only reference a version in Lambda@Edge. Yup, it seems like yearly days here. Oh, and good luck deleting that Lambda@Edge via CloudFormation...
The default root document is index.html
.
This value can be changed by updating the DefaultRootObject: index.html
in the serverless_static_website_with_basic_auth.yaml
template.
cloudfront:CreateInvalidation
Permission?As of January 2018, CloudFront does not seem to provide fine grained access control for distributions on the cloudfront:CreateInvalidation
permission. So much for true least privileged then...
Splendid! Open a pull request and let's make things better for everyone!
The code in this repository builds upon a great article by Leonid Makarov describing the underlying idea as well as providing a Node.js implementation of Basic Authentication.