← back to NationalPaperHangers

dns/cloudflare-zone.yaml

140 lines

# cloudflare-zone.yaml
# Declarative Cloudflare zone configuration for nationalpaperhangers.com
#
# BEFORE YOU RUN apply-cloudflare-zone.sh:
#
# 1. Mint a NEW Cloudflare API token with Zone:Edit permission.
#    The current token in secrets-manager is Zone:DNS:Edit only —
#    it cannot add the domain to Cloudflare (zone creation requires Zone:Edit).
#
#    EXACT CLICK-PATH (8 clicks from logged-in state):
#    ─────────────────────────────────────────────────
#    1. dash.cloudflare.com → top-right avatar → "My Profile"
#    2. Left sidebar → "API Tokens"
#    3. Button: "Create Token"
#    4. Select: "Custom token" (bottom of template list) → "Get started"
#    5. Token name: "nationalpaperhangers-zone-edit"
#    6. Permissions row 1: Zone | Zone | Edit
#       (click "+ Add more" to add a second permission row)
#       Permissions row 2: Zone | DNS | Edit
#    7. Zone Resources: Include | Specific zone | nationalpaperhangers.com
#       (If the domain is not yet in CF, set to "All zones" for this one run,
#        then rotate to a scoped token afterward.)
#    8. (Optional but recommended) Client IP Address Filtering:
#       Add 45.61.58.125 (Kamatera) and your Mac2 egress IP.
#    9. Click "Continue to summary" → "Create Token"
#    10. Copy token immediately — shown ONCE.
#    Store via: node ~/Projects/secrets-manager/cli.js add CF_ZONE_EDIT_TOKEN_NPH <value>
#
# 2. Set CF_API_TOKEN in your shell:
#    export CF_API_TOKEN=$(node ~/Projects/secrets-manager/cli.js get CF_ZONE_EDIT_TOKEN_NPH)
#
# 3. Run: bash dns/apply-cloudflare-zone.sh          (dry-run, prints API calls)
#         bash dns/apply-cloudflare-zone.sh --commit  (executes)
#
# ─────────────────────────────────────────────────────────────────────────────
# Email provider: Migadu Mini ($90/yr, unlimited domains)
# DKIM-VERIFY-TOKEN: obtain from Migadu domain setup wizard and replace below
# ─────────────────────────────────────────────────────────────────────────────

zone:
  name: nationalpaperhangers.com
  # Cloudflare account ID — find at dash.cloudflare.com (right sidebar on overview)
  account_id: "REPLACE_WITH_YOUR_CF_ACCOUNT_ID"
  # SSL mode applied once origin cert (Let's Encrypt via certbot-dns-cloudflare) is live
  ssl_mode: full_strict
  # Always redirect HTTP → HTTPS (Cloudflare edge setting)
  always_use_https: true
  # Minimum TLS version
  min_tls_version: "1.2"

records:
  # ─── WEB — apex + www → Kamatera (PROXIED) ────────────────────────────────
  - type: A
    name: "@"
    content: "45.61.58.125"
    proxied: true
    ttl: 1          # 1 = "Auto" in CF UI (required when proxied: true)
    comment: "Apex → Kamatera production server (Cloudflare proxied)"

  - type: A
    name: "www"
    content: "45.61.58.125"
    proxied: true
    ttl: 1
    comment: "www → Kamatera production server (Cloudflare proxied)"

  # ─── MX — Migadu inbound mail servers ─────────────────────────────────────
  # MX records must NOT be proxied (Cloudflare does not proxy mail traffic)
  - type: MX
    name: "@"
    content: "aspmx1.migadu.com"
    priority: 10
    proxied: false
    ttl: 3600
    comment: "Migadu primary MX"

  - type: MX
    name: "@"
    content: "aspmx2.migadu.com"
    priority: 20
    proxied: false
    ttl: 3600
    comment: "Migadu secondary MX"

  # ─── SPF — authorize Migadu to send on behalf of this domain ──────────────
  # -all = hard fail any sender not listed (correct for Migadu; switch from ~all
  # only after confirming DKIM is working and no legitimate mail is failing SPF)
  - type: TXT
    name: "@"
    content: "v=spf1 include:spf.migadu.com -all"
    proxied: false
    ttl: 3600
    comment: "SPF — Migadu authorized sender; hard fail all others"

  # ─── DKIM — 3-key CNAME delegation (Migadu pattern) ──────────────────────
  # These CNAMEs are static; Migadu manages the underlying key rotation.
  # CF MUST NOT proxy CNAME records used for DKIM (set proxied: false).
  - type: CNAME
    name: "key1._domainkey"
    content: "key1.nationalpaperhangers.com._domainkey.migadu.com"
    proxied: false
    ttl: 3600
    comment: "DKIM key1 delegate → Migadu"

  - type: CNAME
    name: "key2._domainkey"
    content: "key2.nationalpaperhangers.com._domainkey.migadu.com"
    proxied: false
    ttl: 3600
    comment: "DKIM key2 delegate → Migadu"

  - type: CNAME
    name: "key3._domainkey"
    content: "key3.nationalpaperhangers.com._domainkey.migadu.com"
    proxied: false
    ttl: 3600
    comment: "DKIM key3 delegate → Migadu"

  # ─── Migadu domain verification (required to claim domain in Migadu admin) ─
  # Replace the token value with what Migadu shows in its domain wizard.
  # This TXT record can coexist with SPF because CF allows multiple TXT on @.
  - type: TXT
    name: "@"
    content: "hosted-email-verify=REPLACE_WITH_MIGADU_VERIFY_TOKEN"
    proxied: false
    ttl: 3600
    comment: "Migadu domain ownership verification — replace token value"

  # ─── DMARC roll-out ───────────────────────────────────────────────────────
  # Phase 1 (Days 1–14): p=none (monitor only, no mail rejected)
  # Aggregate reports (rua) go to info@designerwallcoverings.com (Steve monitors)
  # After 14 days with clean reports: change p=none → p=quarantine
  # After another 14 clean days: change to p=reject (optional, most strict)
  - type: TXT
    name: "_dmarc"
    content: "v=DMARC1; p=none; rua=mailto:info@designerwallcoverings.com; ruf=mailto:info@designerwallcoverings.com; fo=1; adkim=r; aspf=r"
    proxied: false
    ttl: 3600
    comment: "DMARC Phase 1 (monitor). Update p=none→p=quarantine after 14 days of clean reports."