Skip to content

Provision the minimum HTTPS-origin scaffold runtime on ECS and RDS #68

Description

@alexeygrigorev

Parent epic: #9

Normative spec: 08 — Runtime topology
Foundation dependency and reviewed expansion: #67

Runtime integration correction

The #67 foundation used an HTTP custom origin from CloudFront to the ALB. An ALB receiving that request sets X-Forwarded-Proto: http; deployed Django correctly trusts only X-Forwarded-Proto: https while SECURE_SSL_REDIRECT=True. The result would be a redirect/readiness loop even though the viewer connected to CloudFront over HTTPS.

Selected safe solution A is end-to-end edge/origin TLS. This issue owns the reviewed expansion of #67's network/DNS count:

  • a workload-only origin.web.dtcdev.click hostname;
  • an ALB origin certificate in eu-west-1, with DNS validation in exact zone Z05963572WVWFHDQZH5NE;
  • origin.web.dtcdev.click A/AAAA aliases to the ALB;
  • a TLS 1.2 HTTPS ALB listener;
  • CloudFront connecting to origin.web.dtcdev.click with https-only.

web.dtcdev.click remains the only public/canonical website hostname. The origin hostname is operational plumbing, is always non-indexable, is not accepted as a public application canonical, and never appears in a sitemap. Django's proxy trust and SECURE_SSL_REDIRECT remain unchanged; this issue fixes the transport rather than trusting a spoofable header or disabling redirects.

Scope

Extend the DataTalksClub/aws-infra website stack with the minimum production-shaped runtime required by the #1 scaffold and the HTTPS-origin correction above.

HTTPS origin and DNS

  • Add an explicit environment-owned origin_hostname input; sandbox supplies origin.web.dtcdev.click.
  • Create the origin ACM certificate with the primary eu-west-1 provider, validate it through records in exact hosted zone Z05963572WVWFHDQZH5NE, and add only the origin A/AAAA aliases to the workload ALB.
  • Change CloudFront's custom origin domain to origin.web.dtcdev.click, require https-only, and require TLS 1.2 to the ALB.
  • Restrict ALB ingress to the inventoried AWS-managed CloudFront origin-facing prefix list on HTTPS only.
  • Use an HTTPS listener with the origin certificate. Its default action denies access; forwarding requires both the expected public host web.dtcdev.click and the generated secret origin-verification header supplied only by this CloudFront distribution.
  • Do not export, log, or commit the origin-verification value. A request lacking either the CloudFront-network control or the secret/host rule must not reach Django.
  • Keep the public web.dtcdev.click A/AAAA aliases pointing to CloudFront and keep all emitted application canonicals on https://datatalks.club/... as defined by Bootstrap the runnable Django/uv foundation and engineering workflow #1.
  • Treat origin.web.dtcdev.click as operational/noindex only: it is absent from sitemaps/navigation/canonicals and is not added as a normal Django allowed public host.

Minimum scaffold runtime

  • Create an ECR repository with scan-on-push and a bounded lifecycle policy.
  • Create an ECS Fargate cluster and task definitions/services for separate Django web and Django-Q2 worker processes using the same immutable image digest.
  • Create a separate one-off migration task definition; web and worker entrypoints never apply migrations.
  • Run exactly one sandbox worker service instance so the scaffold has one effective Django-Q2 scheduler owner until later job primitives are implemented.
  • Register only the web service with the existing target group and preserve /health/live for ALB liveness plus /health/ready for dependency-aware rollout/readiness verification.
  • Provision a small encrypted, non-public PostgreSQL RDS instance in the isolated subnets with automated backups, storage autoscaling, and explicit final-snapshot/deletion behavior.
  • Create Secrets Manager containers and least-privilege task execution/application roles for current bootstrap values; values are injected out of band.
  • Create CloudWatch log groups with bounded retention/encryption and the minimum ECS/ALB/RDS service-health alarms required for the scaffold.
  • Expose production-account inputs for private tasks, Multi-AZ, stronger deletion protection, longer backup retention, scaling, sizing, public hostname, and origin hostname without sandbox coupling.

