Tuesday, August 9, 2022
HomeWordPress DevelopmentDeploying Django Software on AWS with Terraform. Namecheap Area + SSL

Deploying Django Software on AWS with Terraform. Namecheap Area + SSL


In earlier steps, we have deployed Django with AWS ECS, linked it to the PostgreSQL RDS, and arrange GitLab CI/CD

On this step, we’re going to:

  • Join Namecheap area to Route53 DNS zone.
  • Create an SSL certificates with Certificates Supervisor.
  • Reroute HTTP visitors to HTTPS and disable ALB host for Django utility.
  • Add /well being/ route for Well being Checks.



Establishing Namecheap API

I have already got a site title on Namecheap. So I select to attach the Namecheap area to an AWS Route53 zone. However you may register area with AWS Route53.

First, let’s allow API entry for Namecheap. Look by this information to obtain APIKey and add your IP to the whitelist.

Second, add the Namecheap supplier to Terraform undertaking. Add to the supplier.tf file following code:

terraform {
  required_providers {
    namecheap = {
      supply  = "namecheap/namecheap"
      model = ">= 2.0.0"
    }
  }
}

supplier "namecheap" {
  user_name   = var.namecheap_api_username
  api_user    = var.namecheap_api_username
  api_key     = var.namecheap_api_key
  use_sandbox = false
}
Enter fullscreen mode

Exit fullscreen mode

In variables.tf add:

# Namecheap
variable "namecheap_api_username" {
  description = "Namecheap APIUsername"
}
variable "namecheap_api_key" {
  description = "Namecheap APIKey"
}
Enter fullscreen mode

Exit fullscreen mode

Additionally add TF_VAR_namecheap_api_username and TF_VAR_namecheap_api_key variables to .env to supply values to the corresponding Terraform variables.

TF_VAR_namecheap_api_username=YOUR_API_USERNAME
TF_VAR_namecheap_api_key=YOUR_API_KEY
Enter fullscreen mode

Exit fullscreen mode

Import .env variables with export $(cat .env | xargs) and run terraform init so as to add a Namecheap supplier to the undertaking.



Connecting Area to AWS

Now, let’s create a Route53 zone for the Namecheap area and arrange AWS nameservers. Thus, all DNS queries will likely be routed to the AWS Route53 nameservers, and we will handle DNS data from the AWS Route53 zone.

Add to the variables.tf following code:

# Domains
variable "prod_base_domain" {
  description = "Base area for manufacturing"
  default = "example53.xyz"
}
variable "prod_backend_domain" {
  description = "Backend net area for manufacturing"
  default = "api.example53.xyz"
}
Enter fullscreen mode

Exit fullscreen mode

Add a route53.tf file:

useful resource "aws_route53_zone" "prod" {
  title = var.prod_base_domain
}

useful resource "namecheap_domain_records" "prod" {
  area = var.prod_base_domain
  mode   = "OVERWRITE"

  nameservers = [
    aws_route53_zone.prod.name_servers[0],
    aws_route53_zone.prod.name_servers[1],
    aws_route53_zone.prod.name_servers[2],
    aws_route53_zone.prod.name_servers[3],
  ]
}
Enter fullscreen mode

Exit fullscreen mode

Run terraform apply. Examine nameservers on Namecheap:



Creating SSL Certificates

Now, let’s create an SSL certificates and arrange DNS A file for api.example53.xyz area.

Add to the route53.tf following code:

...

useful resource "aws_acm_certificate" "prod_backend" {
  domain_name       = var.prod_backend_domain
  validation_method = "DNS"
}

useful resource "aws_route53_record" "prod_backend_certificate_validation" {
  for_each = {
    for dvo in aws_acm_certificate.prod_backend.domain_validation_options : dvo.domain_name => {
      title   = dvo.resource_record_name
      file = dvo.resource_record_value
      sort   = dvo.resource_record_type
    }
  }

  allow_overwrite = true
  title            = every.worth.title
  data         = [each.value.record]
  ttl             = 60
  sort            = every.worth.sort
  zone_id         = aws_route53_zone.prod.zone_id
}

