top of page

AWS EC2 HTTPS, from Docker to External Domain: Guide

1000

AWS EC2 HTTPS, from Docker to External Domain: Guide

Updated: Jun 21

#aws-ec2 #docker #aws-cloudfront #ec2-externaldomain #easy-devops



Simple AWS EC2 HTTPS - without API Gateway
Simple AWS EC2 HTTPS - without API Gateway

Intro


During the initial development of Humaitrix Architecture, I faced some challenging time trying to make it run through HTTPS. In short, we have two servers (NodeJS and Python) and one of them is accessed by our React Web Application, using SSO as Authentication.


This is about the backend configuration, but no worries, the soon I will write a post about how to configure HTTPS for a front-end application, using S3 Bucket, Cloud Front and External DNS, but it was really easy to make. The challenge was to understand each of the AWS layers and configure them properly. The intent of this article is to share with you, the material I couldn't find easily.


If you are an AWS Expert and know a better way of achieving this, feel free to contact me, as I'm always open to improve my knowledge.


IMPORTANT: The evolution of this tutorial is to configure Cloud Front and API Gateway in a way to improve your security. This tutorial aim to cover the basic steps.


So let's start building our architecture of AWS EC2 HTTPS...


Table of Content



Before We Start our Mission towards AWS EC2 HTTPS:


I recommend you favorite the following AWS Services that will be used during this tutorial: EC2, Certificate Manager, and Cloud Front.


When you favorite services, they appear as the image below:


AWS Current Favorite Services
AWS Current Favorite Services

In order to favorite one service, search for it in the Search Bar and then click in the Star (when empty) near it's name.



AWS How to Mark Item as Favorite
AWS How to Mark Item as Favorite

AWS EC2 HTTPS Important Considerations

If you work for a company like us, a Start Up saving costs while creating either your MVP or if you don't have much data yet, EC2 is the way to go. AWS offers a nice free plan with 750 hours/month, in a server with 1 GB RAM, and 30 GB disk space. Depending on your system, is enough for a long while, especially if you are using NodeJS.