Non-goals

  • Do not disable SECURE_SSL_REDIRECT, weaken SECURE_PROXY_SSL_HEADER, trust arbitrary viewer-supplied forwarding headers, add an application exception that treats external HTTP as HTTPS, or add origin.web.dtcdev.click as a public canonical.
  • Do not expose the ALB as an alternate website, forward based on origin hostname alone, place the origin hostname in navigation/sitemaps, or permit direct origin access without both network and secret/host controls.
  • No shared/wildcard certificate or DNS ownership beyond the workload's explicit web.dtcdev.click, origin.web.dtcdev.click, and their certificate-validation records. Never discover a hosted zone by name or create a zone.
  • No SES identity/configuration set/provider events, recipient safeguards, email/outbox alarms, GitHub content assets, S3 content/export buckets, content freshness alarms, advanced tracing, domain dashboards, or restore drill. Those belong to Build the durable Relay delivery intent and status lifecycle #49, Add observability, service targets, backup/restore, and failure runbooks #66, and domain issues.
  • No application deployment workflow; Deploy scaffold images through GitHub OIDC and immutable ECS releases #69 owns it.
  • No secret values in Terraform/Git, migration race in web/worker startup, wildcard IAM, public RDS/task ports, broad egress ownership, or production-sized sandbox defaults.

Acceptance criteria

  • Terraform explicitly creates/validates an origin.web.dtcdev.click certificate in eu-west-1 and its A/AAAA ALB aliases in hosted zone Z05963572WVWFHDQZH5NE; it creates or discovers no hosted zone and touches no unrelated DNS.
  • The reviewed plan identifies the expansion from Create the portable sandbox network, DNS, edge, and TLS foundation #67's three DNS records: only the origin certificate-validation record and origin A/AAAA aliases are added, subject to ACM's exact generated validation record count.
  • CloudFront uses origin.web.dtcdev.click with https-only and TLS 1.2; the ALB accepts CloudFront-origin HTTPS, presents the matching origin certificate, and no longer relies on an HTTP CloudFront→ALB hop.
  • The ALB security group allows the explicit CloudFront managed prefix list on HTTPS only. The listener defaults to denial and forwards only when both public host web.dtcdev.click and the generated secret origin header match.
  • Source/plan guarantee: Terraform requires the CloudFront-origin network source, generated secret header, and expected public host before forwarding, so a request missing any control cannot reach Django; the secret is sensitive state only and is absent from outputs, logs, committed plans, task configuration, and issue/test artifacts. Deploy and validate the scaffold at web.dtcdev.click #70 must exercise every direct-origin denial combination live before deployment acceptance.
  • Source/settings guarantee: CloudFront uses HTTPS to the ALB and the ALB supplies forwarded HTTPS state to Django; deployment settings retain SECURE_SSL_REDIRECT=True and the exact existing SECURE_PROXY_SSL_HEADER, with only exact /health/ready exempted for the private direct target probe. Regression tests prove terminal forwarded-HTTPS responses plus preserved non-health redirects and host rejection; Deploy and validate the scaffold at web.dtcdev.click #70 must verify public /, /health/live, and /health/ready have no live redirect loop.
  • Source/configuration guarantee: web.dtcdev.click remains the public and allowed application endpoint, production-equivalent pages retain https://datatalks.club/... canonicals, and origin.web.dtcdev.click is operational/noindex only and absent from sitemap, navigation, canonical, and normal Django host configuration. Deploy and validate the scaffold at web.dtcdev.click #70 must probe and crawl the deployed public and origin paths live before deployment acceptance.
  • ECR uses scan-on-push and lifecycle rules; task definitions accept one immutable image digest for both web and worker.
  • Web and worker are separate ECS services, and migrations run only through a separate one-off task before service rollout.
  • Source/plan guarantee: sandbox worker desired count is one and its dedicated 0/100 deployment configuration plus singleton precondition prevents overlap above desired count; scheduler/lease enhancements remain deferred to Add shared service, configuration, operation, and durable-job primitives #31. Deploy and validate the scaffold at web.dtcdev.click #70 must verify one stable running worker and exercise replacement while proving running plus pending worker tasks never exceed one.
  • Source/test/plan guarantee: container liveness uses /health/live; the ALB sends direct HTTP to exact /health/ready and accepts only 200; regression tests return dependency-aware 200 ready or sanitized 503 not_ready without redirect. Deploy and validate the scaffold at web.dtcdev.click #70 must exercise readiness live before and after migrations and during database failure, proving failed readiness blocks a bad revision.
  • RDS PostgreSQL is encrypted, non-public, task-SG-only, backed up, storage-autoscaling, and has explicit snapshot/deletion settings.
  • Secrets Manager containers and task roles are least privilege; no secret values, state, saved plans, backend credentials, or real tfvars enter Git.
  • CloudWatch logs have explicit retention/encryption and basic alarms cover web/worker desired-vs-running state, ALB target health/5xx, and RDS availability/storage.
  • Production variables enable a distinct origin hostname/certificate, private tasks, Multi-AZ, stronger protection/retention, and scaling without sandbox state/resource dependencies.
  • Terraform format/validate/static-policy checks and reviewed sandbox/production-fixture plans pass with no unrelated shared-resource mutation. Policies cover HTTPS origin, explicit DNS ownership, direct-origin denial, public RDS/task ingress, encryption, wildcard IAM, and committed secrets.

