Simple static web hosting AWS infrastructure with protected Dev environment
Why
When working on web app development projects, it’s often useful to have a separate environment where unfinished work can be previewed without being visible to the public. This is particularly important for companies who want to keep their work confidential and secure within their network, or for developers who want to give access to specific users on the internet. In this article, we’ll explore one of the ways to create a private web app environment and how it can benefit your development process.
We will consider such a group of applications that don’t use browser routing for simplicity of hosting infrastructure. In this article a solution idea is more important than a particular website functionality.
Such applications could be:
- HTML reports
- Single page apps without browser routing (i.e. /my/favorite/page), or with routing via hash (/#my/favorite/page) and search parameters (/?nav=%2Fmy%2Ffavorite%2Fpage)
- Demo apps with UI library samples
- Web assets without the app itself
- etc.
Lets consider next URL requirements as minimal and sufficient:
- The public production environment is available at https://simple-hosting.examples.oleksiipopov.com
- The Dev environment is available privately at https://dev.simple-hosting.examples.oleksiipopov.com
- Feature branches are deployed to the destination path, corresponding to the URL-safe formatted git branch name. For instance, https://dev.simple-hosting.examples.oleksiipopov.com/[branch-name]/
- Deployment happens with help of GitHub Actions
- For simplicity, the Dev environment is protected by a firewall that has a white list of public ip ranges
How
Solution proposed here is a combination of AWS and GitHub services. AWS is used for hosting and GitHub Actions – for continuous integration and delivery.
CI/CD side
Role of GitHub Actions is to build the distribution artifact and put it to the hosting storage, which is in our case an S3 bucket.
Application artifact is a feature branch directory with files. For instance,
- /main/index.html
- /main/js/index.min.js
- /styling-issue-123/index.html
Hosting side
Production hosting should serve files from the /main directory, but the Development one – from the root "/". When serving from the root, all subdirectories become available for the Dev hosting, so it can serve from all of those at the same time.
The role of AWS is to implement 2 environments: Development and Production. External public can see only the production web app. When trying to open development URL’s, they see 403 error page from AWS CloudFront. Internal public and external maintainers and clients are supposed to be “whitelisted”. They should see all environments.
The environments can be implemented with 2 CloudFront distributions. 1 for each environment. Those will be available via human-readable domains, configured in Route53 hosted zones. S3 bucket is their common origin of files.
The difference between them is in the next things:
- URL
- Caching policy
- Price class
- Viewer restriction
URLs are obviously different by design.
Cache policy sets TTL (Time To Leave) configuration, which can be longer (1 hour – 1 day) for Production and shorter (1 second – 1 minute) for Development. The longer the TTL the better, but for the frequently updated environments that becomes a problem – we need to run tests and showcase changes as we go.
Price class of distribution depends on your latency requirements for the client locations. CloudFront has edge locations all over the world. The lower class offers fewer locations than the higher one. Production usually uses the highest class. For the development, the lowest one is enough.
Viewer restrictions can be implemented in different ways. The easiest is to attach a Web Application Firewall (WAF) to the Dev CloudFront distribution. It should have a white list of IP ranges. Usually, the company offices have internal networks with gateways to the internet that have static public IP addresses. External clients and maintainers join these networks via VPN connection. That makes them also virtually present in the internet under the company IP range. So, putting this IP range to the WAF white list “authorizes” visitors to see the private hidden environment. It makes sense to enable only IPv4 support for the Dev CloudFront distribution, because IPv6 is dynamic and cannot be used for a long time in WAF IP list.
So in the end we should get the next AWS solution
Implementation example
Check out this GitHub repository with the implementation example https://github.com/AlexeyPopovUA/simple-hosting.
It is written with aws-cdk, following the “infrastructure as a code” idea. Deployment happens automatically with GitHub actions for the “main” branch.
Next hosting environments are deployed:
- Public production: https://simple-hosting.examples.oleksiipopov.com/
- Private development:
- “main” branch: https://dev.simple-hosting.examples.oleksiipopov.com/main/
- “test” branch: https://dev.simple-hosting.examples.oleksiipopov.com/test/
The web application was kindly generated by ChatGPT. I uploaded it manually to the origin S3 bucket.
So, for the “whitelisted” visitor IP, we can see the next results:
For any random external visitor:
Hosting environments were tested by adding home router public ip to the white list of AWS WAF. The NordVPN client was used to test the external visitor case.
Conclusions
In this article, a simple hosting environment infrastructure for web applications was suggested. Private environments are very helpful when you need feature branches during the development of web projects. Such a system is easy to understand and maintain. It can be used as a first step towards complex CI/CD and hosting setup of a growing project.
Additional requirements usually come later. For instance:
- Public environment behind basic authentication
- Dynamic subdomains and proper browser routing
- Dynamic localization detection and automatic redirection to the route of given locale
- Non-cached hidden production clone environment for pre-rendering and automation testing