Important: be generous with your EC2 disk image (it's free up to 30 GB), it's challenging to change it after your image is running.


If you are running a Python application, I recommend you to erase your old docker images every now and then, because even a small application will consume a fair amount of disk space. Example: a simple FastAPI with MongoDB will consume at least 7 GB and will quickly scale to 12 GB.


Also, when choosing your technology, consider the CI/CD time, as Python deployment time is, for instance, considerably higher than a NodeJS application (6 GB image vs 300mb docker image, doing the same).


Enough talking, let's get started.


Step 1: Create the EC2 Template

The first thing we want to do is create an EC2 template. Keep in mind that every time you change the template settings, you will have to create a new version and re-deploy, renewing then your EC2 IP address and Public IPv4 DNS, which will require also a CloudFront update.


AWS EC2 - Launch Template, to create a new one
AWS EC2 - Launch Template, to create a new one

AWS EC2 - Create Launch Template
AWS EC2 - Create Launch Template

Next, inform your subdomain name and your template version name. Here is not a must, but it helps keeping things readable. In the following image, we are creating the v2 of our imaginary my-api-subdomain.yourdomain.com.


AWS EC2 Launch Template: Inform subdomain and template version description
AWS EC2 Launch Template: Inform subdomain and template version description

Next, Select your AMI (Amazon Machine Image). On this example, we will setup our EC2 using Amazon Linux 2023 AMI:

AMI Selection
AMI Selection

When selecting your AMI, prefers to choose the most up to date version, and careful with the Architecture (marked as 2 in the image below). This will affect your packages, especially if you are running a Python docker image. Also, it influences if you will build your docker image using the new Apple ARM processor.


Because my team consists in different types of processors, we decided to use the x86 and configure the Docker Image to build and deploy both options.


AWS EC2 AMI - Careful when Selecting the Architecture
AWS EC2 AMI - Careful when Selecting the Architecture

Another Important part of the AMI selection: The free tier offers 750h/month, which is 31 days and 6 hours. But be aware with the fact this generous amount of hours is distributed between all your free-tier instances. So only run what you need OR sign for an AWS Saving Plan, which can considerably decrease the monthly cost of an EC2 instance (soon I will write about that and post the link here).


Key Pair: Is super important as we plan to access our instance using SSH (Terminal).


AWS EC2 - Key Pair - Essential for SSH Access
AWS EC2 - Key Pair - Essential for SSH Access

Select the option Create new key pair, inform a readable name, select the option that best suits your needs, and if you need help selecting one or another, both are modern and offers reliable security. If your system does not offers any requirement constraints, go for ED25519, because it offers the same level of security with a shorter key length, because it's more modern.


Save your .pem file in your Password Management tool, you will need it when configuring the Docker, in the Step 3 - Docker.


AWS EC2 - Key Pair Creation - Select ED25519
AWS EC2 - Key Pair Creation - Select ED25519

Next, in Networking Settings, you must be careful as, for safety reasons, prefer to don't expose more ports than you actually need, so before you start configuring your Network Settings, grab a piece of paper and write down everything your docker image will access and everything it will be accessible from.


This is extremely important, because by opening more doors it actually needs is a security breach. Also, if possible, limit the IP Addresses which will have access to your EC2.


Before configuring, I will present some useful inbound ports you might need, but keep in mind, the database ports presented below are the default ports, and that you must add only the necessary ones (usually 22, 80, 443 and the one of your database):


Description

Type

Port

SSH

TCP

22

HTTP

TCP

80

HTTPS

TCP

443

Postgres

TCP

5432

MongoDB

TCP

27017

SQL

TCP

1433

VPC:


A Virtual Private Cloud (VPC) is a fundamental component in Amazon Web Services (AWS) that allows you to create a logically isolated section of the AWS Cloud where you can launch AWS resources like Amazon EC2 instances, store data in Amazon S3, and more. Between many key reasons why you need a VPC when creating an EC2 instance, I highlight the following: Isolation and Network Control and Subnet Configuration.


Before creating your new subnet, open a new tab, navigate to your AWS Console and type VPC in the search bar:


AWS VPC
AWS VPC

Next, click on Create VPC:


Create VPC
Create VPC

Define the main settings for your VPC:


AWC VPC - Basic Settings for EC2
AWC VPC - Basic Settings for EC2

Once you created and saved your VPC, return your EC2 creation TAB and click on Create new Subnet:


AWS EC2 - Create New Subnet
AWS EC2 - Create New Subnet

A new tab will automatically open, and there, you select your created VPC:



AWS EC2 - Create Subnet
AWS EC2 - Create Subnet

Once you select your VPC, the subnet options will then be available. Inform a readable name and certify the IP Addresses are correct, as you defined during your VPC creation:



AWS EC2 - Subnet Creation
AWS EC2 - Subnet Creation

Once you have successfully created your subnet, return to the EC2 creation TAB, click on the refresh button right to the subnet creation dropdown, and then select the recently created subnet.


Next, the Firewall. Pick up the table we created with the necessary inbound/outbound ports we are going to expose. We will need it.


Select the option "create security group" and inform a readable name for it. In the description you can add a friendly text to remind which ports are being exposed:

AWS EC2 - Firewall (Security Groups)
AWS EC2 - Firewall (Security Groups)

Usually, we will need the ports 22 (SSH), 80 (HTTP) and 443 (HTTPS). After adding one of the items, click on Add Security Group Rule. In the end, your Inbound Security Group Rules should look like this:


AWS EC2 - Inbound Security Group Rules
AWS EC2 - Inbound Security Group Rules

Storage: The AWS free-tier comes with 30 GB of free storage. But keep in mind this is the sum of all your EC2 images, so if you plan to have 3 images, your Storage can only have 10 GB max. By default it suggests 8 GB. Resize according to your needs. For a small application, 8GB is too small for Python, and too much for NodeJS.


AWS EC2 - Storage
AWS EC2 - Storage

In Advanced Details, leave as it is, and I will write, in the near future, an article talking more about each of the options. Yet, be aware that repetitive tasks that you run every time you restart your EC2 can be added in the User Data of this Section. Not to mention the behaviour of start/restart/shutdown or even the IAM related to this EC2:



AWS EC2 - Advanced Details - This Needs an Specific Article
AWS EC2 - Advanced Details - This Needs an Specific Article

Congratulations! We are ready to create our EC2 Launch Instance. Click in the yellow button at the bottom right, and no worries, this is the most complicated step.


Launch Template
Launch Template

Step 2: Launching your Template Instance

Now that we have created our Template, we must run it. In order to do so, click in your EC2 favorite shortcut, then in the arrow near "Launch Instances" and finally, select the option Launch Instance From Template.


AWS EC2 - Launch Instance From Template
AWS EC2 - Launch Instance From Template

In the screen that will open, select your recently created template, and after, the template version:


Select a Launch Template
Select a Launch Template

After that, click in the bottom right button, Launch Instance. You don't need to care about other settings, as we defined everything when creating the Template:


Click Launch to Start
Click Launch to Start

Congratulations! Your instance should be up and running. Take note of the the following fields, you will need them in the next step:


IP Address

Public IPv4 DNS


AWS EC2 - Public IP and IPv4 DNS - Allow You to Access Your EC2
AWS EC2 - Public IP and IPv4 DNS - Allow You to Access Your EC2

Step 3: Docker

To avoid creating an article too long, I will share the key points when creating your docker image and how to deploy it to your recently created AWS EC2, starting with the fact we will use yum.


About YUM: the primary tool for getting, installing, deleting, querying, and managing Red Hat Enterprise Linux RPM software packages from official Red Hat software repositories, as well as other third-party repositories. yum is used in Red Hat Enterprise Linux versions 5 and later. Versions of Red Hat Enterprise Linux 4 and earlier used up2date. Reference: https://access.redhat.com/solutions/9934 


  1. If you are using Apple ARM M1 Processor (or superior), and your EC2 processor is configured as x86, you must create your docker image for different platforms, and for that, you must create a builder (docker buildx create). And because of the multi-platform and the way buildx was designed, up to the moment this article was written (November/2023), it's important to know that you can't build locally.To create a new builder, run this once (remember to change your builder name to something more meaningful):

  2. Next, you want to create a script with the main information you will need. This will save you a lot of time. I divided the full script into 3 parts:

    1. Variables (useful in your local environment and also in your EC2)

    2. Docker Build Scripts

    3. Docker Running Scripts

  3. Docker Variables - The AWS Key File was generated in the Key Pair:

  4. Docker Build Scripts (basically authenticate in your docker repository and publish a new image):  

  5. EC2 First Time Setup: This sequence of commands will install yum, docker, and nginx (necessary for HTTPS), while also giving the necessary rights to docker and nginx). Note: this is the part you might want to add to User Data, in Template, Advanced Details

  6. Nginx Basic Configuration: If you don't have a certificate and if you are deploying a testing or a development environment, the configuration below for your nginx will do. In short, Nginx will listen to a port and it will redirect the content to another port. Reference: https://docs.nginx.comRemember to update your script with the correct HTTP port needed by your docker image and the fact this configuration is very basic, and should not be used if you are planning to use multiple sub-domains or in your production environment.    server

  7. Useful Docker/Linux Commands:

  8. Once we have docker and nginx installed and configured, it's time to run our docker image. As I am using NodeJS, my running command is yarn start, but if you are running a Python or any other language, you must change your start script. Also, double-check your desired port, in my case, 4000:

  9. Start Nginx (if everything is alright, no message will be displayed):

  10. Test inside your EC2,, that both your 4000 and 443 ports are working properly:

  11. If everything is alright, you should get your api-server welcome message, meaning you successfully configured EC2, Docker and Nginx