Test scenarios

  1. Plan the expanded sandbox root and enumerate every DNS/certificate/listener/origin change. Confirm exact delegated zone use; no zone discovery/creation; existing public records remain CloudFront aliases; new origin aliases target only the ALB.
  2. Validate the origin certificate is in eu-west-1, CloudFront viewer certificate remains in us-east-1, CloudFront origin policy is https-only/TLS 1.2, and the ALB listener uses the matching origin certificate.
  3. Through https://web.dtcdev.click, request /, /health/live, and /health/ready; verify a terminal expected response with no redirect chain/loop and confirm Django observes forwarded HTTPS without any proxy-setting change.
  4. Exercise origin denial without exposing the secret: ordinary internet→origin is blocked by the SG; a permitted-network test without the secret is denied; correct-header/wrong-host and correct-host/missing-header are denied; only the configured CloudFront origin path forwards.
  5. Request or crawl the operational origin hostname and prove it cannot serve a separately indexable/canonical site: direct access is denied, no sitemap/navigation links reference it, and no application canonical points to it.
  6. Confirm the generated origin secret is marked sensitive and absent from Terraform outputs, application environment, logs, committed artifacts, and test reports.
  7. Inspect IAM and plan ownership for exact ECR/ECS/RDS/Secrets/Logs resources and reject wildcard/unrelated permissions.
  8. Run migration successfully, then deploy web and worker from one digest; make migration fail and verify services do not roll forward.
  9. Verify web/worker processes are non-root, separate, receive only required secrets, and exactly one worker is desired.
  10. Confirm target liveness and public dependency-aware readiness before/after migrations and with database unavailable; no case creates a redirect loop.
  11. Stop/replace each task and verify ECS recovery plus basic alarms. Verify encrypted automated backups and final-snapshot/deletion configuration; full restore/RPO/RTO remains in Add observability, service targets, backup/restore, and failure runbooks #66.
  12. Plan the production fixture with a different public/origin domain, account, region, hosted zone, network/private-task/NAT inputs, and backend without sandbox references.

No product UI is implemented here. Deployed browser/header/redirect verification is completed by #70; #68 must still provide infrastructure-level HTTPS-origin and denial evidence before handoff.

Dependencies

Metadata

Metadata

Assignees

No one assigned

    Labels

    P0Must-have or release-blockinginfraArea: infraoperationsArea: operations

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions