Cloudflare Hardening Cheatsheet

Zone: solanasis.com Zone ID: ceb3c9dcba422dc31900360da7117173 Account ID: 5bc74cd88016b1dd85d05955675fdba8 Auth: X-Auth-Key + X-Auth-Email: mr.sunshine@solanasis.com API Key: In Infisical folder solanasis-site as CLOUDFLARE_GLOBAL_API_KEY


Quick Reference — What’s Deployed

Tunnel Access Baseline

  • Baseline reference app: docs.solanasis.com
  • Standard Access settings for all tunnel-backed local services:
    • Session duration: 24h
    • Allowed emails: dmitri@solanasis.com, ds@solanasis.com, mr.sunshine@solanasis.com
    • auto_redirect_to_identity: false
    • allowed_idps: []
    • No extra require or exclude rules
  • As of 2026-03-29, this baseline is active on:
    • erp.solanasis.com
    • baserow.solanasis.com
    • sm.solanasis.com
    • edit.solanasis.com
    • docs.solanasis.com
    • db.solanasis.com
  • Any deviation from that baseline requires Dmitri’s explicit override and must be recorded in:
    • solanasis-scripts/security/config/cf_exceptions.json
    • solanasis-docs/operations/service-inventory.md

Pages Preview URL Status

  • Verified on 2026-03-30 with:
    • Cloudflare API reads for Pages projects and deployments
    • Cloudflare Access app inventory check
    • direct curl -D - probes to pages.dev URLs
  • Current state:
    • solanasis-site
      • solanasis-site.pages.dev returned HTTP/2 200
      • no active preview deployments were present at check time
      • no Access app was found for the project’s pages.dev domain
    • solanasis-docs
      • solanasis-docs.pages.dev returned HTTP/2 200
      • no active preview deployments were present at check time
      • no Access app was found for the project’s pages.dev domain
    • mrsunshine-site
      • mrsunshine-site.pages.dev returned HTTP/2 200
      • active preview deployment URL db219932.mrsunshine-site.pages.dev was public
      • active preview alias feature-resume-redesign-v2.mrsunshine-site.pages.dev was public
      • no Access app was found for the project’s pages.dev domain
  • Important limitation:
    • Cloudflare’s Pages project API exposed deployment_configs.preview, but not a preview Access-policy field
    • the audit therefore verifies current state via Access app presence plus live HTTP behavior, not via a single authoritative API flag

WAF Custom Rules (Ruleset 397364e337ef4d2c9ab01ec34deb61f8)

SlotRuleExpression SummaryAction
1IP Allowlist(ip.src eq <dmitri_ip>)Skip
2Bad User-AgentsScanners, vuln tools, AI crawlersBlock
3Datacenter ASNsHosting/cloud ASNsManaged challenge
4US-Only(ip.src.country ne "US")Managed challenge
5Sensitive Paths/api/, /admin/, /login/, /.env, /wp-*Managed challenge

Zone Settings

SettingValue
Bot Fight ModeON
AI Bots Protectionblock
Crawler Protectionenabled
Browser Integrity CheckON
Challenge Passage15 min

Notifications

DDoS, origin monitoring, tunnel health, tunnel events, SSL expiration.

Anti-Indexing

  • X-Robots-Tag Transform Rule: noindex, nofollow, noarchive on baserow, sm, edit, erp, docs, db
  • robots.txt: docs.solanasis.com (Quartz quartz/static/robots.txt)

API Commands

Get credentials (WSL)

# Load Infisical client secret
export INFISICAL_CLIENT_SECRET=$(grep INFISICAL_CLIENT_SECRET /mnt/c/_my/.env | cut -d= -f2)
 
# Get CF API key
CF_API_KEY=$(python3 /mnt/c/_my/_solanasis/infisical/manage_secrets.py get CLOUDFLARE_GLOBAL_API_KEY -f solanasis-site --plain)
 
# Common headers for all API calls
CF_ZONE="ceb3c9dcba422dc31900360da7117173"
CF_EMAIL="mr.sunshine@solanasis.com"

View WAF rules

curl -s "https://api.cloudflare.com/client/v4/zones/$CF_ZONE/rulesets" \
  -H "X-Auth-Email: $CF_EMAIL" \
  -H "X-Auth-Key: $CF_API_KEY" | python3 -m json.tool

