← back to Chinoiseriewallpaper
finish_dns.sh
157 lines
#!/usr/bin/env bash
# finish_dns.sh — run AFTER Steve has:
# 1. Cancelled any Afternic listing (if applicable) in GoDaddy portfolio
# 2. Added chinoiseriewallpaper.com as a zone in Cloudflare dashboard
# → dash.cloudflare.com → Add Site → enter chinoiseriewallpaper.com → Free plan
#
# Usage: bash finish_dns.sh
# Prereqs: CLOUDFLARE_API_TOKEN and GODADDY_API_KEY/GODADDY_API_SECRET in env or .env
set -euo pipefail
DOMAIN="chinoiseriewallpaper.com"
ORIGIN_IP="45.61.58.125"
ORIGIN_PORT="9853"
# Load secrets
if [ -f "$(dirname "$0")/../secrets-manager/.env" ]; then
set -o allexport
source "$(dirname "$0")/../secrets-manager/.env"
set +o allexport
fi
: "${CLOUDFLARE_API_TOKEN:?Need CLOUDFLARE_API_TOKEN}"
: "${GODADDY_API_KEY:?Need GODADDY_API_KEY}"
: "${GODADDY_API_SECRET:?Need GODADDY_API_SECRET}"
CF="$CLOUDFLARE_API_TOKEN"
# Prefer a Bearer PAT when present (TK-10 key rotation); else legacy sso-key.
GD_PAT="${GODADDY_PAT:-}"
if [ -n "$GD_PAT" ]; then GD_AUTH="Bearer $GD_PAT"; else GD_AUTH="sso-key $GODADDY_API_KEY:$GODADDY_API_SECRET"; fi
echo "=== Step 1: Get Cloudflare zone ==="
ZONE=$(curl -sS -H "Authorization: Bearer $CF" "https://api.cloudflare.com/client/v4/zones?name=$DOMAIN")
ZONE_ID=$(echo "$ZONE" | python3 -c "import json,sys; d=json.load(sys.stdin); r=d.get('result',[]); print(r[0]['id'] if r else 'NOT_FOUND')")
echo "Zone ID: $ZONE_ID"
[ "$ZONE_ID" = "NOT_FOUND" ] && echo "ERROR: Zone not found. Add $DOMAIN in CF dashboard first." && exit 1
NS=$(echo "$ZONE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(' '.join(d['result'][0]['name_servers']))")
echo "CF nameservers: $NS"
echo ""
echo "=== Step 2: Repoint GoDaddy NS to Cloudflare ==="
NS_JSON=$(echo "$ZONE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(json.dumps(d['result'][0]['name_servers']))")
curl -sS -X PATCH "https://api.godaddy.com/v1/domains/$DOMAIN" \
-H "Authorization: $GD_AUTH" \
-H "Content-Type: application/json" \
--data "{\"nameServers\":$NS_JSON}" \
-w "\nHTTP %{http_code}\n"
echo ""
echo "=== Step 3: Delete stale Afternic/GreenDomainBrokers records ==="
sleep 5
curl -sS -H "Authorization: Bearer $CF" \
"https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records?per_page=100" \
| python3 -c "
import json,sys
d=json.load(sys.stdin)
records=d.get('result',[])
print(f'Found {len(records)} records to evaluate')
for r in records:
print(f' {r[\"type\"]:6} {r[\"name\"]:40} {r[\"content\"][:50]}')
"
# Delete everything except our A record
curl -sS -H "Authorization: Bearer $CF" \
"https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records?per_page=100" \
| python3 -c "
import json,sys
d=json.load(sys.stdin)
keep_content='$ORIGIN_IP'
ids=[r['id'] for r in d.get('result',[]) if not (r['type']=='A' and r['content']==keep_content)]
print(' '.join(ids))
" > /tmp/stale_ids.txt
while IFS=' ' read -r -a ids; do
for id in "${ids[@]}"; do
[ -z "$id" ] && continue
curl -sS -X DELETE -H "Authorization: Bearer $CF" \
"https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/$id" >/dev/null
echo " Deleted record $id"
done
done < /tmp/stale_ids.txt
echo ""
echo "=== Step 4: Add A records (proxy OFF for certbot) ==="
for name in "$DOMAIN" "www.$DOMAIN"; do
curl -sS -H "Authorization: Bearer $CF" -H "Content-Type: application/json" \
-X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records" \
--data "{\"type\":\"A\",\"name\":\"$name\",\"content\":\"$ORIGIN_IP\",\"ttl\":1,\"proxied\":false}" \
| python3 -c "import json,sys; d=json.load(sys.stdin); print(f'A record {d[\"result\"][\"name\"] if d.get(\"result\") else d.get(\"errors\")}')" 2>/dev/null
done
echo ""
echo "=== Waiting 90s for NS propagation before certbot ==="
sleep 90
echo ""
echo "=== Step 5: Certbot SSL ==="
ssh root@45.61.58.125 "certbot --nginx -d $DOMAIN -d www.$DOMAIN \
--non-interactive --agree-tos --email steve@designerwallcoverings.com --redirect"
echo ""
echo "=== Step 6: Re-enable Cloudflare proxy ==="
for id in $(curl -sS -H "Authorization: Bearer $CF" \
"https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records?type=A" \
| python3 -c "import json,sys; [print(r['id']) for r in json.load(sys.stdin).get('result',[])]"); do
curl -sS -X PATCH -H "Authorization: Bearer $CF" -H "Content-Type: application/json" \
"https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/$id" \
--data '{"proxied":true}' >/dev/null
done
echo "Proxy enabled"
echo ""
echo "=== Step 7: Add email DNS (MX, SPF, DMARC for Purelymail) ==="
# MX record
curl -sS -H "Authorization: Bearer $CF" -H "Content-Type: application/json" \
-X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records" \
--data "{\"type\":\"MX\",\"name\":\"$DOMAIN\",\"content\":\"mx1.purelymail.com\",\"priority\":10,\"ttl\":3600,\"proxied\":false}" | python3 -c "import json,sys; d=json.load(sys.stdin); print('MX1:', 'ok' if d.get('success') else d.get('errors'))"
curl -sS -H "Authorization: Bearer $CF" -H "Content-Type: application/json" \
-X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records" \
--data "{\"type\":\"MX\",\"name\":\"$DOMAIN\",\"content\":\"mx2.purelymail.com\",\"priority\":20,\"ttl\":3600,\"proxied\":false}" | python3 -c "import json,sys; d=json.load(sys.stdin); print('MX2:', 'ok' if d.get('success') else d.get('errors'))"
curl -sS -H "Authorization: Bearer $CF" -H "Content-Type: application/json" \
-X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records" \
--data "{\"type\":\"MX\",\"name\":\"$DOMAIN\",\"content\":\"mx3.purelymail.com\",\"priority\":30,\"ttl\":3600,\"proxied\":false}" | python3 -c "import json,sys; d=json.load(sys.stdin); print('MX3:', 'ok' if d.get('success') else d.get('errors'))"
# SPF
curl -sS -H "Authorization: Bearer $CF" -H "Content-Type: application/json" \
-X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records" \
--data "{\"type\":\"TXT\",\"name\":\"$DOMAIN\",\"content\":\"v=spf1 include:purelymail.com ~all\",\"ttl\":3600}" | python3 -c "import json,sys; d=json.load(sys.stdin); print('SPF:', 'ok' if d.get('success') else d.get('errors'))"
# DKIM — Purelymail standard selector
curl -sS -H "Authorization: Bearer $CF" -H "Content-Type: application/json" \
-X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records" \
--data "{\"type\":\"TXT\",\"name\":\"purelymail1._domainkey.$DOMAIN\",\"content\":\"v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMkobFe6QQRhHdMpLxNk3HObA7hXJ4iBxGlwBSmIDRiUhzjrVYkUEgXN3n09ZZzJGypXlZYLcMz4iR3VQ0oE8BPVNP11TMpbJqLhT8lgTOFe+w1+8eLsRSflTiDkFzIL8Ds6I4H/KxrqiVlQNUKlGVIzHDkDqzHMJ0j2DQN9NxQIDAQAB\",\"ttl\":3600}" | python3 -c "import json,sys; d=json.load(sys.stdin); print('DKIM:', 'ok' if d.get('success') else d.get('errors'))"
# DMARC
curl -sS -H "Authorization: Bearer $CF" -H "Content-Type: application/json" \
-X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records" \
--data "{\"type\":\"TXT\",\"name\":\"_dmarc.$DOMAIN\",\"content\":\"v=DMARC1; p=none; rua=mailto:info@$DOMAIN\",\"ttl\":3600}" | python3 -c "import json,sys; d=json.load(sys.stdin); print('DMARC:', 'ok' if d.get('success') else d.get('errors'))"
echo ""
echo "=== Step 8: Verify HTTPS ==="
sleep 30
curl -sS -o /dev/null -w "https://$DOMAIN/ → HTTP %{http_code} in %{time_total}s\n" "https://$DOMAIN/?_=$RANDOM"
echo ""
echo "=== DONE ==="
echo "Live URL: https://$DOMAIN"
echo "Origin: http://45.61.58.125:$ORIGIN_PORT (pm2 chinoiseriewallpaper)"
echo ""
echo "TODO (manual):"
echo " 1. Add chinoiseriewallpaper.com to Purelymail dashboard (addDomain API timed out)"
echo " 2. Create info@chinoiseriewallpaper.com mailbox in Purelymail dashboard"
echo " 3. Get DKIM key from Purelymail dashboard and update purelymail1._domainkey record"
echo " 4. Save mailbox password via: node ~/Projects/secrets-manager/cli.js add INFO_CHINOISERIEWALLPAPER_PASSWORD <pass>"