Cloud Agnostic Secure Delivery of a Website on Akamai Edge Platform using Terraform — Part II

Deepak
AWS Tip
Published in
8 min readMar 29, 2022

--

Continuing from part I of this post that is located here

Quick recap — In the previous part we discussed:-

  1. Site architecture — AWS + Akamai
  2. Setting up AWS and Akamai CLI
  3. Setting up terraform
  4. Code Walkthrough of AWS related terraform file

In this part we will solely concentrate of Akamai related terraform stuff.

Let’s begin where we left in part I i.e. about creating terraform files for akamai stuff.

4. akamai-property.tf — This block onboards website on to Akamai platform to take advantage of edge computing including but not limited to CDN. Akamai is much more than just only CDN.

//data source to define the location of the JSON template files and provide information about any user-defined variables included within the templates.
data "akamai_property_rules_template" "rules" {
template_file = abspath("${path.module}/property-snippets/main.json")
variables {
name = "cpcode_value"
value = replace(akamai_cp_code.cp_code.id,"cpc_","")
type = "number"
}
variables {
name = "origin_value"
value = var.originValue
type = "string"
}
}
// Create and manage cp_code
resource "akamai_cp_code" "cp_code" {
product_id = var.productId
contract_id = data.akamai_contract.contract.id
group_id = data.akamai_group.group.id
name = "${var.akamaiPrefix}-cp"
}
// Create and manage Edge Hostnameresource "akamai_edge_hostname" "edge_hostname" {
product_id = var.productId
contract_id = data.akamai_contract.contract.id
group_id = data.akamai_group.group.id
ip_behavior = "IPV4"
edge_hostname = "${var.akamaiPrefix}.edgesuite.net"
}
// Create and manage Akamai Property
resource "akamai_property" "property" {
name = "${var.akamaiPrefix}-pm"
product_id = var.productId
contract_id = data.akamai_contract.contract.id
group_id = data.akamai_group.group.id
hostnames {
cname_from = "www.${var.akamaiPrefix}"
cname_to = "${var.akamaiPrefix}.edgesuite.net"
cert_provisioning_type = "CPS_MANAGED"
}
hostnames {
cname_from = "${var.akamaiPrefix}.edgesuite-staging.net"
cname_to = "${var.akamaiPrefix}.edgesuite.net"
cert_provisioning_type = "CPS_MANAGED"
}
hostnames {
cname_from = "<WWW-Hostname>"
cname_to = "${var.akamaiPrefix}.edgesuite.net"
cert_provisioning_type = "CPS_MANAGED"
}
rule_format = var.ruleFormat
rules = data.akamai_property_rules_template.rules.json
depends_on = [
akamai_edge_hostname.edge_hostname
]
}
// Activate the property in Staging
resource "akamai_property_activation" "activate_staging" {
property_id = akamai_property.property.id
contact = var.email
version = akamai_property.property.latest_version
network = "STAGING"
note = var.activationNote
depends_on = [
aws_instance.web
]
}

akamai_property_rules_template — Akamai keeps all the business rules in template called property rules. These rules define how your site will be accessed and what business logic should be executed under what match criteria. I am not going to going to go into detail of property rules but it is way of letting you tell Akamai to how to deliver your website, for e.g. for how much time edge should cache your content, how should edge perform the redirect, what should happen when we get error from origin and like wise many many scenarios. You can get these rule templates easily either by using Akamai cli’s property management module.

or cloning using akamai terraform module

Also note how we are using variables to interpolate the fields within rules json file.

akamai_cp_code — Content Provider (CP) code is unique identifier that Akamai assigns to you for reporting, billing, and monitoring traffic served over the Akamai network. This block creates a cp code and later use it in property rules template json to assign it to your web property

akamai_edge_hostname — An edge hostname is the CNAME target you use when directing your end user traffic to Akamai. Basically a carrier for your website traffic to akamai edge platform. This resource creates either new edge hostname or use an existing one. In this example i am creating new edge hostname.

akamai_property — this is next resource which is for provisioning/managing your digital property on Akamai platform.

akamai_property_activation — Akamai consists of two network, staging and production. It is highly recommended that you test your configuration in staging before moving to production. This resource block make sure that akamai property is activated post creation or after making any changes.

5. akamai-dns.tf— This file defines the Akamai dns block, basically we are going to create two records