View a specific ruleset

RULESET_ID="397364e337ef4d2c9ab01ec34deb61f8"
curl -s "https://api.cloudflare.com/client/v4/zones/$CF_ZONE/rulesets/$RULESET_ID" \
  -H "X-Auth-Email: $CF_EMAIL" \
  -H "X-Auth-Key: $CF_API_KEY" | python3 -m json.tool

Check Bot Fight Mode

curl -s "https://api.cloudflare.com/client/v4/zones/$CF_ZONE/bot_management" \
  -H "X-Auth-Email: $CF_EMAIL" \
  -H "X-Auth-Key: $CF_API_KEY" | python3 -m json.tool

Check zone settings (challenge passage, BIC, etc.)

curl -s "https://api.cloudflare.com/client/v4/zones/$CF_ZONE/settings" \
  -H "X-Auth-Email: $CF_EMAIL" \
  -H "X-Auth-Key: $CF_API_KEY" | python3 -m json.tool

View notification policies

CF_ACCOUNT="5bc74cd88016b1dd85d05955675fdba8"
curl -s "https://api.cloudflare.com/client/v4/accounts/$CF_ACCOUNT/alerting/v3/policies" \
  -H "X-Auth-Email: $CF_EMAIL" \
  -H "X-Auth-Key: $CF_API_KEY" | python3 -m json.tool

View Transform Rules (X-Robots-Tag)

curl -s "https://api.cloudflare.com/client/v4/zones/$CF_ZONE/rulesets/phases/http_response_headers_transform/entrypoint" \
  -H "X-Auth-Email: $CF_EMAIL" \
  -H "X-Auth-Key: $CF_API_KEY" | python3 -m json.tool

View Access applications

curl -s "https://api.cloudflare.com/client/v4/accounts/$CF_ACCOUNT/access/apps" \
  -H "X-Auth-Email: $CF_EMAIL" \
  -H "X-Auth-Key: $CF_API_KEY" | python3 -m json.tool

Run the baseline policy checks

# Check tunnel hostnames against the docs baseline
python3 /home/zasage/_my/_solanasis/solanasis-scripts/security/cloudflare_tunnel_policy.py check --json
 
# Preview the exact Cloudflare mutations before applying them
python3 /home/zasage/_my/_solanasis/solanasis-scripts/security/cloudflare_tunnel_policy.py apply --dry-run
 
# Apply the docs baseline to all tunnel hostnames
python3 /home/zasage/_my/_solanasis/solanasis-scripts/security/cloudflare_tunnel_policy.py apply
 
# Run the full audit after any policy change
python3 /home/zasage/_my/_solanasis/solanasis-scripts/security/cloudflare_access_audit.py --output-dir /tmp/cloudflare-audit
 
# Run the nightly scan orchestration dry-run, which now includes the tunnel baseline check
python3 /home/zasage/_my/_solanasis/solanasis-scripts/security/nightly_security_scan.py --dry-run

Verification Checks

# X-Robots-Tag on tunnel services
curl -sI https://baserow.solanasis.com/ | grep -i x-robots
curl -sI https://sm.solanasis.com/ | grep -i x-robots
curl -sI https://edit.solanasis.com/ | grep -i x-robots
curl -sI https://erp.solanasis.com/ | grep -i x-robots
curl -sI https://docs.solanasis.com/ | grep -i x-robots
 
curl -sI https://db.solanasis.com/ | grep -i x-robots
 
# Access perimeter behavior on the two most material baseline changes
curl -s -o /dev/null -D - https://erp.solanasis.com/ | sed -n '1,12p'
curl -s -o /dev/null -D - https://baserow.solanasis.com/ | sed -n '1,12p'
 
# robots.txt
curl -s https://docs.solanasis.com/robots.txt
 
# Challenge passage (should be 900 = 15 min)
curl -s "https://api.cloudflare.com/client/v4/zones/$CF_ZONE/settings/challenge_ttl" \
  -H "X-Auth-Email: $CF_EMAIL" \
  -H "X-Auth-Key: $CF_API_KEY"

