Cloudflare Security Operations
Covers: zintegrowana.online and radekkonarski.com — Cloudflare Free plan, DNS-only zones.
All security findings come from Cloudflare Security Insights dashboard.
1. Access
| Path | Notes |
|---|---|
| Dashboard → Security → Security Insights | Primary view |
https://dash.cloudflare.com/<account>/security-insights | Direct URL |
API: GET /accounts/{account_id}/security-insights/issues | Requires CF_API_TOKEN with Account Read scope |
2. Monitoring Frequency
| Cadence | Who / How |
|---|---|
| Weekly | cloudflare-security-check.yml CI workflow — runs Mondays 07:00 UTC; opens a GH issue on any new unresolved findings |
| Monthly | Manual review — open dashboard, verify the fix backlog is draining, update compliance_notes in dev_r_services |
| On DNS change | Run a manual review after any record add/edit — unproxied record findings update within ~1h |
3. Current Finding Inventory (2026-06-23)
Captured from dashboard screenshot. Fix status tracked here until resolved.
| # | Finding | Type | Count | Priority | Status |
|---|---|---|---|---|---|
| 1 | DMARC Record Error detected (zintegrowana.online) | Email Security | — | High | Fixed 2026-06-22 — SPF+DMARC updated |
| 1b | DMARC Record Error detected (radekkonarski.com) | Email Security | — | High | Fixed 2026-06-23 — confirmed p=quarantine, zone documented (issue #988) |
| 2 | Bot Fight Mode not enabled | Config suggestion | 2 | Medium | Open — dismiss (DNS-only zone) |
| 3 | Security.txt not configured | Config suggestion | 2 | Low | Partially fixed 2026-06-22 — radekkonarski.com done (issue #987); zintegrowana.online pending |
| 4 | Unproxied A Record detected | Exposed infra | 3 | Medium | Open — intentional (VPS/BMS direct — see §4.4) |
| 5 | Unproxied CNAME Record detected | Exposed infra | 1 | Medium | Open — intentional |
| 6 | Insecure configuration | Config suggestion | 1 | Medium | Open |
| 7 | Weak authentication | Auth | 1 | High | Open — requires 2FA (human action) |
| 8 | Dangling A Record (*.zintegrowana.online) | Security | 1 | Moderate | Investigated 2026-06-23 — see §4.4 (no dangling record; bms-4 wildcard was undocumented) |
Investigation note (2026-06-23, issue #985):
Cloudflare flagged *.zintegrowana.online as a “Dangling A Record” finding (Moderate severity, detected 2026-06-13).
Full DNS audit performed via API — no record points to an IP we no longer own.
The finding covers all three unproxied wildcard A records in the zone:
*.vps-i1.infra.zintegrowana.online→217.154.82.162(IONOS vps-i1, owned)*.vps-h1.infra.zintegrowana.online→72.60.32.61(Hostinger vps-h1, owned)*.bms-4.infra.zintegrowana.online→54.36.123.110(OVH bms-4, owned — added 2026-06-10, undocumented until this fix)
All three are intentional unproxied records managed by their respective server’s TLS stack (Caddy/Traefik). No DNS changes needed. §4.4 updated to include bms-4.
4. Fix Procedures
4.1 DMARC Record Error (Priority: High) — Fixed 2026-06-22
Root causes found and fixed:
- SPF was missing Mailgun EU (
include:mailgun.org) — Alertmanager emails viap24-email-senderWorker weren’t covered - DMARC alignment was
adkim=s; aspf=s(strict) — failed when Cloudflare Worker sends on behalf of the domain - DMARC
ruawas pointing todmarc-reports@zintegrowana.online(no routing rule, reports bounced)
Applied fixes (2026-06-22 via Cloudflare API):
SPF: v=spf1 include:_spf.mx.cloudflare.net include:mailgun.org ~all
DMARC: v=DMARC1; p=quarantine; rua=mailto:radieu@gmail.com; sp=quarantine; adkim=r; aspf=r; pct=100
Email architecture on this domain:
- All infra alerts →
email-api.zintegrowana.online→p24-email-senderCF Worker → Cloudflare Email Routing - Worker DKIM:
cf2024-1._domainkey.zintegrowana.online(Cloudflare managed, auto-passes DMARC) - Bounce subdomain:
cf-bounce.zintegrowana.online(separate SPF + DKIM)
⚠️ Important: email-api.zintegrowana.online → 192.0.2.1 (proxied) is NOT a placeholder to delete.
It is the required DNS record for the p24-email-sender Cloudflare Worker route (workers/email-sender/).
Deleting it breaks all infra alert emails. Confirmed 2026-06-22.
To escalate to p=reject — only after DMARC aggregate reports from radieu@gmail.com confirm zero failures for 2+ weeks:
# Via Cloudflare API (PowerShell):
$body = '{"type":"TXT","name":"_dmarc.zintegrowana.online","content":"v=DMARC1; p=reject; rua=mailto:radieu@gmail.com; sp=reject; adkim=r; aspf=r; pct=100","ttl":1}'
Invoke-RestMethod "https://api.cloudflare.com/client/v4/zones/57cb3d8f24c7cc319fb703394edc7b87/dns_records/<id>" -Method PUT -Headers @{"Authorization"="Bearer $CF_TOKEN"} -Body $body4.2 Bot Fight Mode Not Enabled (Priority: Medium)
What it means: Cloudflare Bot Fight Mode is disabled — automated bot traffic hits VPS directly without Cloudflare filtering. Relevant only for proxied records; DNS-only records bypass it anyway.
Current state: Zone is DNS-only (no orange-cloud proxy) — Bot Fight Mode applies only to proxied hostnames. Since all infra subdomains use unproxied A records, this setting has no effect until proxy is enabled.
Decision options:
- Option A — ignore (recommended for now): Accept the finding. Our services use Caddy/Traefik with their own rate limiting. Bot Fight Mode requires proxied records which expose Cloudflare IPs.
- Option B — enable selectively: Proxy only public-facing endpoints (e.g.
grafana.vps-i1.infra.zintegrowana.online) and enable Bot Fight Mode. Risk: Cloudflare IPs instead of direct VPS IPs.
To dismiss in dashboard: Security Insights → finding → “Dismiss” (marks as accepted risk).
4.3 Security.txt Not Configured (Priority: Low)
What it means: /.well-known/security.txt is missing. Security researchers cannot find contact info for responsible disclosure.
4.3.1 radekkonarski.com — Fixed 2026-06-22
Status: Live at https://radekkonarski.com/.well-known/security.txt (HTTP 200).
Implementation: Static file committed to radieu/radekkonarski-personal-brand in
website/.well-known/security.txt, served via Vercel with Content-Type: text/plain; charset=utf-8
header in website/vercel.json. Commit: f744af3 on main.
Content (RFC 9116 compliant):
Contact: mailto:radieu@gmail.com
Expires: 2027-06-22T00:00:00.000Z
Canonical: https://radekkonarski.com/.well-known/security.txt
Preferred-Languages: en, pl
Renewal reminder: Expires is 2027-06-22. Update before that date in
website/.well-known/security.txt in the radekkonarski-personal-brand repo.
4.3.2 zintegrowana.online — Pending
Fix — add to Caddy on vps-i1:
# In /opt/p24-infra/monitoring/caddy/Caddyfile
zintegrowana.online {
handle /.well-known/security.txt {
respond `Contact: mailto:radieu@gmail.com
Expires: 2027-01-01T00:00:00.000Z
Preferred-Languages: en, pl
` 200
}
}
Then reload: ssh root@217.154.82.162 "docker exec monitoring-caddy-1 caddy reload --config /etc/caddy/Caddyfile"
4.4 Unproxied A / CNAME Records (Priority: Medium — Intentional)
What it means: Cloudflare detects DNS records that bypass the Cloudflare proxy (no orange cloud). Server IP is exposed directly.
Current state — intentional (all three wildcards documented, verified 2026-06-23):
| Record | IP | Server | TLS handled by |
|---|---|---|---|
*.vps-i1.infra.zintegrowana.online | 217.154.82.162 | IONOS vps-i1 | Caddy |
*.vps-h1.infra.zintegrowana.online | 72.60.32.61 | Hostinger vps-h1 | Traefik |
*.bms-4.infra.zintegrowana.online | 54.36.123.110 | OVH bms-4 (added 2026-06-10) | Traefik |
CNAME (grey cloud, intentional):
n8n-cloud.infra.zintegrowana.online→ CNAME top24.app.n8n.cloud(n8n Cloud, no proxy needed)
Action: Dismiss these findings in Cloudflare dashboard → mark as “Accepted Risk”.
Rule — update this doc when adding a new unproxied record: Any new wildcard or direct A record for an infra server MUST be added to the table above in the same PR that creates the DNS record. This prevents future false-positive “Dangling A Record” security findings.
If a new unproxied record appears unexpectedly:
# List all A/CNAME records and check for unexpected entries
python3 /opt/p24-infra/scripts/dns-manager.py list4.5 Insecure Configuration (Priority: Medium)
What it means: Likely SSL/TLS minimum version or HSTS not configured at zone level.
Check:
- Cloudflare dashboard → SSL/TLS → Edge Certificates
- Verify: Minimum TLS Version = TLS 1.2 (or 1.3)
- Verify: Always Use HTTPS = ON (for any proxied records)
Fix: Enable in dashboard. For DNS-only zone, this is advisory — TLS is handled by Caddy/Traefik on VPS.
4.6 Weak Authentication (Priority: High)
What it means: Cloudflare account has weak authentication — likely 2FA is not enabled or using email/password only.
Fix:
- Cloudflare dashboard → My Profile → Authentication
- Enable Two-Factor Authentication (TOTP via authenticator app)
- Store backup codes in SOPS: add
CLOUDFLARE_2FA_BACKUP_CODEStosecrets/vps-h1.env.sops
Acceptance criteria: 2FA enabled, finding disappears within 24h.
5. Monitoring Integration
5.1 GitHub Actions — Weekly Check
File: .github/workflows/cloudflare-security-check.yml
Runs every Monday at 07:00 UTC. Queries Cloudflare Security Insights API. Opens a GH issue if any non-dismissed High findings exist.
Required GH Secret (already exists): CF_API_TOKEN
5.2 Prometheus — Future Exporter
If Cloudflare security posture needs continuous tracking, add a cloudflare-exporter to the monitoring stack:
Port: 9215
Metrics:
cloudflare_security_insights_open_total{zone, severity}
cloudflare_security_insights_dismissed_total{zone}
cloudflare_dns_records_unproxied_total{zone}
Not implemented yet — the weekly CI check covers the current need.
6. Acceptance Criteria — Clearing the Backlog
| Item | Target date | Done when |
|---|---|---|
| 2FA enabled on Cloudflare account | 2026-06-30 | Finding #7 clears |
| DMARC record fixed | 2026-06-30 | 0 DMARC errors in Cloudflare |
| Unproxied records dismissed | 2026-06-25 | Findings marked Accepted Risk |
| Bot Fight Mode dismissed | 2026-06-25 | Finding marked Accepted Risk |
| Security.txt — radekkonarski.com | 200 at https://radekkonarski.com/.well-known/security.txt ✓ | |
| Security.txt — zintegrowana.online | 2026-07-15 | 200 response at https://zintegrowana.online/.well-known/security.txt |
| Insecure config resolved | 2026-06-30 | TLS 1.2+ enforced |
7. Escalation
| Condition | Action |
|---|---|
| New High finding not in this doc | Open GH issue bug + p24-infra-nc-alert, fix within 48h |
| DMARC alignment failure on email | Check Mailgun EU sending logs → rotate DKIM if needed |
| DNS record anomaly (unexpected record) | Immediate: delete via dns-manager.py, open GH issue |
| Cloudflare account compromise suspected | Immediately rotate CF_API_TOKEN + CLOUDFLARE_TOKEN_ZINTEGROWANA in SOPS + GH Secrets |
8. Credential Rotation
| Credential | Rotation freq | SOPS key | Scope |
|---|---|---|---|
CF_API_TOKEN | 180d | secrets/monitoring.env.sops | zintegrowana.online DNS + Account Read |
CLOUDFLARE_TOKEN_ZINTEGROWANA | 180d | secrets/monitoring.env.sops | zintegrowana.online zone |
CF_RADEKKONARSKI_DNS_TOKEN | 30d (auto) | secrets/monitoring.env.sops | radekkonarski.com DNS:Write only |
CF_GLOBAL_API_KEY | 180d | secrets/administration.env.sops | Full account — token management only |
| Cloudflare account password | 180d | password manager | — |
Next rotation due: 2026-11-25 (from 2026-05-28 last rotation).
9. radekkonarski.com Zone
Personal brand domain managed under the same Cloudflare account as zintegrowana.online.
| Field | Value |
|---|---|
| Zone ID | 7d2cd27bc46075f693771646cc54b0f8 |
| SOPS key | CF_ZONE_ID_RADEKKONARSKI in secrets/monitoring.env.sops |
| Nameservers | alberto.ns.cloudflare.com / kehlani.ns.cloudflare.com |
| Plan | Free — DNS-only |
| Repo | radekkonarski-personal-brand |
9.1 Email Security Records (verified 2026-06-23)
| Record | Name | Content | Status |
|---|---|---|---|
| TXT (DMARC) | _dmarc.radekkonarski.com | v=DMARC1; p=quarantine; rua=mailto:radieu@gmail.com; sp=quarantine; adkim=r; aspf=r; pct=100 | Active |
| TXT (DMARC subdomain) | _dmarc.send.radekkonarski.com | v=DMARC1; p=quarantine; rua=mailto:radieu@gmail.com; sp=quarantine; adkim=r; aspf=r; pct=100 | Active |
| TXT (SPF) | radekkonarski.com | v=spf1 include:_spf.mx.cloudflare.net include:_spf.google.com include:amazonses.com a mx include:_spf.mlsend.com ~all | Active |
| TXT (SPF subdomain) | send.radekkonarski.com | v=spf1 include:amazonses.com ~all | Active |
| TXT (DKIM) | cf2024-1._domainkey.radekkonarski.com | RSA 2048 — Cloudflare managed | Active |
| TXT (DKIM) | resend._domainkey.radekkonarski.com | RSA key — Resend transactional | Active |
Note: DMARC p=quarantine was applied (stronger than the Cloudflare Security Insight finding suggested p=none). The Cloudflare alert for issue #988 was raised 2026-06-18 — the fix was confirmed 2026-06-23.
9.2 Escalating DMARC to p=reject
Wait until DMARC aggregate reports sent to radieu@gmail.com confirm zero failures for 2+ weeks, then:
# Read zone ID from SOPS, update record via Cloudflare API
$env:SOPS_AGE_KEY_FILE = "C:\Users\konar\.age\p24-infra-keys.txt"
$secrets = sops --decrypt --input-type dotenv --output-type dotenv secrets\monitoring.env.sops
$zoneId = ($secrets | Select-String "^CF_ZONE_ID_RADEKKONARSKI=").Line.Split("=")[1]
$cfToken = ($secrets | Select-String "^CF_API_TOKEN=").Line.Split("=",2)[1]
$headers = @{ "Authorization" = "Bearer $cfToken"; "Content-Type" = "application/json" }
# Get DMARC record ID
$records = Invoke-RestMethod "https://api.cloudflare.com/client/v4/zones/$zoneId/dns_records?type=TXT&name=_dmarc.radekkonarski.com" -Headers $headers
$recordId = $records.result[0].id
# Update to p=reject
$body = @{ type="TXT"; name="_dmarc.radekkonarski.com"; content="v=DMARC1; p=reject; rua=mailto:radieu@gmail.com; sp=reject; adkim=r; aspf=r; pct=100"; ttl=1 } | ConvertTo-Json
Invoke-RestMethod "https://api.cloudflare.com/client/v4/zones/$zoneId/dns_records/$recordId" -Method PUT -Headers $headers -Body $body9.3 API Token Scope
The zone-scoped CF_API_TOKEN in secrets/monitoring.env.sops covers only zintegrowana.online.
For radekkonarski.com DNS operations use CF_RADEKKONARSKI_DNS_TOKEN — a scoped token with
DNS:Write permissions on the radekkonarski.com zone only. Stored in secrets/monitoring.env.sops
and GH Secret CF_RADEKKONARSKI_DNS_TOKEN. Created 2026-06-27 as part of issue #1835.
CF_GLOBAL_API_KEY scope limitation (Cloudflare architecture constraint):
CF_GLOBAL_API_KEY remains required for token management operations (creating, rotating, and
deleting scoped tokens) because the User:API Tokens:Edit permission is only available via
Global API Key auth — it cannot be granted to a scoped token. This is a Cloudflare design
constraint, not a configuration gap. CF_GLOBAL_API_KEY should not be used for routine DNS ops
on either zone — use the respective scoped tokens instead.