delegate dap.ps domain management to Route53

We have to use Route53 because it provides the option to use an
ALIAS type record which works for apex records and can point to
a CloudFront distribution in a dynamic way.

Without this we'd have to use A records which are static unlike
a CNAME, which would eventually become obsolete and take down
the site when they do.

Details: https://github.com/dap-ps/infra-dapps/issues/18

Signed-off-by: Jakub Sokołowski <jakub@status.im>
This commit is contained in:
Jakub Sokołowski 2020-01-27 20:38:42 +01:00
parent 9a207dddbc
commit f294580e31
No known key found for this signature in database
GPG Key ID: 4EF064D0E6D63020
14 changed files with 155 additions and 138 deletions

View File

@ -8,12 +8,6 @@ endif
PLUGIN_DIR = ~/.terraform.d/plugins
GANDI_PROVIDER_NAME = terraform-provider-gandi
GANDI_PROVIDER_VERSION = 1.1.0
GANDI_PROVIDER_ARCHIVE = $(GANDI_PROVIDER_NAME)-v$(GANDI_PROVIDER_VERSION).zip
GANDI_PROVIDER_URL = https://github.com/tiramiseb/terraform-provider-gandi/archive/v$(GANDI_PROVIDER_VERSION).zip
GANDI_PROVIDER_PATH = $(PLUGIN_DIR)/$(ARCH)/$(GANDI_PROVIDER_NAME)_v$(GANDI_PROVIDER_VERSION)
ANSIBLE_PROVIDER_NAME = terraform-provider-ansible
ANSIBLE_PROVIDER_VERSION = v1.0.3
ANSIBLE_PROVIDER_ARCHIVE = $(ANSIBLE_PROVIDER_NAME)-$(ARCH).zip
@ -29,7 +23,7 @@ ANSIBLE_PROVISIO_PATH = $(PLUGIN_DIR)/$(ARCH)/$(ANSIBLE_PROVISIO_NAME)_$(ANSIBLE
all: requirements plugins secrets init-terraform
@echo "Success!"
plugins: install-ansible-provider install-gandi-provider install-ansible-provisioner
plugins: install-ansible-provider install-ansible-provisioner
requirements:
ansible-galaxy install --ignore-errors --force -r ansible/requirements.yml
@ -48,19 +42,6 @@ install-ansible-provider: check-unzip
echo "Already installed: $(ANSIBLE_PROVIDER_PATH)"; \
fi
install-gandi-provider: check-unzip
@if [ ! -e $(GANDI_PROVIDER_PATH) ]; then \
mkdir -p $(PLUGIN_DIR); \
wget $(GANDI_PROVIDER_URL) -O /tmp/$(GANDI_PROVIDER_ARCHIVE); \
unzip -o /tmp/$(GANDI_PROVIDER_ARCHIVE) -d /tmp/; \
cd /tmp/$(GANDI_PROVIDER_NAME)-$(GANDI_PROVIDER_VERSION) && \
go build -o terraform-provider-gandi; \
mv /tmp/$(GANDI_PROVIDER_NAME)-$(GANDI_PROVIDER_VERSION)/terraform-provider-gandi \
$(PLUGIN_DIR)/$(ARCH)/$(GANDI_PROVIDER_NAME)_v$(GANDI_PROVIDER_VERSION); \
else \
echo "Already installed: $(GANDI_PROVIDER_PATH)"; \
fi
install-ansible-provisioner:
@if [ ! -e $(ANSIBLE_PROVISIO_PATH) ]; then \
mkdir -p $(PLUGIN_DIR); \
@ -79,7 +60,6 @@ secrets:
# secrets extracted from password-store\n\
aws_access_key = \"$(shell pass cloud/AWS/access-key)\"\n\
aws_secret_key = \"$(shell pass cloud/AWS/secret-key)\"\n\
gandi_api_token = \"$(shell pass cloud/Gandi/api-token)\"\n\
dap_ps_smtp_user = \"$(shell pass cloud/AWS/ses/smtp-access-key)\"\n\
dap_ps_smtp_pass = \"$(shell pass cloud/AWS/ses/smtp-password)\"\n\
dap_ps_admin_user = \"$(shell pass service/dev/app/admin-user)\"\n\

View File

@ -20,11 +20,12 @@ The infrastructure is hosted on AWS and consists of 5 main elements:
* [__EC2__](https://aws.amazon.com/ec2/) - [MongoDB](https://www.mongodb.com/) cluster
* [__S3__](https://aws.amazon.com/s3/) - [MongoDB](https://www.mongodb.com/) backups & [Terraform](https://www.terraform.io/) state
* [__SES__](https://aws.amazon.com/ses/) - Mail forwarding
* [__CF__](https://aws.amazon.com/cloudfront/) - CDN
* [__CF__](https://aws.amazon.com/cloudfront/) - [CDN](https://en.wikipedia.org/wiki/Content_delivery_network)
* [__R53__](https://aws.amazon.com/route53/) - Route53 DNS
All the AWS parts are provisioned and managed with [Terraform](https://www.terraform.io/) and the MongoDB cluster configured with [Ansible](https://www.ansible.com/).
The only part that is not AWS is [Gandi](https://www.gandi.net/) DNS provider due to AWS [Route53](https://aws.amazon.com/route53/) not supporting the `.ps` [TLD](https://en.wikipedia.org/wiki/Top-level_domain).
The `dap.ps` domain is registered via [Gandi](https://www.gandi.net/) DNS provider and is managed with AWS [Route53](https://aws.amazon.com/route53/) [Hosted Zone](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/hosted-zones-working-with.html) by changing the Name Servers with help from Gandi support. See `dns.tf` for more details.
# Usage

51
dev.tf
View File

@ -34,7 +34,9 @@ module "dev_cert" {
stage = "dev"
domain = "dap.ps"
sans = ["dap.ps", "raw.dev.dap.ps"]
zone_id = gandi_zone.dap_ps_zone.id
zone_id = aws_route53_zone.dap_ps.zone_id
route53_zone_id = aws_route53_zone.dap_ps.zone_id
}
module "dev_db_bucket" {
@ -58,8 +60,8 @@ module "dev_db" {
subnet_id = module.dev_env.subnet_ids[0]
sec_group = module.dev_env.security_group_id
/* Plumbing */
keypair_name = aws_key_pair.admin.key_name
gandi_zone_id = gandi_zone.dap_ps_zone.id
keypair_name = aws_key_pair.admin.key_name
route53_zone_id = aws_route53_zone.dap_ps.zone_id
}
module "dev_env" {
@ -89,21 +91,34 @@ module "dev_cdn" {
origin_fqdns = module.dev_env.elb_fqdns
}
/* DNS ------------------------------------------*/
resource "gandi_zonerecord" "dev_dns" {
zone = gandi_zone.dap_ps_zone.id
name = "dev"
type = "CNAME"
ttl = 3600
values = ["${module.dev_cdn.cf_domain_name}."]
}
/* AWS DNS --------------------------------------*/
/* raw subdomain for access without CDN */
resource "gandi_zonerecord" "dev_dns_raw" {
zone = gandi_zone.dap_ps_zone.id
name = "raw.dev"
type = "CNAME"
ttl = 3600
values = [for elb in module.dev_env.elb_fqdns: "${elb}."]
resource "aws_route53_record" "dev_dns_raw" {
zone_id = aws_route53_zone.dap_ps.zone_id
name = "raw.dev"
type = "CNAME"
ttl = 3600
records = [for elb in module.dev_env.elb_fqdns: "${elb}."]
}
resource "aws_route53_record" "dev_dns_cdn" {
zone_id = aws_route53_zone.dap_ps.zone_id
name = "cdn.dev"
type = "CNAME"
ttl = 3600
records = ["${module.dev_cdn.cf_domain_name}."]
}
resource "aws_route53_record" "dev_dns" {
zone_id = aws_route53_zone.dap_ps.zone_id
name = "dev"
type = "CNAME"
alias {
name = aws_route53_record.dev_dns_cdn.fqdn
zone_id = aws_route53_record.dev_dns_cdn.zone_id
evaluate_target_health = false
}
}

13
dns.tf Normal file
View File

@ -0,0 +1,13 @@
/* DNS ------------------------------------*/
/** WARNING: The .ps TLD has special status.
* Its registration took extra long time when it was purchased.
* It required help from Gandi support to change the nameservers to AWS ones.
* Make changes to it on Gandi side with that in mind.
**/
/* Configure managing domain with Route53 Hosted Zone */
resource "aws_route53_zone" "dap_ps" {
name = "dap.ps"
comment = "Domain for Dapp Discovery website."
}

15
main.tf
View File

@ -8,10 +8,6 @@ provider "aws" {
secret_key = var.aws_secret_key
}
provider "gandi" {
key = var.gandi_api_token
}
/* DATA -----------------------------------------*/
terraform {
@ -44,17 +40,6 @@ resource "aws_s3_bucket" "tf-state" {
}
}
/* Gandi DNS ------------------------------------*/
resource "gandi_zone" "dap_ps_zone" {
name = "${var.public_domain} zone"
}
resource "gandi_domainattachment" "dap_ps" {
domain = var.public_domain
zone = gandi_zone.dap_ps_zone.id
}
/* ACCESS ---------------------------------------*/
resource "aws_key_pair" "admin" {

View File

@ -9,19 +9,19 @@ resource "aws_acm_certificate" "main" {
}
}
resource "gandi_zonerecord" "cert_verification" {
zone = var.zone_id
name = replace(aws_acm_certificate.main.domain_validation_options[count.index].resource_record_name, ".${var.domain}.", "")
type = aws_acm_certificate.main.domain_validation_options[count.index].resource_record_type
ttl = 300
values = [aws_acm_certificate.main.domain_validation_options[count.index].resource_record_value]
count = length(var.sans)+1
resource "aws_route53_record" "cert_verification" {
zone_id = var.zone_id
name = replace(aws_acm_certificate.main.domain_validation_options[count.index].resource_record_name, ".${var.domain}.", "")
type = aws_acm_certificate.main.domain_validation_options[count.index].resource_record_type
ttl = 300
records = [aws_acm_certificate.main.domain_validation_options[count.index].resource_record_value]
count = length(var.sans)+1
}
resource "aws_acm_certificate_validation" "main" {
certificate_arn = aws_acm_certificate.main.arn
validation_record_fqdns = [
for verification in gandi_zonerecord.cert_verification:
for verification in aws_route53_record.cert_verification:
"${verification.name}.${var.domain}"
]
}

View File

@ -9,7 +9,7 @@ variable "domain" {
}
variable "zone_id" {
description = "ID of the zone in Gandi DNS registrar."
description = "ID of the zone in AWS Route53."
type = string
}
@ -18,3 +18,8 @@ variable "sans" {
type = list(string)
default = []
}
variable "route53_zone_id" {
description = "ID of the zone in AWS Route53."
type = string
}

View File

@ -3,6 +3,11 @@ output "cf_id" {
description = "ID of AWS CloudFront distribution"
}
output "cf_zone_id" {
value = aws_cloudfront_distribution.default.hosted_zone_id
description = "Zone ID of AWS CloudFront distribution"
}
output "cf_arn" {
value = aws_cloudfront_distribution.default.arn
description = "ARN of AWS CloudFront distribution"

View File

@ -96,15 +96,16 @@ resource "aws_instance" "main" {
}
}
resource "gandi_zonerecord" "main" {
zone = var.gandi_zone_id
name = "${aws_instance.main[count.index].tags.Name}.${var.subdomain}"
type = "A"
ttl = 600
values = [aws_instance.main[count.index].public_ip]
count = length(aws_instance.main)
resource "aws_route53_record" "main" {
zone_id = var.route53_zone_id
name = "${aws_instance.main[count.index].tags.Name}.${var.subdomain}"
type = "A"
ttl = 600
records = [aws_instance.main[count.index].public_ip]
count = length(aws_instance.main)
}
/* this adds the host to the Terraform state for Ansible inventory */
resource "ansible_host" "main" {
inventory_hostname = aws_instance.main[count.index].tags.Name

View File

@ -65,8 +65,8 @@ variable "sec_group" {
/* DNS ------------------------------------------*/
variable "gandi_zone_id" {
description = "ID of the zone in Gandi DNS registrar."
variable "route53_zone_id" {
description = "ID of the zone in AWS Route53."
type = string
}

72
prod.tf
View File

@ -38,7 +38,9 @@ module "prod_cert" {
stage = "prod"
domain = "dap.ps"
sans = ["dap.ps", "raw.prod.dap.ps"]
zone_id = gandi_zone.dap_ps_zone.id
zone_id = aws_route53_zone.dap_ps.zone_id
route53_zone_id = aws_route53_zone.dap_ps.zone_id
}
module "prod_db_bucket" {
@ -63,8 +65,8 @@ module "prod_db" {
sec_group = module.prod_env.security_group_id
/* Plumbing */
keypair_name = aws_key_pair.admin.key_name
gandi_zone_id = gandi_zone.dap_ps_zone.id
keypair_name = aws_key_pair.admin.key_name
route53_zone_id = aws_route53_zone.dap_ps.zone_id
}
module "prod_env" {
@ -94,34 +96,50 @@ module "prod_cdn" {
origin_fqdns = module.prod_env.elb_fqdns
}
/* DNS ------------------------------------------*/
resource "gandi_zonerecord" "prod_dns" {
zone = gandi_zone.dap_ps_zone.id
name = "prod"
type = "CNAME"
ttl = 3600
values = ["${module.prod_cdn.cf_domain_name}."]
}
/* AWS DNS --------------------------------------*/
/* raw subdomain for access without CDN */
resource "gandi_zonerecord" "prod_dns_raw" {
zone = gandi_zone.dap_ps_zone.id
name = "raw.prod"
type = "CNAME"
ttl = 3600
values = [for elb in module.prod_env.elb_fqdns: "${elb}."]
resource "aws_route53_record" "prod_dns_raw" {
zone_id = aws_route53_zone.dap_ps.zone_id
name = "raw.prod"
type = "CNAME"
ttl = 3600
records = [for elb in module.prod_env.elb_fqdns: "${elb}."]
}
/* Apex DNS records cannot be CNAMEs */
data "dns_a_record_set" "prod_cdn" {
host = module.prod_cdn.cf_domain_name
resource "aws_route53_record" "prod_dns_cdn" {
zone_id = aws_route53_zone.dap_ps.zone_id
name = "cdn.prod"
type = "CNAME"
ttl = 3600
records = ["${module.prod_cdn.cf_domain_name}."]
}
resource "gandi_zonerecord" "prod_apex_dns" {
zone = gandi_zone.dap_ps_zone.id
name = "@"
type = "A"
ttl = 3600
values = data.dns_a_record_set.prod_cdn.addrs
resource "aws_route53_record" "prod_dns" {
zone_id = aws_route53_zone.dap_ps.zone_id
name = "prod"
type = "CNAME"
alias {
name = aws_route53_record.prod_dns_cdn.fqdn
zone_id = aws_route53_record.prod_dns_cdn.zone_id
evaluate_target_health = false
}
}
/* Apex DNS Record ------------------------------*/
/* WARNING: Main site record for https://dap.ps/ */
resource "aws_route53_record" "prod_dns_apex" {
zone_id = aws_route53_zone.dap_ps.zone_id
name = ""
type = "A"
alias {
name = module.prod_cdn.cf_domain_name
zone_id = module.prod_cdn.cf_zone_id
evaluate_target_health = false
}
}

63
ses.tf
View File

@ -13,47 +13,47 @@ resource "aws_ses_domain_mail_from" "dap_ps" {
mail_from_domain = "mail.${aws_ses_domain_identity.dap_ps.domain}"
}
resource "gandi_zonerecord" "dap_ps_verification" {
zone = gandi_zone.dap_ps_zone.id
name = "_amazonses"
type = "TXT"
ttl = 3600
values = ["\"${aws_ses_domain_identity.dap_ps.verification_token}\""]
resource "aws_route53_record" "dap_ps_verification" {
zone_id = aws_route53_zone.dap_ps.zone_id
name = "_amazonses"
type = "TXT"
ttl = 3600
records = ["${aws_ses_domain_identity.dap_ps.verification_token}"]
}
resource "gandi_zonerecord" "dap_ps_mail_mx" {
zone = gandi_zone.dap_ps_zone.id
name = "mail"
type = "MX"
ttl = 3600
values = ["10 feedback-smtp.us-east-1.amazonses.com."]
resource "aws_route53_record" "dap_ps_mail_mx" {
zone_id = aws_route53_zone.dap_ps.zone_id
name = "mail"
type = "MX"
ttl = 3600
records = ["10 feedback-smtp.us-east-1.amazonses.com."]
}
resource "gandi_zonerecord" "dap_ps_mail_spf" {
zone = gandi_zone.dap_ps_zone.id
name = "mail"
type = "TXT"
ttl = 3600
values = ["\"v= spf1 include:amazonses.com ~all\""]
resource "aws_route53_record" "dap_ps_mail_spf" {
zone_id = aws_route53_zone.dap_ps.zone_id
name = "mail"
type = "TXT"
ttl = 3600
records = ["v= spf1 include:amazonses.com ~all"]
}
resource "gandi_zonerecord" "dap_ps_dkim" {
zone = gandi_zone.dap_ps_zone.id
ttl = 3600
type = "CNAME"
count = 3
name = "${element(aws_ses_domain_dkim.dap_ps.dkim_tokens, count.index)}._domainkey"
values = ["${element(aws_ses_domain_dkim.dap_ps.dkim_tokens, count.index)}.dkim.amazonses.com."]
resource "aws_route53_record" "dap_ps_dkim" {
zone_id = aws_route53_zone.dap_ps.zone_id
ttl = 3600
type = "CNAME"
count = 3
name = "${element(aws_ses_domain_dkim.dap_ps.dkim_tokens, count.index)}._domainkey"
records = ["${element(aws_ses_domain_dkim.dap_ps.dkim_tokens, count.index)}.dkim.amazonses.com."]
}
/* SES EMail Fowarding --------------------------*/
resource "gandi_zonerecord" "dap_ps_mx" {
zone = gandi_zone.dap_ps_zone.id
name = "@"
type = "MX"
ttl = 3600
values = ["10 inbound-smtp.us-east-1.amazonaws.com."]
resource "aws_route53_record" "dap_ps_mx" {
zone_id = aws_route53_zone.dap_ps.zone_id
name = "@"
type = "MX"
ttl = 3600
records = ["10 inbound-smtp.us-east-1.amazonaws.com."]
}
/* Validated Emails -----------------------------*/
@ -65,4 +65,3 @@ resource "aws_ses_email_identity" "jakub" {
resource "aws_ses_email_identity" "andy" {
email = "andy@status.im"
}

View File

@ -8,10 +8,6 @@ variable "aws_secret_key" {
description = "Secret key for the AWS API."
}
variable "gandi_api_token" {
description = "API token for Gandi DNS API"
}
/* GENERAL --------------------------------------*/
variable "hosts_subdomain" {

View File

@ -3,6 +3,5 @@ terraform {
required_version = ">= 0.12"
required_providers {
aws = "= 2.44.0"
gandi = ">= 1.1.0"
}
}