Manual Pages Preview Verification

  1. Open Cloudflare dashboard: Workers & Pages > target project.
  2. Check the *.pages.dev subdomain shown on the project.
  3. Open Zero Trust > Access > Applications and confirm whether an app exists for:
    • the project root pages.dev hostname
    • any wildcard preview pattern if used
  4. If an active preview deployment exists, open both:
    • the project root https://<project>.pages.dev
    • a fresh preview alias URL
  5. Verify whether Cloudflare Access redirects before page content loads.
  6. Record the result in service-inventory.md or this cheatsheet if the exposure is intentional or deferred.

Post-Standardization Validation Evidence

DateHostnameUnauthenticated ResultBaseline ComparisonManual OTP Step
2026-03-30erp.solanasis.comHTTP/2 302 to solanasis.cloudflareaccess.com/cdn-cgi/access/login/erp.solanasis.com; X-Robots-Tag: noindex, nofollow, noarchive presentMatches docs.solanasis.com perimeter behaviorManual browser/email OTP still required for full end-to-end proof
2026-03-30baserow.solanasis.comHTTP/2 302 to solanasis.cloudflareaccess.com/cdn-cgi/access/login/baserow.solanasis.com; X-Robots-Tag: noindex, nofollow, noarchive presentMatches docs.solanasis.com perimeter behaviorManual browser/email OTP still required for full end-to-end proof

Adding a New Tunnel Service

  1. Script first: create or update the checked-in script before execution. Do not script Cloudflare changes on the fly.
  2. Access policy first: create the Cloudflare Access app using the docs baseline.
  3. Record override first: if the new service needs to differ from the docs baseline, record the override in cf_exceptions.json and service-inventory.md before exposure.
  4. Add to X-Robots-Tag rule: update the Transform Rule expression to include the new hostname.
  5. Verify WAF coverage: WAF rules are zone-wide (automatic), but check Rule 2 if service-specific UAs need blocking.
  6. Update cloudflared config: add the ingress rule in ~/.cloudflared/config.yml.
  7. Run compliance scripts: use cloudflare_tunnel_policy.py check and cloudflare_access_audit.py.
  8. Update service inventory: solanasis-docs/operations/service-inventory.md
  9. Verify Pages preview exposure if this is a Pages project: check the root pages.dev URL and a current preview alias.
  10. Test:
  • curl -sI https://new.solanasis.com/ | grep x-robots → should show noindex
  • Visit in browser → should prompt for OTP
  • Check Security > Events in dashboard after 24h

Recent baseline standardization:

  • 2026-03-29 — erp, baserow, sm, edit, docs, and db aligned to the docs OTP baseline
  • 2026-03-30 — nightly security scan now includes cloudflare_tunnel_policy.py check
  • 2026-03-30 — Pages preview verification upgraded from “could not determine” to live Access-app + HTTP evidence

Gotchas & Lessons Learned

  • API auth: Use Global API Key (X-Auth-Key + X-Auth-Email), NOT the Pages API token (limited scope)
  • Bot Fight Mode: Must set enable_js: true before fight_mode: true — API rejects otherwise
  • BFM is all-or-nothing on free tier: Cannot skip per-path or per-hostname. If API traffic breaks, disable entirely
  • WAF rule order matters: Allowlist (skip) must be first, or your own IP gets challenged
  • Transform Rules: Free plan allows 2 response header modification rules (1 used, 1 spare)
  • robots.txt vs X-Robots-Tag: Both deployed as belt-and-suspenders. X-Robots-Tag is the reliable layer (covers all services via single rule)
  • Quartz static files: Live in quartz/static/, NOT static/ at repo root. Needs rebuild to deploy.

Phase 6 Status (cloudflared Config Hardening)

Applied: 2026-03-28

  • connectTimeout: 10s (default 30s)
  • noTLSVerify: false (explicit)
  • http2Origin: false (explicit)

Multi-Account Cloudflare Management

Solanasis operates across 3 separate Cloudflare accounts. Each has its own credentials, zones, and Infisical folder structure.

Account Map

AccountEmailAccount IDInfisical SourceAuth Method
Personaldmitri@mrsunshine.mec8d052bb9a514aa4965fa199aef40e00/cf-personal/Global API Key (X-Auth-Key + X-Auth-Email)
Solanasismr.sunshine@solanasis.com5bc74cd88016b1dd85d05955675fdba8/shared/Global API Key (X-Auth-Key + X-Auth-Email)
Matchkeyzadmin@matchkeyz.com(separate)/matchkeyz/Global API Key (own keys, no imports)