resource "akamai_dns_record" "origin" {
zone = "<Zone_Name>"
name = var.originValue
recordtype = "A"
active = true
ttl = 30
target = [aws_instance.web.public_ip]
depends_on = [
aws_instance.web
]
}
resource "akamai_dns_record" "www" {
zone = "<Zone_Name>"
name = "<WWW-Hostname>"
recordtype = "CNAME"
active = true
ttl = 30
target = ["${var.akamaiPrefix}.edgesuite.net"]
depends_on = [
aws_instance.web
]
}

(i) an A record for origin server so that Akamai edge platform can fetch data from origin when needed

(ii) a CNAME record to divert traffic from www(main hostname) to Akamai edge hostname (that we coded in previous file)

6. akamai-appsec.tf — this file consists of various security rules to protect your site. Please note that for this demo i am using only few control whereas akamai offer large number of control to keep your site safe. Checkout the appsec guide for more information on options available.

https://registry.terraform.io/providers/akamai/akamai/latest/docs/guides/get_started_appsec

resource "akamai_appsec_configuration" "akamai_appsec" {contract_id = replace(data.akamai_contract.contract.id, "ctr_", "")group_id  = replace(data.akamai_group.group.id, "grp_", "")name = "${var.akamaiPrefix}-SecurityConfig"description = "Security Configuration for ${var.akamaiPrefix}-pm"host_names = [ "www.${var.akamaiPrefix}" ]depends_on = [ akamai_property_activation.activate_staging ]}resource "akamai_appsec_security_policy" "security_policy" {config_id = akamai_appsec_configuration.akamai_appsec.config_iddefault_settings       = truesecurity_policy_name = var.policy_namesecurity_policy_prefix = "${var.securityPolicyPrefix}"}resource "akamai_appsec_advanced_settings_pragma_header" "pragma_header" {config_id = akamai_appsec_configuration.akamai_appsec.config_idsecurity_policy_id = akamai_appsec_security_policy.security_policy.security_policy_idpragma_header = file("${path.module}/appsec-snippets/pragma_header.json")}resource "akamai_networklist_network_list" "IPBLOCKLIST" {#for_each = { for country in local.country_code : country.CountryName => country }#count = length(local.country_code)name = "IPBLOCKLIST"type = "IP"description = "IPBLOCKLIST"list = var.ipblock_listmode = "REPLACE"depends_on = [null_resource.network_list_extraction_script]}resource "akamai_networklist_network_list" "IPBLOCKLISTEXCEPTIONS" {name = "IPBLOCKLISTEXCEPTIONS"type = "IP"description = "IPBLOCKLISTEXCEPTIONS"list = var.ipblock_list_exceptionsmode = "REPLACE"}resource "akamai_networklist_network_list" "GEOBLOCKLIST" {name = "GEOBLOCKLIST"type = "GEO"description = "GEOBLOCKLIST"#list = ["data.local_file.output_for_terraform.content"]list = var.geoblock_listmode = "REPLACE"#depends_on = [#  null_resource.network_list_extraction_script#]}/*resource "akamai_networklist_network_list" "GEOBLOCKLIST" {name = "GEOBLOCKLIST"type = "GEO"description = "GEOBLOCKLIST"list = [upper(data.local_file.output_for_terraform.content)]mode = "REPLACE"depends_on = [null_resource.network_list_extraction_script]}*/resource "akamai_networklist_network_list" "SECURITYBYPASSLIST" {name = "SECURITYBYPASSLIST-DJ"type = "IP"description = "SECURITYBYPASSLIST"list = var.securitybypass_listmode = "REPLACE"}resource "akamai_appsec_match_target" "match_target" {config_id = akamai_appsec_configuration.akamai_appsec.config_idmatch_target = templatefile("${path.module}/appsec-snippets/match_targets.json", {config_id = akamai_appsec_configuration.akamai_appsec.config_id,hostname = "www.${var.akamaiPrefix}",policy_id = akamai_appsec_security_policy.security_policy.security_policy_id,securitybypass_list = akamai_networklist_network_list.SECURITYBYPASSLIST.id})}resource  "akamai_appsec_ip_geo" "ip_geo_block" {config_id = akamai_appsec_configuration.akamai_appsec.config_idsecurity_policy_id = akamai_appsec_security_policy.security_policy.security_policy_idmode = "block"ip_network_lists = [ akamai_networklist_network_list.IPBLOCKLIST.id ]geo_network_lists = [ akamai_networklist_network_list.GEOBLOCKLIST.id ]exception_ip_network_lists = [ akamai_networklist_network_list.IPBLOCKLISTEXCEPTIONS.id ]}

In above file we are creating a

akamai_appsec_configuration — appsec configuration i.e. a container for all security features.

akamai_appsec_security_policy — within a security configuration you can have multiple security policy which can be specific to some part of your website for e.g. login pages having more restrictive security policy as compared to blogging page.

akamai_appsec_advanced_settings_pragma_header — pragma headers can reveal important information about your origin server and this control can restrict that.

akamai_networklist_network_list — This is kind of network firewall list which can allow/block traffic based on IP or GEO. We are using this block to create few network lists and than blocking traffic for certain geographies

akamai_appsec_match_target — It is the way to let akamai know that which part of your website should be attached to which particular security config. As i said earlier not every part of your website would have same security requirement. Match target defines way to choose security policies based on path based matches.

Section 5 — Other terraform files

Before provisioning lets see my variables.tf, output.tf and terraform.tfvars file as well

Variables.tf

variable "prefix" {
description = "The prefix used for all resources in this example"
default = "TerraformDemo4Akamai"
}
variable "aws_region" {
default = "us-east-1"
}
variable "vpc_cidr" {
default = "192.168.0.0/16"
}
variable "public_subnet_cidr"{
default = "192.168.10.0/24"
}
variable "sg_name" {
default = "sg-akamai"
}
variable "ami_id"{
default = "ami-0c02fb55956c7d316"
}
variable "instance_type"{
default = "t2.micro"
}
variable "instance_key"{
default = "/Users/djha/.ssh/ec2_id_rsa.pub"
}
##### Akamai Related Variables ####variable "env" {
type = string
description = "a variable to hold akamai network name"
default = "staging"
}
variable "akamaiPrefix" {
type = string
description = "a variable to hold prefix to interpolate other values"
}
variable "section" {
type = string
description = "a variable to hold edgerc section name"
}
variable "email" {
type = list
default = ["your.email@id"]
}
variable "groupName" {
type = string
description = "a variable to hold group name"
}
variable "productId" {
type = string
description = "a variable to hold product ID"
}
variable "originValue" {
type = string
description = "a variable to hold product ID"
}
variable "ruleFormat" {
type = string
description = "a variable to hold product ID"
default = "latest"
}
variable "activationNote" {
type = string
description = "a variable to hold Activation Note"
}
variable "policy_name" {
type = string
description = "a variable to hold security policy name"
}
variable "ipblock_list" {
type = list
description = "a variable to hold Ip Block List"
}
variable "ipblock_list_exceptions" {
type = list
description = "a variable to hold Ip Block List exceptions"
}
variable "geoblock_list" {
type = list
description = "a variable to hold GEO Block List"
}
variable "securitybypass_list" {
type = list
description = "a variable to hold securitybypass_list"
}
variable "securityPolicyPrefix" {
type = string
description = "a 4 alphaNumeric prefix"
}

