Networking & Content Delivery

Hosting Internal HTTPS Static Websites with ALB, S3, and PrivateLink

Amazon Simple Storage Service (Amazon S3) is a powerful platform that enables you to do various tasks. One notable feature is the ability to create a bucket with an FQDN, point an alias record to the bucket website endpoint, and immediately get up-and-running with an HTTP static website. If you want to serve HTTPS traffic for your static website, then you can use Amazon CloudFront to provide both caching and HTTPS certificates to your public-facing users.

If your users are located inside of an Intranet or private network, then you will want to provide access to your S3 buckets using interface endpoints on AWS PrivateLink for S3. Your users will also want to access your static website using a friendly domain name like portal.example.com. HTTPS is available through the common s3-website.<region>.amazonaws.com domain name. However, custom domains will require an additional internal proxy to serve your TLS certificate. This can be achieved with an internal Application Load Balancer (ALB).

Solution overview

This solution leverages your existing private connection to the VPC and an Internal ALB to present the TLS certificate of the custom S3 bucket domain to the end-user. The ALB leverages AWS Certificate Manager (ACM) to present a valid certificate for the end-user, while maintaining a secure TLS connection to the trusted Amazon S3 VPC Endpoint. This enables the use of custom domain names for your static website.

AWS architectural diagram showing a group called Private VPC. Direct Connect, Site-to-Site VPN, and Client VPN provide a connection into the Private VPC which then routes to the Internal ALB. The Internal ALB uses TLS Certificate and AWS Certificate Manager to present a TLS certificate, then routes to the S3 VPC Endpoint IPs. Finally, the VPC Endpoint IPs route to the S3 Static Website on portal.example.com

Walkthrough

In this walkthrough, you’ll be creating an Amazon S3 VPC Endpoint, and an Internal ALB which can be leveraged with your existing AWS connection.

Prerequisites

For this walkthrough, you should have the following prerequisites:

  • An AWS account
  • A private or imported certificate for the domain imported in ACM in the Region of your choice
  • An Amazon S3 bucket named with the domain that matches your ACM certificate (such as “portal.example.com”). You can read here to get started with Amazon S3, and read about working with Amazon S3 buckets here.
    • You do not need to enable static website hosting on the bucket, as this is only for public website endpoints. Requests to the bucket will be going through a private REST API instead.
  • A VPC with at least two private subnets
  • An existing Direct Connect, Site-to-Site VPN, or Client VPN connection with correct routing into your VPC. This will be referred to as your private inbound connection.
  • An private hosted zone for your custom domain name
  • A resource that can access your VPC network, such as an Amazon Elastic Compute Cloud (Amazon EC2) instance running inside of the VPC
  • A static website containing an index.html entry page populated into your S3 bucket

Step 1: Create your Amazon S3 VPC Endpoint

To securely and privately connect the ALB to your S3 bucket, you must start by creating an Amazon S3 VPC Endpoint.

  1. Log in to your VPC Dashboard.
  2. On the left-hand menu, navigate to the “Endpoints” page.
  3. Select “Create Endpoint”.
  4. Search for “s3” in the Service List, and select the Amazon S3 Interface Endpoint service.Screenshot of the VPC Create Endpoints page, with S3 Interface endpoint selected
  5. Select the VPC which contains your private inbound connection, and at least two subnets to which the endpoint will belong. It’s recommended that you select subnets belonging to two or more different Availability Zones (AZs) to leverage fault isolation on your interface endpoint.
  6. Select the security group that you would like to protect the VPC Endpoint. This security group must allow access on ports 80 and 443 from the security group for your ALB at minimum. You can read more about working with security groups here.
  7. Select “Full Access” for the VPC Endpoint policy. This policy makes sure that all AWS principals working in the VPC can access the VPC Endpoint for any S3 bucket. This doesn’t bypass the security policy we’ll define on the S3 bucket. However, you can choose to limit this policy to only allow access to the ALB later after you’ve created it.
  8. Select “Create endpoint”.
  9. Select your new VPC Endpoint ID to navigate to the new VPC Endpoint.
  10. On the bottom tabs, go to “Subnets”.
  11. Note the IPv4 Addresses of your VPC Endpoint, as you’ll need them later!Screenshot of the IPv4 Addresses of the VPC Endpoint

Step 2: Allow the VPC Endpoint to your S3 bucket