useful resource "aws_acm_certificate_validation" "prod_backend" {
  certificate_arn         = aws_acm_certificate.prod_backend.arn
  validation_record_fqdns = [for record in aws_route53_record.prod_backend_certificate_validation : record.fqdn]
}

useful resource "aws_route53_record" "prod_backend_a" {
  zone_id = aws_route53_zone.prod.zone_id
  title    = var.prod_backend_domain
  sort    = "A"

  alias {
    title                   = aws_lb.prod.dns_name
    zone_id                = aws_lb.prod.zone_id
    evaluate_target_health = true
  }
}
Enter fullscreen mode

Exit fullscreen mode

Right here we’re going to create a brand new SSL certificates for api.example53.xyz, validate the SSL certificates through DNS CNAME file, and add DNS A file to Load Balancer.

Apply adjustments with terraform apply and look forward to certificates validation. Normally, it takes as much as a number of minutes. However in some instances, it might probably take a number of hours. You may examine extra data right here.



Redirecting HTTP to HTTPS

Now let’s use the issued SSL certificates to allow HTTPS. Change the useful resource "aws_lb_listener" "prod_http" block within the load_balancer.tf with the next code:

# Goal listener for http:80
useful resource "aws_lb_listener" "prod_http" {
  load_balancer_arn = aws_lb.prod.id
  port              = "80"
  protocol          = "HTTP"
  depends_on        = [aws_lb_target_group.prod_backend]

  default_action {
    sort = "redirect"
    redirect {
      port        = "443"
      protocol    = "HTTPS"
      status_code = "HTTP_301"
    }
  }
}

# Goal listener for https:443
useful resource "aws_alb_listener" "prod_https" {
  load_balancer_arn = aws_lb.prod.id
  port              = "443"
  protocol          = "HTTPS"
  ssl_policy        = "ELBSecurityPolicy-2016-08"
  depends_on        = [aws_lb_target_group.prod_backend]

  default_action {
    sort             = "ahead"
    target_group_arn = aws_lb_target_group.prod_backend.arn
  }

  certificate_arn = aws_acm_certificate_validation.prod_backend.certificate_arn
}
Enter fullscreen mode

Exit fullscreen mode

Right here we redirect unsecured HTTP visitors to HTTPS and add a listener for the HTTPS port. Apply adjustments and examine https://api.example53.xyz URL. You need to see Django beginning web page.



Establishing the ALLOWED_HOSTS variable

Now, let’s present the ALLOWED_HOSTS setting to the Django app. It is essential to forestall HTTP Host header assaults. So, Django Software ought to solely settle for our area api.example53.xyz within the host header.

Now Django accepts any area, for instance, Load Balancer’s area. Go to https://prod-1222631842.us-east-2.elb.amazonaws.com to examine this truth. You may ignore the warning about an invalid SSL Certificates and see that Django responds to this host.

Additionally, let’s disable a Debug mode and take away the SECRET_KEY worth from the code to enhance safety. Add the TF_VAR_prod_backend_secret_key variable with a random generated worth to the .env, run export $(cat .env | xargs), and specify this var in variables.tf:

variable "prod_backend_secret_key" {
  description = "manufacturing Django's SECRET_KEY"
}
Enter fullscreen mode

Exit fullscreen mode

Subsequent, cross the area title and SECRET_KEY in ecs.tf, arrange SECRET_KEY, DEBUG, and ALLOWED_HOSTS variables in backend_container.json.tpl and apply adjustments:

locals {
  container_vars = {
    ...

    area = var.prod_backend_domain
    secret_key = var.prod_backend_secret_key
  }
}
Enter fullscreen mode

Exit fullscreen mode

"atmosphere": [
  ...
  {
    "name": "SECRET_KEY",
    "value": "${secret_key}"
  },
  {
    "name": "DEBUG",
    "value": "off"
  },
  {
    "name": "ALLOWED_HOSTS",
    "value": "${domain}"
  }
],
Enter fullscreen mode