terraform.tfvars

akamaiPrefix = "aws.<Hostname>"
aws_region="us-east-1"
#propertyName = ""
section = "terraform"
groupName = "DJ"
productId = "prd_SPM"
ruleFormat = "latest"
email = ["you.email@id"]
activationNote = "Activated via. Terraform"
originValue = "origin.<Hostname>"
policy_name = "WAF Security Policy"
ipblock_list = []
ipblock_list_exceptions = []
geoblock_list = ["RU"]
securitybypass_list = []
securityPolicyPrefix = "dj12"

outputs.tf

output "public_ip" {
value = aws_instance.web.public_ip
}
output "private_ip" {
value = aws_instance.web.private_ip
description = "The private IP address of the main server instance."
}
output "SSH_Connection" {
value = format("ssh connection to instance ${var.prefix}-vm ==> sudo ssh -i ~/.ssh/ec2_id_ed25519 ec2-user@%s",aws_instance.web.public_ip)
}

Section 6 — Start Provisioning

  1. Initialize terraform from the directory where you have all the terraform files stored
terraform init

2. Next let’s see what terraform is going to provision

terraform plan

3. Deploy the resources

terraform apply

Section 7 — Test your website.

If you have noted previously that we deployed akamai stuff in staging network i.e. not a production network. For testing your site on staging network you would have to spoof your network to point your web traffic to staging network.

Use below link to identify your staging ip and spoof your /etc/hosts file to test your newly deployed website.

Here is how my website looks like.

Section 8 — Code Repo.

If you made it till here then i must thank you and hopefully you would have learnt something new.

--

--