Architecture

One VPC with public and private subnets, two TLS stories, four Linux hosts — public trust on the ALB, private trust you install yourself.

On this page


Topology

VPC 10.42.0.0/16 · ap-southeast-2 · public + private subnets · IGW + NAT

1 · Public path · ACM on the ALB
Internet browser / curl
→
ALB :443 public subnet · ACM · demo.johna.kiwi
→
public-web private subnet · nginx :80
2a · Private path (default) · alb_acm · ACM private cert on internal ALB
Private CA ROOT · issues via ACM
→
internal ALB :443 private subnets · app.internal.johna.kiwi
→
private-web private subnet · nginx :80
2b · Private path (optional) · nginx_export · leaf on nginx
Private CA ROOT · IssueCertificate
→
Secrets Manager cert · chain · key
→
private-web nginx :443 · app.internal.johna.kiwi

Same hostname as 2a; mutually exclusive via private_tls_mode. No internal ALB in this mode.

3 · Client trust · CA PEM only (same for 2a and 2b)
CA PEM trust anchor
→
S3 trust/ca.pem
→
SSM TrustCA=intranet
→
client-managed private · CA installed · TLS OK
client-unmanaged private · no CA · TLS fail

Clients curl https://app.internal.johna.kiwi (private hosted zone → internal ALB or instance). ACM does not install client trust — only the CA PEM does.

Public TLS ends on the internet-facing ALB. Private TLS is either alb_acm (default: internal ALB + ACM private cert, HTTP backend) or nginx_export (leaf in Secrets Manager on nginx). Clients get only a CA PEM via S3 and SSM — never the leaf key.

Network tiers

Tier Contents Edge
Public subnets (×2 AZs) Public ALB, NAT Gateway IGW for inbound internet → public ALB
Private subnets (×2 AZs) Internal ALB (default private path), public-web, private-web, both clients Egress via single NAT (lab, not HA)
Host Subnet Role Expectation
Public ALB Public HTTPS edge + ACM public Internet reaches demo.johna.kiwi
Internal ALB Private HTTPS edge + ACM private (default) VPC reaches app.internal.johna.kiwi
public-web Private Public ALB HTTP target No public IP
private-web Private Internal ALB HTTP target (default) or nginx TLS (nginx_export) Private DNS
client-managed Private Tag TrustCA=intranet Private URL works without --cacert
client-unmanaged Private No trust tag Private URL fails (untrusted issuer)

Public path

AWS Certificate Manager

TLS terminates on the Application Load Balancer. ACM holds the public certificate; DNS validation writes CNAMEs into the existing johna.kiwi hosted zone. The target group speaks HTTP to public-web.

Piece Lab choice
Certificate ACM public, DNS-validated for demo.johna.kiwi
Listener HTTPS :443 → target group HTTP :80
Backend Amazon Linux 2023 + nginx in a private subnet
Edge ALB in public subnets; IGW for inbound
DNS Route 53 alias to the ALB

ACM public certificates are trusted by major browsers and operating systems by default — no client-side CA install.

Private path

AWS Private Certificate Authority

Terraform creates a ROOT AWS Private CA. Server TLS mode is selected with private_tls_mode in terraform.tfvars (mutually exclusive on the same hostname). The topology diagram above shows 2a (alb_acm, default) then 2b (nginx_export).

Default: alb_acm

TLS terminates on an internal ALB. ACM requests a private certificate from the lab Private CA and attaches it by ARN (same pattern as the public path). private-web speaks HTTP only — no leaf on the instance.

Piece Lab choice
CA ACM Private CA ROOT, RSA 2048, 7-day permanent deletion window
Leaf ACM private cert for app.internal.johna.kiwi (key stays in ACM)
Edge Internal ALB in private subnets; HTTPS :443 from VPC CIDR
Backend private-web nginx :80
DNS Private hosted zone alias → internal ALB

Optional: nginx_export

TLS terminates on nginx on private-web. Terraform issues the leaf via Private CA IssueCertificate, stores leaf + key in Secrets Manager, and SSM State Manager (tag LabRole=private-web) runs scripts/configure-private-web.sh.

Piece Lab choice
Material Secrets Manager JSON (certificate, chain, private_key)
Server config State Manager → nginx + /etc/nginx/ssl/
DNS Private hosted zone A → instance private IP

Managed vs unmanaged clients prove client trust either way. ACM does not install the CA on clients. Pipeline detail: Trust distribution.

Split-horizon DNS (public ACM on an internal name)

Not built in this lab. Architecture only — no Terraform stage or prove script. The lab private path uses Private CA instead.

When you want an ACM public certificate on a hostname that must not resolve on the internet, use Route 53 split-view DNS: a public and a private hosted zone with the same name.

Record Zone Purpose
ACM DNS validation CNAME Public ACM validators query public DNS
App A / alias (x.example.internal → internal ALB) Private VPC clients resolve the name; no public A required

Client trust uses Amazon’s public roots — no CA PEM install. That differs from this lab’s Private CA path (S3 + SSM).

Same zone name · public + private hosted zones · not built in this lab

Public zone · internet DNS · ACM validation only
ACM validators public DNS query
→
Public hosted zone example.internal
→
_token.x.example.internal → acm-validations.aws
no public A for x
Private zone · VPC DNS · service resolution
VPC client resolves x
→
Private hosted zone example.internal
→
internal ALB ACM public cert · x.example.internal
Client trust · Amazon public roots (no CA PEM install)
Client default OS / browser trust
→
This lab’s private path Private CA + S3/SSM CA install

ACM looks up validation CNAMEs in a publicly hosted zone only. A CNAME that exists solely in the private zone never issues a public ACM certificate.

Split-view DNS (Route 53): public zone proves domain control to ACM; private zone routes x.example.internal inside the VPC. Conceptual pattern — not a Terraform stage in this walkthrough.
Choose When
Split-horizon + public ACM Internal name, but clients should trust Amazon’s CA (no fleet CA)
Private CA (this lab) You own the issuer; only managed clients should trust

A validation CNAME that exists only in the private zone will not issue (ACM troubleshooting).

Trust vs leaf (one line)

Material Goes to Clients
CA PEM S3 → SSM → system trust Managed only
Leaf (alb_acm) ACM → internal ALB Never
Leaf + key (nginx_export) Secrets Manager → State Manager → private-web Never

Do not put CA or leaf private keys on clients. Full do/don’t and rotation: Trust distribution.

Names and Region

Item Default
Region ap-southeast-2 (ACM public cert must match the ALB Region)
VPC CIDR 10.42.0.0/16 (public /24s + private /24s, IGW + NAT)
Public hostname demo.johna.kiwi
Private hostname app.internal.johna.kiwi
Private TLS mode alb_acm (or nginx_export)
Public hosted zone Same-account zone (route53_zone_id) — ACM validation + ALB alias
Private hosted zone Created in stage 03, associated with the lab VPC
Name prefix acm-lab

Override in terraform/terraform.tfvars.

Where next

Topic Page
S3 + SSM install pipeline Trust distribution
Apply, prove, destroy Walkthrough
Trust models / DNS validation Concepts
When to use split-horizon vs Private CA Usage guide
Pricing / renewal Cost

Next: Trust distribution.


Back to top

Lab repository. Tear down the Private CA when finished — it bills monthly.

ACM + Private CA lab docs