Exit fullscreen mode

Now now we have all mandatory atmosphere variables on ECS. Transfer to the Django app and alter settings.py:

# SECURITY WARNING: preserve the key key utilized in manufacturing secret!
SECRET_KEY = env("SECRET_KEY", default="ewfi83f2ofee3398fh2ofno24f")

# SECURITY WARNING: do not run with debug turned on in manufacturing!
DEBUG = env("DEBUG", forged=bool, default=True)

ALLOWED_HOSTS = env("ALLOWED_HOSTS", forged=record, default=["*"])
Enter fullscreen mode

Exit fullscreen mode

Right here we obtain SECRET_KEY, DEBUG, and ALLOWED_HOSTS variables from env variables. We offer default SECRET_KEY to permit operating the appliance domestically with out specifying SECRET_KEY within the .env file.



Well being Examine

All person’s requests would have Host header api.example53.xyz. However, we even have well being examine requests from a load balancer.

AWS load balancers can robotically examine our container’s well being. If the container responds accurately, the load balancer considers that concentrate on is wholesome. In any other case, the goal will likely be marked as unhealthy. Load balancer routes visitors to wholesome targets solely. Thus, person requests would not hit unhealthy containers.

For HTTP or HTTPS well being examine requests, the host header incorporates the IP handle of the load balancer node and the listener port, not the IP handle of the goal and the well being examine port.

We do not know the Load Balancer IP handle. Additionally, this IP might be modified after a while. Subsequently, we can not add the Load Balancer host to the ALLOWED_HOSTS.

The answer is to put in writing a customized middleware that returns a profitable response earlier than the host checking within the SecurityMiddleware.

First, go to the infrastructure, change the well being examine URL in load_balancer.tf to /well being/, and apply adjustments:

useful resource "aws_lb_target_group" "prod_backend" {
  ...
  health_check {
    path = "/well being/"
    ...
  }
}
Enter fullscreen mode

Exit fullscreen mode

Return to the Django undertaking and create django_aws/middleware.py:

from django.http import HttpResponse
from django.db import connection


def health_check_middleware(get_response):
    def middleware(request):
        # Well being-check request
        if request.path == "/well being/":
            # Examine DB connection is wholesome
            with connection.cursor() as cursor:
                cursor.execute("SELECT 1")

            return HttpResponse("Wholesome!")

        # Common requests
        return get_response(request)

    return middleware
Enter fullscreen mode

Exit fullscreen mode

Add this middleware to the settings.py earlier than the SecurityMiddleware:

MIDDLEWARE = [
    'django_aws.middleware.health_check_middleware',
    'django.middleware.security.SecurityMiddleware',
    ...
]
Enter fullscreen mode

Exit fullscreen mode

Run python handle.py runserver and examine 127.0.0.1:8000/well being/ URL in your browser. You need to see the textual content response Wholesome!.

Commit and push adjustments, look forward to the pipeline and examine the Load Balancer area once more https://prod-57218461274.us-east-2.elb.amazonaws.com/. Now, we get a Dangerous Request error. Additionally, we did not see a traceback or different debug data, so we will ensure that the debug mode is disabled.

400 Bad Request

Additionally, navigate to https://prod-57218461274.us-east-2.elb.amazonaws.com/well being/ in your browser to examine health_check_middleware. We get the Wholesome! response. So, the Load Balancer will be capable of examine containers’ well being with out offering the proper Host header.

Health Check Success Response

Congratulations! We have efficiently arrange a site title, created well being checks, disabled the debug mode, and eliminated SECRET_KEY worth from the supply code. Don’t forget to push infrastructure code to GitLab.

You’ll find the supply code of backend and infrastructure initiatives right here and right here.

In case you want technical consulting in your undertaking, try our web site or join with me instantly on LinkedIn.

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

- Advertisment -
Google search engine

Most Popular

Recent Comments