Ir al contenido

Dominio y certificado

Al final de este módulo producción sirve en tu subdominio, con HTTPS y un certificado que se renueva solo.

Con participante = "p01", el sitio queda en p01.workshop.v0x.site.

Objetivo: comprobar que el stack completo funciona antes de meter DNS en la ecuación.

Ventana de terminal
cd taskflow-p01-infra/envs/prod
cp terraform.tfvars.example terraform.tfvars

Cambia dos valores y deja base_domain vacío por ahora:

envs/prod/terraform.tfvars
participante = "p01"
deploy_role_name = "taskflow-p01-deploy-prod"
base_domain = ""

Inicializa con el mismo bucket de estado que dev, y aplica:

Ventana de terminal
cd ../..
make init ENV=prod STATE_BUCKET=taskflow-p01-tfstate-123456789012
make apply ENV=prod
make output ENV=prod

Qué deberías ver: otra site_url de CloudFront, distinta de la de dev. Ahora tienes dos entornos completamente separados: distinto bucket, distinta distribución, distinto rol y distinto estado.

Objetivo: decirle a Terraform cuál es tu subdominio y en qué zona de Route 53 vive.

La zona workshop.v0x.site ya existe en Route 53 (la creó el instructor). Con dns_mode = "route53", Terraform administra todo el DNS por ti: crea los registros de validación del certificado, espera a que ACM lo emita y crea el alias del subdominio.

envs/prod/terraform.tfvars
base_domain = "workshop.v0x.site"
dns_mode = "route53"
route53_zone_id = "Z0123456789ABCDEFGHIJ" # el instructor te lo da

Qué deberías ver: nada todavía, es solo configuración.

Objetivo: emitir el certificado, validarlo y asociarlo a la distribución, en un solo paso.

Ventana de terminal
make apply ENV=prod

Terraform crea el certificado ACM, escribe el registro CNAME de validación en la zona de Route 53, y se queda esperando en aws_acm_certificate_validation a que ACM compruebe el registro. Suele tardar entre dos y diez minutos. No tienes que crear ningún registro a mano: al estar la zona en Route 53, todo el flujo es automático.

Qué deberías ver: el apply termina; el certificado queda emitido y asociado a la distribución, y el alias de tu subdominio ya apunta a CloudFront.

Objetivo: confirmar que tu subdominio resuelve a CloudFront.

Ventana de terminal
make output ENV=prod

Verás site_url con tu subdominio (https://p01.workshop.v0x.site) y dns_target con el dominio de la distribución. El alias ya lo creó Terraform, así que el subdominio debe resolver en pocos minutos (propagación del cambio en Route 53).## Paso 7.7 · Comprobar

Objetivo: verificar HTTPS, cabeceras de seguridad y el fallback de rutas.

Ventana de terminal
curl -sI https://p01.workshop.v0x.site | head -12

Qué deberías ver:

  • HTTP/2 200
  • strict-transport-security presente
  • x-content-type-options: nosniff
  • una cabecera x-cache de CloudFront

Y en el navegador, recargar https://p01.workshop.v0x.site/tareas/<id> tiene que funcionar. Si da 403, el arreglo del paso 6.6 no está aplicado en producción: revisa que descomentaste los bloques en el módulo, que es compartido por los dos entornos, y que aplicaste ENV=prod.

Ver el stack de producción completo
envs/prod/main.tf
locals {
# Tu subdominio. Cada participante obtiene el suyo bajo el mismo dominio de
# prueba: p01.workshop.v0x.site, aperez.workshop.v0x.site, y así.
#
# Si base_domain está vacío, producción también cae al dominio por defecto de
# CloudFront. Sirve para probar el stack completo antes de tener DNS listo.
domain_name = var.base_domain == "" ? "" : "${var.participante}.${var.base_domain}"
}
module "hosting" {
source = "../../modules/spa-hosting"
providers = {
aws = aws
aws.us_east_1 = aws.us_east_1
}
project = var.project
participante = var.participante
environment = "prod"
domain_name = local.domain_name
dns_mode = var.dns_mode
route53_zone_id = var.route53_zone_id
price_class = var.price_class
tags = var.tags
}
# ---------------------------------------------------------------------------
# Permisos del rol de despliegue de producción
#
# Idéntico en forma al de dev, pero apuntando a otro bucket y otra distribución.
# Son dos roles distintos precisamente para que el pipeline de dev no pueda
# escribir en producción por error ni por descuido.
# ---------------------------------------------------------------------------
data "aws_iam_policy_document" "deploy" {
statement {
sid = "SincronizarSitio"
effect = "Allow"
actions = [
"s3:PutObject",
"s3:DeleteObject",
"s3:GetObject",
]
resources = ["${module.hosting.bucket_arn}/*"]
}
statement {
sid = "ListarBucket"
effect = "Allow"
actions = ["s3:ListBucket"]
resources = [module.hosting.bucket_arn]
}
statement {
sid = "InvalidarCache"
effect = "Allow"
actions = [
"cloudfront:CreateInvalidation",
"cloudfront:GetInvalidation",
]
resources = [module.hosting.distribution_arn]
}
}
resource "aws_iam_role_policy" "deploy" {
name = "${var.project}-${var.participante}-prod-deploy"
role = var.deploy_role_name
policy = data.aws_iam_policy_document.deploy.json
}

El subdominio se compone en un local, no se escribe a mano:

domain_name = var.base_domain == "" ? "" : "${var.participante}.${var.base_domain}"

Con base_domain vacío, producción también cae al dominio por defecto de CloudFront. Eso es lo que permitió el paso 7.1.

Con domain_name configurado, el módulo activa además HSTS. Es dinámico a propósito: el dominio por defecto de CloudFront es compartido entre muchos clientes, y fijarle una política HSTS afectaría a terceros. Con dominio propio, en cambio, tiene todo el sentido.

Y la renovación es automática mientras el registro de validación siga existiendo en el DNS. Nadie tiene que acordarse de renovar nada, que es el otro recordatorio de por qué salimos de nginx en una instancia.

Ventana de terminal
# producción sin dominio
cd envs/prod && cp terraform.tfvars.example terraform.tfvars # editar
cd ../.. && make init ENV=prod STATE_BUCKET=<bucket-de-estado>
make apply ENV=prod
# activar el dominio: en envs/prod/terraform.tfvars poner
# base_domain = "workshop.v0x.site"
# dns_mode = "route53"
# route53_zone_id = "<zona que te dio el instructor>"
make apply ENV=prod # crea validación + alias y espera a ACM
make output ENV=prod # site_url y dns_target
curl -sI https://p01.workshop.v0x.site | head -12

Ahora automatizamos el despliegue.