• Software
  • Cookbook
  • Software
  • Cookbook
  • Home
  • Rand
    • Minecraft Sucks
  • Docs
    • SPA Deployments in AWS
    • Minecraft Server Administration
    • Test Harness for Postgres Integrations
    • Enterprise REST API
  • Cookbook
    • Cookies
    • Muffins
    • Pasta

SPA Deployments in AWS

Single-Page Applications deployed to S3, with Cloudfront and Origin Access Controls

Why would you want this?

It's convenient to route the output of a React/Vue build to S3. With Cloudfront and some KMS configurations, we can securely serve these static files and cache them throughout the Cloudfront CDN getting really good performance on load times. This strategy however doesn't really account well for SSR which may be useful to your organization.

Prerequisites

  1. A valid certificate in Certificate Manager, configured for the (sub)domain used in Step 6.

Steps

1. Configure Cloudfront

Create a Cloudfront Distribution with S3 configured as its origin.

Set the Origin to a valid subdomain from the domain in certificate. Don't be too picky, this will become S3 bucket during final step.

2. Configure KMS

The key is used by S3 to encrypt the website data, and by Cloudfront to decrypt the website data. In KMS create a new Customer Managed Key. Update the KMS Key Policy to include the Cloudfront distribution in the list of Conditions as seen below:

{
    "Version": "2012-10-17",
    "Id": "key-consolepolicy-3",
    "Statement": [
        {
            "Sid": "Enable IAM User Permissions",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::${AWS_ACCOUNT_ID}:root"
            },
            "Action": "kms:*",
            "Resource": "*"
        },
        {
            "Sid": "AllowCloudFrontServicePrincipalSSE-KMS",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::${AWS_ACCOUNT_ID}:root",
                "Service": "cloudfront.amazonaws.com"
            },
            "Action": [
                "kms:Decrypt",
                "kms:Encrypt",
                "kms:GenerateDataKey*"
            ],
            "Resource": "*",
            "Condition": {
                "StringEquals": {
                    "AWS:SourceArn": [
                        "arn:aws:cloudfront::${AWS_ACCOUNT_ID}:distribution/${DISTRIBUTION_ID}"
                    ]
                }
            }
        }
    ]
}

Note: If you've already set this up before, it should be fine to just add your Cloudfront ARN to the list rather than set up another key altogether. This is no longer true if shared between, say, development and production environments.

3. Configure S3

Create an S3 Bucket with server-side encryption enabled, using the key from Step 2. Update the S3 Bucket Policy to include Cloudfront as a Principal with Read privileges.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AllowCloudFrontServicePrincipalReadOnly",
            "Effect": "Allow",
            "Principal": {
                "Service": "cloudfront.amazonaws.com"
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::${S3_BUCKET}/*",
            "Condition": {
                "StringEquals": {
                    "AWS:SourceArn": "arn:aws:cloudfront::${AWS_ACCOUNT_ID}:distribution/${DISTRIBUTION_ID}"
                }
            }
        }
    ]
}

4. Configure Cloudfront

From Security > Origin access in the sub-menut, create a new Control Setting the Origin Type to S3. Back in the Distribution settings, Edit the Origin already there to include the S3 Bucket as Origin domain, and Origin Access to point at the Control Setting we just created. Leave Origin path empty. Also in the distribution settings, set the Default Root Object to index.html, or whatever your root html file is.

5. Deploy Content

Upload your static files to the S3 bucket. It can help to start with a single index.html to test the configuration.

Note: If, for whatever reason, you already had files in the S3 bucket, it's likely you'll run into an error if the encryption settings were different during a previous deployment.

deploy.sh
#!/bin/bash
aws s3 sync $SRC_DIR s3://$S3_BUCKET && ./invalidate-cloud-front.sh
invalidate-cloud-frount.sh
#!/bin/bash
aws cloudfront create-invalidation \
    --region=$AWS_REGION \
    --distribution-id=$DISTRIBUTION_ID \
    --paths "/*"

6. Configure Route 53

In Route 53 the record you create will depend on the domain.

6a. Root domain [yourcompany].[tld] e.g example.com

  • Create an A record for your domain.
  • Toggle alias, and select target type of Cloudfront. Select the new distribution from the dropdown.

6b. Subdomain [subdomain].[yourcompany].[tld] e.g www.example.com

  • Create a CNAME record with the value of the Cloudfront distribution ID.

7. Configure Cloudfront

  • Create a Default Behaviour in the Cloudfront distribution routing HTTP to HTTPS For Single-Page Applications create a Behaviour in the Cloudfront distribution to route all unfound routes to /.

Troubleshooting

Issues adding Route53 record

  • If creating for a subdomain, add the CNAME record reference to the Cloudfront settings before adding the Route53 record. It may prevent you from linking the Cloudfront settings if the Route53 record exists.

Issue with SSL

  • Ensure Redirect to HTTPS enabled on default Behaviour.

403

  • Double check bucket policies. Empty S3 bucket and re-deploy content to verify correct encryption key (from Step 2).
  • Ensure that the Origin Access Control Setting from Step 4 is signing requests.
Next
Minecraft Server Administration