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: falseallowed_idps: []- No extra
requireorexcluderules
- Session duration:
- As of 2026-03-29, this baseline is active on:
erp.solanasis.combaserow.solanasis.comsm.solanasis.comedit.solanasis.comdocs.solanasis.comdb.solanasis.com
- Any deviation from that baseline requires Dmitri’s explicit override and must be recorded in:
solanasis-scripts/security/config/cf_exceptions.jsonsolanasis-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 topages.devURLs
- Current state:
solanasis-sitesolanasis-site.pages.devreturnedHTTP/2 200- no active preview deployments were present at check time
- no Access app was found for the project’s
pages.devdomain
solanasis-docssolanasis-docs.pages.devreturnedHTTP/2 200- no active preview deployments were present at check time
- no Access app was found for the project’s
pages.devdomain
mrsunshine-sitemrsunshine-site.pages.devreturnedHTTP/2 200- active preview deployment URL
db219932.mrsunshine-site.pages.devwas public - active preview alias
feature-resume-redesign-v2.mrsunshine-site.pages.devwas public - no Access app was found for the project’s
pages.devdomain
- 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
- Cloudflare’s Pages project API exposed
WAF Custom Rules (Ruleset 397364e337ef4d2c9ab01ec34deb61f8)
| Slot | Rule | Expression Summary | Action |
|---|---|---|---|
| 1 | IP Allowlist | (ip.src eq <dmitri_ip>) | Skip |
| 2 | Bad User-Agents | Scanners, vuln tools, AI crawlers | Block |
| 3 | Datacenter ASNs | Hosting/cloud ASNs | Managed challenge |
| 4 | US-Only | (ip.src.country ne "US") | Managed challenge |
| 5 | Sensitive Paths | /api/, /admin/, /login/, /.env, /wp-* | Managed challenge |
Zone Settings
| Setting | Value |
|---|---|
| Bot Fight Mode | ON |
| AI Bots Protection | block |
| Crawler Protection | enabled |
| Browser Integrity Check | ON |
| Challenge Passage | 15 min |
Notifications
DDoS, origin monitoring, tunnel health, tunnel events, SSL expiration.
Anti-Indexing
- X-Robots-Tag Transform Rule:
noindex, nofollow, noarchiveon 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.toolView 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.toolCheck 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.toolCheck 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.toolView 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.toolView 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.toolView 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.toolRun 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-runVerification 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
- Open Cloudflare dashboard: Workers & Pages > target project.
- Check the
*.pages.devsubdomain shown on the project. - Open Zero Trust > Access > Applications and confirm whether an app exists for:
- the project root
pages.devhostname - any wildcard preview pattern if used
- the project root
- If an active preview deployment exists, open both:
- the project root
https://<project>.pages.dev - a fresh preview alias URL
- the project root
- Verify whether Cloudflare Access redirects before page content loads.
- Record the result in
service-inventory.mdor this cheatsheet if the exposure is intentional or deferred.
Post-Standardization Validation Evidence
| Date | Hostname | Unauthenticated Result | Baseline Comparison | Manual OTP Step |
|---|---|---|---|---|
| 2026-03-30 | erp.solanasis.com | HTTP/2 302 to solanasis.cloudflareaccess.com/cdn-cgi/access/login/erp.solanasis.com; X-Robots-Tag: noindex, nofollow, noarchive present | Matches docs.solanasis.com perimeter behavior | Manual browser/email OTP still required for full end-to-end proof |
| 2026-03-30 | baserow.solanasis.com | HTTP/2 302 to solanasis.cloudflareaccess.com/cdn-cgi/access/login/baserow.solanasis.com; X-Robots-Tag: noindex, nofollow, noarchive present | Matches docs.solanasis.com perimeter behavior | Manual browser/email OTP still required for full end-to-end proof |
Adding a New Tunnel Service
- Script first: create or update the checked-in script before execution. Do not script Cloudflare changes on the fly.
- Access policy first: create the Cloudflare Access app using the docs baseline.
- Record override first: if the new service needs to differ from the docs baseline, record the override in
cf_exceptions.jsonandservice-inventory.mdbefore exposure. - Add to X-Robots-Tag rule: update the Transform Rule expression to include the new hostname.
- Verify WAF coverage: WAF rules are zone-wide (automatic), but check Rule 2 if service-specific UAs need blocking.
- Update cloudflared config: add the ingress rule in
~/.cloudflared/config.yml. - Run compliance scripts: use
cloudflare_tunnel_policy.py checkandcloudflare_access_audit.py. - Update service inventory:
solanasis-docs/operations/service-inventory.md - Verify Pages preview exposure if this is a Pages project: check the root
pages.devURL and a current preview alias. - Test:
curl -sI https://new.solanasis.com/ | grep x-robots→ should shownoindex- 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, anddbaligned 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: truebeforefight_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/, NOTstatic/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
| Account | Account ID | Infisical Source | Auth Method | |
|---|---|---|---|---|
| Personal | dmitri@mrsunshine.me | c8d052bb9a514aa4965fa199aef40e00 | /cf-personal/ | Global API Key (X-Auth-Key + X-Auth-Email) |
| Solanasis | mr.sunshine@solanasis.com | 5bc74cd88016b1dd85d05955675fdba8 | /shared/ | Global API Key (X-Auth-Key + X-Auth-Email) |
| Matchkeyz | admin@matchkeyz.com | (separate) | /matchkeyz/ | Global API Key (own keys, no imports) |
Zone Distribution
| Account | Zones |
|---|---|
| Personal | selfinquire.com, collab-culture.com, co-nexus.co, creators-hub.co, regenerositysociety.com, zasage.me |
| Solanasis | solanasis.com, mrsunshine.me, docs.solanasis.com |
| Matchkeyz | matchkeyz.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 solanasisCross-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.pyandcloudflare_access_audit.pyscripts only cover the Solanasis account
Related Docs
- 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