Deploying the Network Stack

Deploying the Networking and Front End Stack on LocalStack

Start a fresh LocalStack 63,725 with one addition: its gateway answers CORS checks itself and rejects unknown origins, so the website's origin goes into EXTRA_CORS_ALLOWED_ORIGINS for Full Stack Deploy's API calls:

Restarting LocalStack and deploying booknest-webYAML
SITE=http://booknest-web-000000000000.s3-website.localhost.localstack.cloud:31566
docker rm -f -v l1-localstack >/dev/null
docker run -d --name l1-localstack -p 127.0.0.1:31566:4566 \
  -e MAIN_CONTAINER_NAME=l1-localstack -e EXTRA_CORS_ALLOWED_ORIGINS=$SITE \
  -v /var/run/docker.sock:/var/run/docker.sock localstack/localstack:4.13.1 >/dev/null
until curl -fs localhost:31566/_localstack/health >/dev/null; do sleep 2; done
cd ~/v5-ch7/booknest && cfn-lint infra/booknest-web.yaml
aws cloudformation deploy --stack-name booknest-web --template-file infra/booknest-web.yaml \
  --tags app=booknest | tail -1
Output
Successfully created/updated stack - booknest-web

Then ask EC2 24 and S3 24 themselves, not CloudFormation 24 :

Checking routes, security groups and public access with direct callsYAML
VPC=$(aws ec2 describe-vpcs --filters Name=tag:Name,Values=booknest-vpc \
  --query 'Vpcs[0].VpcId' --output text)
aws ec2 describe-route-tables --filters Name=vpc-id,Values=$VPC --output text \
  --query 'RouteTables[].Routes[].[DestinationCidrBlock || DestinationPrefixListId,GatewayId]'
aws ec2 describe-security-groups --filters Name=vpc-id,Values=$VPC --output text --query \
  'SecurityGroups[].[Description,length(IpPermissions),IpPermissionsEgress[0].IpProtocol]'
aws s3api get-public-access-block --bucket booknest-web-000000000000 --output text
Output
10.20.0.0/16   local
10.20.0.0/16   local
0.0.0.0/0   None
10.20.0.0/16   local
pl-22fbdba0f346abeb7   vpce-052978e89a1b23b34
default VPC security group   0   -1
API functions, nothing in, HTTPS out only   0   -1
Load balancer tier, HTTPS in from anywhere   0   -1
PUBLICACCESSBLOCKCONFIGURATION   True   True   True   True

The route tables and the endpoint route are real (the first local route is the main table's). Three things differ from the template: the default route has no target (4.13.1's route provider drops GatewayId), the web group has no ingress rule, and every group keeps allow-all egress (-1). The public access switches are all on, yet anonymous reads work below. On AWS 24 all three match the template. Upload a static snapshot and fetch it:

Uploading the front end and fetching it through the website endpointYAML
jq .books db/seed.json > web/books.json
echo '{"booksUrl": "books.json"}' > web/config.json
aws s3 sync web/ s3://booknest-web-000000000000/ --only-show-errors
curl -s -o /dev/null -w '%{http_code} %{content_type}\n' $SITE/
curl -s -w ' %{http_code}\n' $SITE/no-such-page
Output
200 text/html
<!doctype html><title>Not found</title><h1>BookNest: page not found</h1>
 404

A missing key returns the error document with status 404. Headless Chrome 1 shows the page:

BookNest's front end on LocalStack's S3 website endpoint, reading books.json
BookNest's front end on LocalStack's S3 website endpoint, reading books.json

The generated web/books.json and web/config.json are git-ignored; the commit adds the template and the two HTML files.