Part 2: Deploying a Secure Full-Stack Application on AWS EC2 with Docker and Nginx

Part 2: Deploying a Secure Full-Stack Application on AWS EC2 with Docker and Nginx

·

8 min read

Introduction

In the first part of this guide, we outlined steps to set up your deployment environment necessary for deploying a full-stack application on AWS EC2. We launched an instance of EC2 and then made some additional security configuration settings. We then used automation via a user-data script to install Docker, Docker Compose, Git, Nginx, and other needed software. Finally, we logged into the EC2 instance to verify everything was correctly configured.

Nginx is a powerful, high-performance web server and reverse proxy server. It is excellent for handling many concurrent connections, delivering static content, load balancing, and working as a reverse proxy to distribute traffic to various application servers. Integrating Nginx into your Application architecture can enhance performance, scalability, and security.

This chapter is dedicated to configuring Nginx as a reverse proxy, configuring DNS and fortifying your Application with SSL/TLS. These steps are significant and critical for any robust Application. By setting up Nginx as a reverse proxy, you can enjoy two essential benefits for your Application—enhanced performance and security. It will efficiently handle all incoming traffic. Securing your Application with SSL/TLS ensures encrypted communication, preventing any interception or viewing of sensitive data during transit.

Setting Up Nginx as a Reverse Proxy

Configuring and setting up Nginx requires several steps. At the same time, it's essential to follow the necessary steps below to avoid errors ahead of time. Since our Application runs in EC2, let's start by viewing our Nginx configuration file and understanding its syntax before updating it.

Step 1: Viewing the Nginx Configuration File

  1. Connect to your EC2 and enter this command to view the Nginx configuration file.

     sudo nano /etc/nginx/sites-available/default
    

    Explanation:

  2. sudo nano: To open the file with an administrative command and to update the file

  3. /etc/nginx/sites-available/default: Location of the Nginx configuration file

  4. overview of the actual default file

    
     # You should look at the following URL's in order to grasp a solid understanding
     # of Nginx configuration files in order to fully unleash the power of Nginx.
     # https://www.nginx.com/resources/wiki/start/
     # https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/
     # https://wiki.debian.org/Nginx/DirectoryStructure
     # In most cases, administrators will remove this file from sites-enabled/ and
     # leave it as reference inside of sites-available where it will continue to be
     # updated by the nginx packaging team.
     # This file will automatically load configuration files provided by other
     # applications, such as Drupal or Wordpress. These applications will be made
     # available underneath a path with that package name, such as /drupal8.
     # Please see /usr/share/doc/nginx-doc/examples/ for more detailed examples.
     # Default server configuration
     server {
             listen 80 default_server;
             listen [::]:80 default_server;
    
             # SSL configuration
             # listen 443 ssl default_server;
             # listen [::]:443 ssl default_server;
             # Note: You should disable gzip for SSL traffic.
             # See: https://bugs.debian.org/773332
             # Read up on ssl_ciphers to ensure a secure configuration.
             # See: https://bugs.debian.org/765782
             # Self signed certs generated by the ssl-cert package
             # Don't use them in a production server!
             # include snippets/snakeoil.conf;
    
             root /var/www/html;
    
             # Add index.php to the list if you are using PHP
             index index.html index.htm index.nginx-debian.html;
    
             server_name _;
    
             location / {
                     # First attempt to serve request as file, then
                     # as directory, then fall back to displaying a 404.
                     try_files $uri $uri/ =404;
             }
    
             # pass PHP scripts to FastCGI server
             #location ~ \.php$ {
             #       include snippets/fastcgi-php.conf;
             #       # With php-fpm (or other unix sockets):
             #       fastcgi_pass unix:/run/php/php7.4-fpm.sock;
             #       # With php-cgi (or other tcp sockets):
             #       fastcgi_pass 127.0.0.1:9000;
             #}
             # deny access to .htaccess files, if Apache's document root
             # concurs with nginx's one
             #location ~ /\.ht {
             #       deny all;
             #}
     }
    
     # Virtual Host configuration for example.com
     # You can move that to a different file under sites-available/ and symlink that
     # to sites-enabled/ to enable it.
     #server {
     #       listen 80;
     #       listen [::]:80;
     #       server_name example.com;
     #       root /var/www/example.com;
     #       index index.html;
     #       location / {
     #               try_files $uri $uri/ =404;
     #       }
     #}
    

Step 2: Update the Nginx Configuration to Proxy Requests to the Backend Application

Now that we have viewed the Nginx configuration file in the above steps let's configure it to suit our backend Application. Our backend Application runs on port 8000, so let's update the file with the correct port number. You can configure yours to suit your actual backend port number.

  1. Open the Nginx configuration file.

     sudo nano /etc/nginx/sites-available/default
    
  2. Modify the configuration to proxy requests for your backend Application running in port 8000 and add the configuration settings below

     # You should look at the following URL's in order to grasp a solid understanding
     # of Nginx configuration files in order to fully unleash the power of Nginx.
     # https://www.nginx.com/resources/wiki/start/
     # https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/
     # https://wiki.debian.org/Nginx/DirectoryStructure
     # In most cases, administrators will remove this file from sites-enabled/ and
     # leave it as reference inside of sites-available where it will continue to be
     # updated by the nginx packaging team.
     # This file will automatically load configuration files provided by other
     # applications, such as Drupal or Wordpress. These applications will be made
     # available underneath a path with that package name, such as /drupal8.
     # Please see /usr/share/doc/nginx-doc/examples/ for more detailed examples.
    
     # Default server configuration
     server {
         listen 80 default_server;
         listen [::]:80 default_server;
    
         server_name _;
    
         location / {
             proxy_pass http://localhost:8000;  # Change this to your backend application port
             proxy_http_version 1.1;
             proxy_set_header Upgrade $http_upgrade;
             proxy_set_header Connection 'upgrade';
             proxy_set_header Host $host;
             proxy_cache_bypass $http_upgrade;
         }
    
         # Additional security headers (optional)
         add_header X-Frame-Options SAMEORIGIN;
         add_header X-Content-Type-Options nosniff;
         add_header X-XSS-Protection "1; mode=block";
     }
    
  3. Save the file, exit the editor, and use this command to Test the Nginx configuration for syntax errors.

     sudo nginx -t
    
  4. If the test passes, reload Nginx to apply the changes.

     sudo systemctl reload nginx
    