You can read more about S3 bucket policies for VPC Endpoints here.

  1. Navigate to your S3 bucket, and go to the “Permissions” tab.
  2. Scroll down to “Bucket policy”, and Select “Edit”.
  3. Add your policy based on the provided documentation. For convenience, you can also use this provided policy to make sure that only your VPC Endpoint is explicitly allowed:
{
   "Version": "2012-10-17",
   "Id": "Policy1415115909152",
   "Statement": [
     {
       "Sid": "Access-to-specific-VPCE-only",
       "Principal": "*",
       "Action": "s3:GetObject",
       "Effect": "Allow",
       "Resource": ["arn:aws:s3:::yourbucketname",
                    "arn:aws:s3:::yourbucketname/*"],
       "Condition": {
         "StringEquals": {
           "aws:SourceVpce": "vpce-1a2b3c4d"
         }
       }
     }
   ]
}

Step 3: Set up your Internal ALB

The Internal ALB will terminate your client-facing TLS connection

  1. Navigate to your AWS Console EC2 Dashboard.
  2. On the left-hand menu, select “Load Balancers”.
  3. Select “Create load balancer”.
  4. Select “Create” under the “Application Load Balancer” box.
  5. Name your ALB and pick the “internal” scheme.
  6. Switch the listener protocol to “HTTPS”.
  7. Select your private subnets that the ALB will serve. Select “Next”.
  8. Select the ACM certificate that will be served to your clients. Note that it must match your static S3 bucket domain/name. Select “Next”.
  9. Select or create a security group that will allow your existing private connection to connect to port 443 on the load balancer. Select “Next”.
    1. If you haven’t done so already, then make sure that you update the security group for your VPC Endpoint to allow access to the ALB security group that you’ve defined here.
  10. Create a new target group that will target IPs using the HTTPS protocol. Use HTTP protocol under the health checks. Under “Advanced Health Check settings”, ensure that the Port Override is set to 80 to match the HTTP protocol. Screenshot of the Target Group page with options entered
  11. ALB health checks Host headers will not contain a domain name, so S3 will return a non-200 HTTP response code. Add “307,405” to the health check success codes. Select “Next”.
  12. Register the VPC Endpoint IP addresses that you noted from Step 1 into the target group. Select “Next”.Screenshot of the VPC Endpoint IP addresses populated into the ALB target group page

Step 4: Configure extra listener rules

The Amazon S3 PrivateLink Endpoint is a REST API Endpoint, which means that trailing slash requests will return XML directory listings by default. To work around this, you’ll create a redirect rule to point all requests ending in a trailing slash to index.html.

  1. Navigate to your Internal ALB. Select it, and open the “Listeners” tab.
  2. On the right side of the table for the HTTPS listener, select “View/edit rules”.
  3. Select the “+” icon near the top to enable the insertion of a new rule.
  4. Under “IF”, select “Add Condition”, then select “Path…”.
  5. Enter “*/” under the Path Value.
  6. Under “THEN”, select “Add Action”, then select “Redirect to…”.
  7. Enter “#{port}” under “Enter port”.
  8. Pick “Custom host, path, query” under the dropdown.
  9. Modify “Path” to “/#{path}index.html”.
  10. Select “Save” in the top-right corner.Screenshot of the ALB Listener rules with options populated as described above

Step 5: Configure your DNS and test your ALB

Configure your on-premises or private DNS entries to point to the Internal ALB. You can use Route53 private hosted zones (PHZs) to set up a private alias record, and associate the PHZ with your VPC. You can also forward inbound DNS queries from on-premises to your VPC.

Step 6: Test your ALB

You must use a resource with private access to your VPC to reach the Internal ALB. Connect to your resource and try navigating to your new private DNS entry. If you only have console access to your resource, then you can also use a cURL command to validate the private static website.

Cleanup

To clean up, you can delete or revert the resources created in this guide in the following order:

  • Route53 PHZ DNS entries
  • ALB
  • Load Balancer Target Group
  • The Amazon S3 VPC Endpoint
  • Any related Security Groups that you created
  • Your S3 bucket policy

Conclusion

In this post, you learned how to create a private static Amazon S3 website with a custom domain, without having to provision any proxies on Amazon EC2. This can be useful when scaling your static website to large internal user bases, while allowing them to access a friendly domain name. Finally, you will benefit from not having to manage the undifferentiated heavy lifting of upgrading, securing, or scaling Amazon EC2 proxy instances.

If you want to add authentication mechanisms to your static website, then you can leverage authentication using your ALB using one of the use-cases provided in the documentation, or use AWS Verified Access.

About the Author

Headshot of Schuyler Jager

Schuyler Jager

Schuyler Jager is a Sr. Solutions Architect on the Canadian Central FSI Team. He brings over five years of AWS Networking experience in the start-up industry, and has modernized monolithic applications into containerized environments. In his spare time, you can find him building (and eventually flying) his experimental airplane!