Zone Distribution

AccountZones
Personalselfinquire.com, collab-culture.com, co-nexus.co, creators-hub.co, regenerositysociety.com, zasage.me
Solanasissolanasis.com, mrsunshine.me, docs.solanasis.com
Matchkeyzmatchkeyz.io

Scoped Tokens vs Global Keys

  • Global API Key — Full account access. Stored in Infisical as CLOUDFLARE_GLOBAL_API_KEY. Used for DNS management, token updates, and account-wide operations.
  • Scoped API Token — Limited permissions per zone. Stored as CLOUDFLARE_API_TOKEN. Used in CI/CD (GitHub Actions). Must explicitly include all needed permissions (Pages, DNS, Workers, etc.).

Gotcha: Scoped tokens for CF Pages deployments need Zone > DNS > Read + Edit permissions if you want automatic domain binding. Without DNS permissions, custom domains get stuck at “pending — CNAME record not set.”

Onboarding a New Domain

For domains on the Personal account (dmitri@mrsunshine.me):

# Creates Infisical folder, adds /shared/ + /cf-personal/ imports, verifies CF key
python3 ~/\_my/_solanasis/infisical/link_cf_account.py <folder-name>
 
# Dry run first
python3 ~/\_my/_solanasis/infisical/link_cf_account.py <folder-name> --check-only
 
# For Solanasis account domains
python3 ~/\_my/_solanasis/infisical/link_cf_account.py <folder-name> --cf-account solanasis

Cross-Account API Commands

# Personal account — get credentials
CF_API_KEY=$(secret get CLOUDFLARE_GLOBAL_API_KEY -f cf-personal --plain)
CF_EMAIL="dmitri@mrsunshine.me"
CF_ACCOUNT="c8d052bb9a514aa4965fa199aef40e00"
 
# Solanasis account — get credentials (same pattern as main cheatsheet above)
CF_API_KEY=$(secret get CLOUDFLARE_GLOBAL_API_KEY -f solanasis-site --plain)
CF_EMAIL="mr.sunshine@solanasis.com"
CF_ACCOUNT="5bc74cd88016b1dd85d05955675fdba8"
 
# List zones for any account
curl -s "https://api.cloudflare.com/client/v4/zones?account.id=$CF_ACCOUNT" \
  -H "X-Auth-Email: $CF_EMAIL" \
  -H "X-Auth-Key: $CF_API_KEY" | python3 -c "import sys,json; [print(z['name'], z['id']) for z in json.load(sys.stdin)['result']]"
 
# List API tokens for any account
curl -s "https://api.cloudflare.com/client/v4/user/tokens" \
  -H "X-Auth-Email: $CF_EMAIL" \
  -H "X-Auth-Key: $CF_API_KEY" | python3 -c "import sys,json; [print(t['name'], t['id'], t['status']) for t in json.load(sys.stdin)['result']]"

Hardening Notes for Non-Solanasis Accounts

The WAF rules, Bot Fight Mode, and tunnel baseline documented above are Solanasis-account specific. For the Personal and Matchkeyz accounts:

  • CF Pages sites (selfinquire.com, etc.) use CF’s automatic SSL and DDoS protection
  • No tunnel services — these are static sites, not proxied internal apps
  • WAF and Access policies should be configured separately per account as needed
  • The cloudflare_tunnel_policy.py and cloudflare_access_audit.py scripts only cover the Solanasis account

  • Full hardening plan: claude-code-plans/deep-plans/deep-plan-cf-hardening-master.md
  • Phase docs: claude-code-plans/deep-plans/deep-plan-cf-hardening-phase-{1-7}-*.md
  • Incident log: solanasis-incident-reports/incidents/2026-03-27-docs-exposure-incident-log.md
  • Security hardening guide: solanasis-docs/website-config/04-security-hardening-guide.md
  • Service inventory: solanasis-docs/operations/service-inventory.md
  • Global CLAUDE.md: ~/.claude/CLAUDE.md (Cloudflare Zone Hardening Standard section)
  • CF Pages cutover playbook: solanasis-docs/playbooks/cf-pages-cutover-playbook.md
  • CF account map (memory): reference_cf_account_map.md
  • Infisical account linker: _solanasis/infisical/link_cf_account.py