Step 4: Create an AWS Public Certificate

A public certificate is needed in order to link your DNS provider with the cloud-front. For some reason, the first time I tried to create this, it took a very long while to be available, but once the first one was ready, the new ones got created almost instantaneously.


Before creating your certificate, keep in mind you can create it using a wildcard (*) or you can create specific for your subdomains. Example:


*.domain.com (will cover www.domain.com, api.domain.com, etc)


To start, select Certificate Manager either from your favorites or type in the search bar:


AWS - Certificate Manager
AWS - Certificate Manager

For this step only, It's important that you switch your region to us-east-1, the only one (up to this moment), able to create public certificates:


AWS - Select Region us-east-1
AWS - Select Region us-east-1

In the next screen, click on Request a Certificate, then select Public Certificate (it's free):


AWS - Creating a New Private Certificate
AWS - Creating a New Private Certificate

Request a Public Certificate:


AWS - Request a Public Certificate
AWS - Request a Public Certificate

When informing your domain information, keep in mind the following:


  • wildcards: *.yourdomain.com, for instance, should only be used if you are managing all your subdomains (www, api, dev, demo, etc) in AWS. If you use a CMS for your website and AWS for your admin-panel, for instance, don't use this

AWS - Public Certificate - Wildcard
AWS - Public Certificate - Wildcard

  • specific subdomains: if you can't use the *.domain.com wildcard, specify your subdomains, one by one. Keep in mind, you can't use wildcard and part of the name of the subdomain, unfortunately:


AWS - Public Certificate - Subdomains
AWS - Public Certificate - Subdomains

In verification method, prefer DNS, it's way faster. For Key Algorithm, select RSA 2048 and then click on Request:


Domain Verification
Prefer DNS, if possible

Well done, your Public Certificate has been created. Wait some few seconds and open the certificates page once again. Your certificate will be ready to be assigned to your DNS provider. But before we go there to inform it, open your recently created Public Certificate and store somewhere (notepad for instance) your CNAME Name and Value, as below. We will need it in the final step:


CNAME Name: _some_numbers_and_letters.www.your-domain.com.

CNAME Value: _some_numbers_and_letters.mhbtsbpdnt.acm-validations.aws.



Step 5: Create a new Cloud Front

We are almost done, but before starting, remember to switch back to your previous AWS Region.


This step is quite easy but requires attention to some details:


  • Origin domain: (your EC2 ipv4 public address)

  • Protocol: HTTP Only

  • Name: your DNS Domain name: yourdomain.com

  • Viewer protocol Policy: Redirect HTTP to HTTPS

  • Allowed HTTP Methods: GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE (if it's an API Server)

  • Cache Policy: caching optimized

  • Origin Request Policy: AllViewerAndCloudFrontHeaders-2022-06

  • Response Header Policy: CORS-with-preflight-and-SecurityHeadersPolicy

  • WAF: I recommend you to enable this because it enables some security protocols by default ("Keep your application secure from the most common web threats and security vulnerabilities using AWS WAF. Blocked requests are stopped before they reach your web servers."). But as it involves some costs, you might want to keep it enabled for production and staging only. I am a defender of the "better safe than sorry". It's around 15$/month, per environment

  • Alternate Domain Name: it's your domain address, like yourdomain.com 

  • Custom SSL Certificates: select the public certificate we created previously


Add your description and then click on "Create Distribution".


After informing above information, open your recently created CloudFront and copy the autogenerated distribution domain name, which should be a combination-of-letters-and-numbers.cloudfront.net.



AWS Cloud Front - Distribution Domain Name
AWS Cloud Front - Distribution Domain Name

Step 6: DNS Setup

Finally, open your Domain Provider DNS Settings and add two new CNAME entries, as bellow:

​Name

Value

certificate CNAME

certificate CNAME value

subdomain (www, dev-api, api, etc)

letters-and-numbers.cloudfront.net


Conclusion

And that's pretty much what it's necessary to have your EC2 with Docker, CloudFront, running with an external Domain DNS.


Some additional tips:


  • I tried to expose the docker image to the 443 port, it won't work

  • if you face some CloudFront errors when accessing your deployed website, check the Origin Protocol (it must be HTTP Only)

  • Sometimes you might face some CORS errors but they are completely unrelated to your Server API configuration but the Request Header itself. If this happens, check this reply I added to Stack-Overflow: https://stackoverflow.com/a/77295468/5936119 


If you still have issues, tips, or suggestions, feel free to contact me through LinkedIn, as I spent more than a month to realize what I was doing wrong.


Good luck and remember: better safe than sorry! Activate the WAF, please!

43 views0 comments

Comments


bottom of page