Step 3: Obtain an SSL Certificate with Certbot

Since the Nginx file from the previous steps has the proper configuration to proxy requests to your backend, you can obtain an SSL certificate using Certbot. To ensure we have the information requested by Certbot, we must first configure DNS for our domain.

Configure DNS for Your Domain

DNS, the Domain Name System, is like the Internet's phone system. It's the clever system that takes easy-to-understand domain names like example.com and turns them into geeky IP addresses like 192.0.2.1 that computers use to find each other on the network. To configure your DNS, follow the steps below.

  1. Login to your Domain registrar(The site where you bought your domain name).

  2. Go to the DNS management page and add an A record to point to your EC2 instance IP address.

    • For Type, enter A.

    • For Host, use any name of your choice.

    • For the Answer, enter your current EC2 IP address.

    • Click Add.

Sarting Certbot and Register Domain name

We have finally set up and configured our domain name from the previous steps; before starting up Certbot to obtain a certificate, we must ensure our Application runs within the EC2 instance.

  1. Start up your application using this docker command.

     sudo docker-compose -f /home/ubuntu/techdoc-backend/docker-compose.yml up -d
    
  2. Run this command to start up Certbot and follow the prompts by entering the necessary answers. Examples of prompt questions asked by Certbot are as follows:

    • Email address: Enter your email address.

    • Agree to the terms of service: Enter Y.

    • Willing to receive emails from Chatbot: Enter N.

    • Enter your domain name: Enter the domain name you registered in the previous steps.

    sudo certbot --nginx

Gif description

  1. After completing the Certbot set-up, use this command to check if everything works as expected.

     sudo nginx -t
    

Step 4: Update Nginx Configuration for HTTPS

In the earlier steps, we obtained an SSL Certificate using Certbot, but now we need to tweak our configuration files to ensure traffic is properly directed to HTTPS. Let's update the configuration and give our Application another test run.

  1. open the Nginx configuration file again

     sudo nano /etc/nginx/sites-available/default
    
  2. Copy and paste the configuration into the file.

     # Default server configuration
     server {
         listen 80 default_server;
         listen [::]:80 default_server;
         server_name _;
    
         location / {
             proxy_pass http://localhost:8000;  # Change this to your backend application port
             proxy_http_version 1.1;
             proxy_set_header Upgrade $http_upgrade;
             proxy_set_header Connection 'upgrade';
             proxy_set_header Host $host;
             proxy_cache_bypass $http_upgrade;
         }
    
         # Additional security headers (optional)
         add_header X-Frame-Options SAMEORIGIN;
         add_header X-Content-Type-Options nosniff;
         add_header X-XSS-Protection "1; mode=block";
     }
    
     # Secure server configuration
     server {
         server_name api.lokytech.co; # managed by Certbot
    
         location / {
             proxy_pass http://localhost:8000;  # Change this to your backend application port
             proxy_http_version 1.1;
             proxy_set_header Upgrade $http_upgrade;
             proxy_set_header Connection 'upgrade';
             proxy_set_header Host $host;
             proxy_cache_bypass $http_upgrade;
         }
    
         # Additional security headers (optional)
         add_header X-Frame-Options SAMEORIGIN;
         add_header X-Content-Type-Options nosniff;
         add_header X-XSS-Protection "1; mode=block";
    
         listen [::]:443 ssl ipv6only=on; # managed by Certbot
         listen 443 ssl; # managed by Certbot
         ssl_certificate /etc/letsencrypt/live/api.lokytech.co/fullchain.pem; # managed by Certbot
         ssl_certificate_key /etc/letsencrypt/live/api.lokytech.co/privkey.pem; # managed by Certbot
         include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
         ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
     }
    
     # Redirect HTTP to HTTPS
     server {
         listen 80;
         listen [::]:80;
         server_name api.lokytech.co;
    
         if ($host = api.lokytech.co) {
             return 301 https://$host$request_uri;
         }
    
         return 404; # managed by Certbot
     }
    
  3. After making changes, validate the configuration files and restart Ngnix using the command.

     sudo nginx -t
     sudo systemctl restart nginx
    
  4. Test the Application using your domain name to ensure everything works; you can use tools like Postman or others.

Conclusion

In this part of the guide, you have successfully followed these steps to configure Nginx to secure your Application with SSL/TLS and ensure that your Application can handle incoming traffic efficiently while maintaining secure communication through HTTPS. This significant achievement reflects your expertise and dedication to Application security.

Integrating Nginx and obtaining SSL/TLS certificates improved our Application's performance, scalability, and security. This setup ensures that sensitive data is encrypted in transit, securing it from any possible interception. These steps will smoothen your deployment process, setting an excellent base for your applications' security and efficiency.