diff --git a/skills/cloudflare/SKILL.md b/skills/cloudflare/SKILL.md index f011253..0db010e 100644 --- a/skills/cloudflare/SKILL.md +++ b/skills/cloudflare/SKILL.md @@ -1,12 +1,16 @@ --- name: cloudflare -description: Comprehensive Cloudflare platform skill covering Workers, Pages, storage (KV, D1, R2), AI (Workers AI, Vectorize, Agents SDK), networking (Tunnel, Spectrum), security (WAF, DDoS), and infrastructure-as-code (Terraform, Pulumi). Use for any Cloudflare development task. +description: Comprehensive Cloudflare platform skill covering Workers, Pages, storage (KV, D1, R2), AI (Workers AI, Vectorize, Agents SDK), networking (Tunnel, Spectrum), security (WAF, DDoS), Zero Trust (Access, Gateway, WARP, DLP), and infrastructure-as-code (Terraform, Pulumi). Use for any Cloudflare development task. references: - workers - pages - d1 - durable-objects - workers-ai + - access + - gateway + - devices + - dlp --- # Cloudflare Platform Skill @@ -25,6 +29,26 @@ Not authenticated? → `references/wrangler/auth.md` - Interactive/local: `wrangler login` (one-time OAuth) - CI/CD: Set `CLOUDFLARE_API_TOKEN` env var +### Zero Trust API Authentication + +For Zero Trust API operations: + +```bash +# Verify API token +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/user/tokens/verify" | jq . + +# Get account ID +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts" | jq '.result[] | {id, name}' +``` + +**Required permissions for Zero Trust:** +- Account > Zero Trust > Edit +- Account > Access: Apps and Policies > Edit +- Account > Zero Trust: Gateway > Edit +- Account > Zero Trust: Tunnels > Edit + ## Quick Decision Trees ### "I need to run code" @@ -94,6 +118,37 @@ Need security? └─ Credential leak detection → waf/ (managed ruleset) ``` +### "I need Zero Trust / secure access" + +``` +Need Zero Trust? +├─ Control who can access apps? +│ ├─ Web application with login page → access/ (self-hosted app) +│ ├─ SaaS application (Okta, GitHub) → access/ (SaaS app) +│ ├─ SSH/RDP/VNC access → access/ (infrastructure app) +│ ├─ Internal service via browser → access/ + tunnel/ +│ └─ API/service-to-service auth → access/ (service tokens) +├─ Filter/inspect traffic? +│ ├─ Block malicious domains (DNS) → gateway/ (DNS policies) +│ ├─ Block categories (gambling, adult) → gateway/ (DNS policies) +│ ├─ Inspect/block HTTP traffic → gateway/ (HTTP policies) +│ └─ Custom allow/block lists → gateway/ (lists) +├─ Connect networks/services? +│ ├─ Expose local service securely → tunnel/ +│ ├─ Connect office network → tunnel/ + devices/ +│ ├─ Private network routing → tunnel/ (private networks) +│ └─ Replace VPN → tunnel/ + devices/ (WARP) +├─ Manage endpoint devices? +│ ├─ Deploy WARP client → devices/ +│ ├─ Device compliance checks → devices/ (posture) +│ ├─ Split tunnel configuration → devices/ (policies) +│ └─ MDM integration → devices/ (posture integrations) +└─ Prevent data leaks? + ├─ Detect PII/credit cards → dlp/ (profiles) + ├─ Custom data patterns → dlp/ (custom entries) + └─ Block file uploads by type → gateway/ (HTTP policies) +``` + ### "I need media/content" ``` @@ -150,6 +205,15 @@ Need IaC? → pulumi/ (Pulumi), terraform/ (Terraform), or api/ (REST API) | AI Gateway | `references/ai-gateway/` | | AI Search | `references/ai-search/` | +### Zero Trust +| Product | Reference | +|---------|-----------| +| Access | `references/access/` | +| Gateway | `references/gateway/` | +| Tunnel | `references/tunnel/` | +| Devices (WARP) | `references/devices/` | +| DLP | `references/dlp/` | + ### Networking & Connectivity | Product | Reference | |---------|-----------| diff --git a/skills/cloudflare/references/access/README.md b/skills/cloudflare/references/access/README.md new file mode 100644 index 0000000..4d95d2d --- /dev/null +++ b/skills/cloudflare/references/access/README.md @@ -0,0 +1,151 @@ +# Cloudflare Access + +Zero Trust application access control. Secure web apps, SSH, RDP, and APIs without a VPN. + +## Overview + +Cloudflare Access provides: +- **Identity-aware proxy** - Authenticate users before they reach your app +- **Multiple app types** - Self-hosted, SaaS, infrastructure (SSH/RDP/VNC) +- **Flexible policies** - Based on identity, device posture, location, and more +- **Service tokens** - Machine-to-machine authentication + +**Architecture**: User → Cloudflare Edge → Access Policy Check → Origin Application + +## Application Types + +| Type | Use Case | Example | +|------|----------|---------| +| `self_hosted` | Web apps you control | Internal dashboard at `app.example.com` | +| `saas` | Third-party SaaS apps | Okta, Salesforce, GitHub | +| `infrastructure` | SSH/RDP/VNC/SMB | Server at `ssh.example.com` | +| `bookmark` | Quick links in App Launcher | Link to external tool | +| `app_launcher` | User portal | Central access hub | + +## Quick Start + +### 1. Create Self-Hosted Application + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/apps" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Internal Dashboard", + "domain": "dashboard.example.com", + "type": "self_hosted", + "session_duration": "24h" + }' +``` + +### 2. Create Policy for Application + +```bash +# Get the app_id from step 1 +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/apps/$APP_ID/policies" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Allow Engineering Team", + "decision": "allow", + "include": [ + {"group": {"id": "GROUP_UUID"}} + ], + "precedence": 1 + }' +``` + +### 3. Connect Application via Tunnel + +```bash +# Create tunnel +cloudflared tunnel create dashboard-tunnel + +# Configure ingress (in config.yml) +# tunnel: +# ingress: +# - hostname: dashboard.example.com +# service: http://localhost:8080 +# - service: http_status:404 + +# Route DNS +cloudflared tunnel route dns dashboard-tunnel dashboard.example.com + +# Run +cloudflared tunnel run dashboard-tunnel +``` + +## Policy Decisions + +| Decision | Effect | +|----------|--------| +| `allow` | Grant access if rules match | +| `deny` | Block access if rules match | +| `bypass` | Skip authentication (public access) | +| `non_identity` | Allow service tokens only | + +## Policy Rules + +Policies use `include`, `exclude`, and `require` arrays: + +```json +{ + "name": "Engineering Access", + "decision": "allow", + "include": [ + {"group": {"id": "engineering-group-uuid"}}, + {"email": {"email": "admin@example.com"}} + ], + "exclude": [ + {"ip_range": {"ip": "192.168.1.0/24"}} + ], + "require": [ + {"device_posture": {"integration_uid": "posture-check-uuid"}} + ] +} +``` + +**Logic:** +- `include` - User must match at least one (OR) +- `require` - User must match all (AND) +- `exclude` - User must not match any (NOT) + +## Common Rule Types + +| Type | Example | Description | +|------|---------|-------------| +| `email` | `{"email": "user@example.com"}` | Specific email | +| `email_domain` | `{"email_domain": "example.com"}` | Email domain | +| `group` | `{"id": "uuid"}` | Access Group | +| `everyone` | `{}` | All authenticated users | +| `ip_range` | `{"ip": "10.0.0.0/8"}` | IP/CIDR range | +| `country` | `{"code": "US"}` | Country code | +| `device_posture` | `{"integration_uid": "uuid"}` | Posture check | +| `service_token` | `{"id": "uuid"}` | Service token | +| `certificate` | `{}` | mTLS certificate | + +## Session Duration + +```json +{ + "session_duration": "24h", // How long before re-auth + "same_site_cookie_attribute": "lax" +} +``` + +**Values:** `30m`, `1h`, `6h`, `12h`, `24h`, `168h` (1 week) + +## In This Reference + +- [Policies](./policies.md) - Policy configuration details +- [Groups](./groups.md) - Access Groups for rule reuse +- [Identity Providers](./identity-providers.md) - IdP configuration +- [Service Tokens](./service-tokens.md) - Machine authentication +- [API](./api.md) - Full API reference +- [Gotchas](./gotchas.md) - Common issues and limits + +## See Also + +- [Gateway](../gateway/README.md) - DNS/HTTP filtering +- [Tunnel](../tunnel/README.md) - Secure connectivity +- [Devices](../devices/README.md) - Device posture integration diff --git a/skills/cloudflare/references/access/api.md b/skills/cloudflare/references/access/api.md new file mode 100644 index 0000000..6706f4b --- /dev/null +++ b/skills/cloudflare/references/access/api.md @@ -0,0 +1,338 @@ +# Access API Reference + +Base URL: `https://api.cloudflare.com/client/v4/accounts/{account_id}` + +## Applications + +### List Applications + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/apps" | jq '.result' +``` + +### Get Application + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/apps/$APP_ID" +``` + +### Create Application + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/apps" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "App Name", + "domain": "app.example.com", + "type": "self_hosted", + "session_duration": "24h", + "auto_redirect_to_identity": false, + "app_launcher_visible": true, + "logo_url": "https://example.com/logo.png", + "allowed_idps": ["IDP_UUID"], + "cors_headers": { + "allowed_methods": ["GET", "POST"], + "allowed_origins": ["https://example.com"], + "allow_credentials": true + } + }' +``` + +### Update Application + +```bash +curl -X PUT "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/apps/$APP_ID" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Updated Name", + "session_duration": "12h" + }' +``` + +### Delete Application + +```bash +curl -X DELETE "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/apps/$APP_ID" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" +``` + +### Revoke Application Tokens + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/apps/$APP_ID/revoke_tokens" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" +``` + +## Policies + +### List Policies for Application + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/apps/$APP_ID/policies" +``` + +### Create Policy + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/apps/$APP_ID/policies" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Allow Engineering", + "decision": "allow", + "precedence": 1, + "include": [ + {"group": {"id": "GROUP_UUID"}} + ], + "exclude": [], + "require": [] + }' +``` + +### Update Policy + +```bash +curl -X PUT "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/apps/$APP_ID/policies/$POLICY_ID" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Updated Policy", + "decision": "allow", + "precedence": 1, + "include": [ + {"email_domain": {"domain": "example.com"}} + ] + }' +``` + +### Delete Policy + +```bash +curl -X DELETE "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/apps/$APP_ID/policies/$POLICY_ID" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" +``` + +## Reusable Policies + +### List Reusable Policies + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/policies" +``` + +### Create Reusable Policy + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/policies" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Corporate Users", + "decision": "allow", + "include": [ + {"email_domain": {"domain": "company.com"}} + ] + }' +``` + +## Groups + +### List Groups + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/groups" +``` + +### Create Group + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/groups" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Engineering Team", + "include": [ + {"email_domain": {"domain": "eng.example.com"}}, + {"email": {"email": "contractor@example.com"}} + ], + "exclude": [], + "require": [] + }' +``` + +### Update Group + +```bash +curl -X PUT "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/groups/$GROUP_ID" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Updated Group Name", + "include": [{"email_domain": {"domain": "example.com"}}] + }' +``` + +### Delete Group + +```bash +curl -X DELETE "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/groups/$GROUP_ID" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" +``` + +## Identity Providers + +### List Identity Providers + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/identity_providers" +``` + +### Create Identity Provider (Okta Example) + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/identity_providers" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Okta", + "type": "okta", + "config": { + "client_id": "YOUR_CLIENT_ID", + "client_secret": "YOUR_CLIENT_SECRET", + "okta_account": "https://your-org.okta.com" + } + }' +``` + +### Delete Identity Provider + +```bash +curl -X DELETE "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/identity_providers/$IDP_ID" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" +``` + +## Service Tokens + +### List Service Tokens + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/service_tokens" +``` + +### Create Service Token + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/service_tokens" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "CI/CD Token", + "duration": "8760h" + }' +``` + +**Response includes `client_id` and `client_secret` - save these immediately!** + +### Rotate Service Token + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/service_tokens/$TOKEN_ID/rotate" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" +``` + +### Delete Service Token + +```bash +curl -X DELETE "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/service_tokens/$TOKEN_ID" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" +``` + +## Users & Sessions + +### List Access Users + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/users" +``` + +### Get User's Active Sessions + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/users/$USER_ID/active_sessions" +``` + +### Get User's Failed Logins + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/users/$USER_ID/failed_logins" +``` + +### Revoke User's Sessions + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/organizations/revoke_user" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"email": "user@example.com"}' +``` + +## Access Logs + +### Get Access Request Logs + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/logs/access_requests?limit=50" +``` + +## Certificates + +### List mTLS Certificates + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/certificates" +``` + +### Upload mTLS Certificate + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/certificates" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Client CA", + "certificate": "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----", + "associated_hostnames": ["app.example.com"] + }' +``` + +## Tags + +### List Tags + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/tags" +``` + +### Create Tag + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/tags" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"name": "production"}' +``` diff --git a/skills/cloudflare/references/access/gotchas.md b/skills/cloudflare/references/access/gotchas.md new file mode 100644 index 0000000..d141da4 --- /dev/null +++ b/skills/cloudflare/references/access/gotchas.md @@ -0,0 +1,191 @@ +# Access Gotchas + +Common issues, limitations, and troubleshooting for Cloudflare Access. + +## Common Errors + +### "Access Denied" Despite Correct Policy + +**Symptoms:** User matches policy rules but still gets denied + +**Causes & Solutions:** + +1. **Policy precedence** - Lower number = higher priority. A `deny` policy with precedence 1 blocks before `allow` with precedence 2. + ```bash + # Check policy order + curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/apps/$APP_ID/policies" | \ + jq '.result | sort_by(.precedence) | .[] | {name, decision, precedence}' + ``` + +2. **`require` rules not met** - User matches `include` but fails `require` (e.g., device posture) + ```bash + # Check user's failed logins for details + curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/users/$USER_ID/failed_logins" + ``` + +3. **Group membership not synced** - IdP group changes take time to propagate + - Wait 5-15 minutes for SCIM sync + - User can force refresh by logging out and back in + +4. **Browser cached old session** - Clear cookies for `*.cloudflareaccess.com` + +### "Application Not Found" + +**Causes:** +- Domain DNS not proxied through Cloudflare (orange cloud) +- Application domain doesn't match exactly (www vs non-www) +- Tunnel not connected for self-hosted apps + +**Solution:** +```bash +# Verify DNS is proxied +dig +short app.example.com # Should return Cloudflare IPs + +# Check tunnel status +cloudflared tunnel info my-tunnel +``` + +### "Invalid Service Token" + +**Causes:** +- Token expired (default: 1 year) +- Token revoked or deleted +- Wrong `CF-Access-Client-Id` or `CF-Access-Client-Secret` headers + +**Using service tokens:** +```bash +curl -H "CF-Access-Client-Id: $CLIENT_ID" \ + -H "CF-Access-Client-Secret: $CLIENT_SECRET" \ + https://app.example.com/api +``` + +### "Session Expired Frequently" + +**Cause:** `session_duration` set too low + +**Solution:** Increase session duration (max 720h = 30 days) +```bash +curl -X PUT "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/apps/$APP_ID" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"session_duration": "168h"}' # 1 week +``` + +### "CORS Errors" + +**Cause:** Access login page triggers CORS issues for API requests + +**Solutions:** + +1. **For browser apps** - Configure CORS headers on the Access app: + ```json + { + "cors_headers": { + "allowed_methods": ["GET", "POST", "PUT", "DELETE", "OPTIONS"], + "allowed_origins": ["https://your-frontend.com"], + "allow_credentials": true, + "max_age": 86400 + } + } + ``` + +2. **For API clients** - Use service tokens instead of browser auth + +3. **For SPA with API** - Use `same_site_cookie_attribute: "none"` with `secure: true` + +### "IdP Login Loop" + +**Symptoms:** Redirects between IdP and Access endlessly + +**Causes:** +- Mismatched redirect URIs in IdP config +- Multiple IdPs with same email domain +- Cookie issues in browser + +**Solutions:** +1. Verify callback URL in IdP matches exactly: `https://.cloudflareaccess.com/cdn-cgi/access/callback` +2. If using multiple IdPs, set `auto_redirect_to_identity: false` on apps +3. Clear all cookies and try incognito mode + +### "Device Posture Check Failed" + +**Cause:** User's device doesn't meet posture requirements + +**Debug:** +```bash +# Check posture rules +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/devices/posture" + +# Check user's device status +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/devices?search=user@example.com" +``` + +**Common posture issues:** +- WARP client not connected +- Antivirus not running +- Disk encryption disabled +- OS version too old + +## Limits + +| Limit | Value | +|-------|-------| +| Applications per account | 500 | +| Policies per application | 50 | +| Groups per account | 300 | +| Rules per policy/group | 100 | +| Service tokens per account | 500 | +| Identity providers | 50 | +| Session duration max | 720h (30 days) | +| mTLS certificates | 50 | + +## Performance Considerations + +### Cold Start Latency + +First request after policy change may be slower (< 500ms typically). + +### IdP Roundtrip + +Initial login adds IdP latency (varies by provider). Subsequent requests use cached session. + +### Global Propagation + +Policy changes propagate globally within 60 seconds. + +## Debugging + +### Check User's Identity + +```bash +# What Access sees for a user +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/users/$USER_ID/last_seen_identity" | jq . +``` + +### Access Request Logs + +```bash +# Recent access attempts +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/logs/access_requests?limit=100" | \ + jq '.result[] | {user_email, app_domain, action, created_at}' +``` + +### Test Policy Evaluation + +```bash +# Test which policy matches for a user +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/apps/$APP_ID/user_policy_checks" +``` + +## See Also + +- [Policies](./policies.md) - Policy configuration details +- [Groups](./groups.md) - Access Groups +- [Devices Gotchas](../devices/gotchas.md) - Device posture issues diff --git a/skills/cloudflare/references/access/groups.md b/skills/cloudflare/references/access/groups.md new file mode 100644 index 0000000..fc23fe9 --- /dev/null +++ b/skills/cloudflare/references/access/groups.md @@ -0,0 +1,224 @@ +# Access Groups + +Reusable collections of rules for Access policies. Define once, use across multiple applications. + +## Overview + +Access Groups allow you to: +- Define user/device criteria once and reuse across applications +- Simplify policy management at scale +- Sync with identity provider groups (Okta, Azure AD, etc.) + +## Group Structure + +```json +{ + "name": "Engineering Team", + "include": [ + {"email_domain": {"domain": "eng.example.com"}}, + {"email": {"email": "contractor@external.com"}} + ], + "exclude": [ + {"email": {"email": "former-employee@eng.example.com"}} + ], + "require": [ + {"device_posture": {"integration_uid": "posture-check-uuid"}} + ] +} +``` + +**Logic:** +- `include` - Must match at least one (OR) +- `exclude` - Must NOT match any +- `require` - Must match ALL (AND) + +## Rule Types + +### Identity-Based Rules + +```json +// Specific email +{"email": {"email": "user@example.com"}} + +// Email domain +{"email_domain": {"domain": "example.com"}} + +// Everyone (any authenticated user) +{"everyone": {}} + +// Identity Provider Group (from IdP) +{"saml": {"attribute_name": "groups", "attribute_value": "Engineering"}} + +// Azure AD Group +{"azure_ad": {"id": "group-object-id", "identity_provider_id": "idp-uuid"}} + +// GitHub Organization +{"github_organization": {"name": "my-org", "identity_provider_id": "idp-uuid"}} + +// GitHub Team +{"github_organization": {"name": "my-org", "team": "engineering", "identity_provider_id": "idp-uuid"}} + +// Okta Group +{"okta": {"name": "Engineering", "identity_provider_id": "idp-uuid"}} + +// Google Workspace Group +{"gsuite": {"email": "group@example.com", "identity_provider_id": "idp-uuid"}} +``` + +### Network-Based Rules + +```json +// IP Range (CIDR) +{"ip_range": {"ip": "10.0.0.0/8"}} + +// Country +{"country": {"code": "US"}} + +// Multiple countries +{"geo": {"country_code": "US"}} +``` + +### Device-Based Rules + +```json +// Device posture check +{"device_posture": {"integration_uid": "posture-rule-uuid"}} + +// Managed device (WARP) +{"warp": {}} + +// Gateway managed +{"gateway": {}} +``` + +### Certificate-Based Rules + +```json +// mTLS certificate +{"certificate": {}} + +// Specific certificate CN +{"common_name": {"common_name": "device-001.example.com"}} +``` + +### Service Token Rules + +```json +// Any service token +{"service_token": {}} + +// Specific service token +{"service_token": {"token_id": "token-uuid"}} +``` + +## Common Patterns + +### Corporate Users + +```json +{ + "name": "Corporate Users", + "include": [ + {"email_domain": {"domain": "company.com"}} + ], + "require": [ + {"warp": {}} + ] +} +``` + +### Engineering Team (Okta Synced) + +```json +{ + "name": "Engineering", + "include": [ + {"okta": {"name": "Engineering", "identity_provider_id": "IDP_UUID"}} + ] +} +``` + +### Contractors with Device Posture + +```json +{ + "name": "Contractors", + "include": [ + {"email_domain": {"domain": "contractor.com"}}, + {"email": {"email": "special@external.com"}} + ], + "require": [ + {"device_posture": {"integration_uid": "antivirus-check-uuid"}}, + {"device_posture": {"integration_uid": "disk-encryption-uuid"}} + ] +} +``` + +### Regional Access + +```json +{ + "name": "US Employees", + "include": [ + {"email_domain": {"domain": "company.com"}} + ], + "require": [ + {"country": {"code": "US"}} + ] +} +``` + +### Service Accounts + +```json +{ + "name": "Service Accounts", + "include": [ + {"service_token": {}} + ] +} +``` + +## Using Groups in Policies + +Once created, reference groups in application policies: + +```json +{ + "name": "Allow Engineering", + "decision": "allow", + "include": [ + {"group": {"id": "engineering-group-uuid"}} + ] +} +``` + +## IdP Group Sync + +### Okta SCIM Sync + +Groups from Okta can be automatically synced: + +```bash +# View synced groups from IdP +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/identity_providers/$IDP_ID/scim/groups" +``` + +### Azure AD Sync + +Enable SCIM provisioning in Azure AD to sync groups automatically. + +## Best Practices + +1. **Use IdP groups when possible** - Sync from Okta/Azure AD for single source of truth +2. **Layer groups** - Create broad groups (all employees) and specific groups (engineering) +3. **Combine with device posture** - Add `require` rules for sensitive applications +4. **Name clearly** - Use descriptive names like "Engineering - Production Access" +5. **Document membership** - Keep track of who manages group membership in IdP + +## Limitations + +- Maximum 100 rules per group +- Group names must be unique within account +- Changes propagate within 60 seconds diff --git a/skills/cloudflare/references/access/identity-providers.md b/skills/cloudflare/references/access/identity-providers.md new file mode 100644 index 0000000..8b6c73a --- /dev/null +++ b/skills/cloudflare/references/access/identity-providers.md @@ -0,0 +1,234 @@ +# Identity Providers + +Configure authentication sources for Cloudflare Access. + +## Supported Providers + +| Provider | Type | SCIM Support | +|----------|------|--------------| +| Okta | `okta` | Yes | +| Azure AD | `azure` | Yes | +| Google Workspace | `google` | No | +| GitHub | `github` | No | +| OneLogin | `onelogin` | Yes | +| Ping Identity | `ping` | No | +| SAML 2.0 (Generic) | `saml` | Varies | +| OIDC (Generic) | `oidc` | No | +| One-Time PIN | `onetimepin` | N/A | +| LinkedIn | `linkedin` | No | +| Facebook | `facebook` | No | + +## Okta Configuration + +### 1. Create OIDC App in Okta + +In Okta Admin Console: +1. Applications → Create App Integration +2. Sign-in method: OIDC +3. Application type: Web Application +4. Sign-in redirect URI: `https://.cloudflareaccess.com/cdn-cgi/access/callback` +5. Sign-out redirect URI: `https://.cloudflareaccess.com` + +### 2. Configure in Cloudflare + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/identity_providers" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Okta", + "type": "okta", + "config": { + "client_id": "YOUR_OKTA_CLIENT_ID", + "client_secret": "YOUR_OKTA_CLIENT_SECRET", + "okta_account": "https://your-org.okta.com", + "claims": ["groups", "email", "name"], + "email_claim_name": "email" + } + }' +``` + +### 3. Enable SCIM Provisioning (Optional) + +In Okta: Applications → Your App → Provisioning → Enable SCIM + +Cloudflare SCIM endpoint: `https://your-team.cloudflareaccess.com/scim/v2` + +## Azure AD Configuration + +### 1. Create App Registration in Azure + +1. Azure Portal → App registrations → New registration +2. Redirect URI: `https://.cloudflareaccess.com/cdn-cgi/access/callback` +3. Certificates & secrets → New client secret +4. API permissions → Add `openid`, `email`, `profile`, `User.Read` + +### 2. Configure in Cloudflare + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/identity_providers" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Azure AD", + "type": "azure", + "config": { + "client_id": "YOUR_AZURE_APP_ID", + "client_secret": "YOUR_AZURE_CLIENT_SECRET", + "directory_id": "YOUR_AZURE_TENANT_ID", + "support_groups": true + } + }' +``` + +## Google Workspace Configuration + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/identity_providers" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Google Workspace", + "type": "google", + "config": { + "client_id": "YOUR_GOOGLE_CLIENT_ID", + "client_secret": "YOUR_GOOGLE_CLIENT_SECRET" + } + }' +``` + +## GitHub Configuration + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/identity_providers" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "GitHub", + "type": "github", + "config": { + "client_id": "YOUR_GITHUB_OAUTH_CLIENT_ID", + "client_secret": "YOUR_GITHUB_OAUTH_CLIENT_SECRET" + } + }' +``` + +**Using GitHub in policies:** +```json +{ + "include": [ + {"github_organization": {"name": "my-org", "identity_provider_id": "idp-uuid"}} + ] +} +``` + +## Generic SAML 2.0 + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/identity_providers" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Corporate SAML", + "type": "saml", + "config": { + "issuer_url": "https://idp.company.com", + "sso_target_url": "https://idp.company.com/sso/saml", + "idp_public_cert": "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----", + "sign_request": true, + "attributes": ["email", "name", "groups"], + "email_attribute_name": "email" + } + }' +``` + +**Cloudflare SP Metadata:** +- Entity ID: `https://.cloudflareaccess.com` +- ACS URL: `https://.cloudflareaccess.com/cdn-cgi/access/callback` + +## Generic OIDC + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/identity_providers" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Custom OIDC", + "type": "oidc", + "config": { + "client_id": "YOUR_CLIENT_ID", + "client_secret": "YOUR_CLIENT_SECRET", + "auth_url": "https://idp.example.com/authorize", + "token_url": "https://idp.example.com/token", + "certs_url": "https://idp.example.com/.well-known/jwks.json", + "scopes": ["openid", "email", "profile"], + "claims": ["email", "name", "groups"] + } + }' +``` + +## One-Time PIN (Email OTP) + +No IdP required - users receive a code via email: + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/identity_providers" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Email OTP", + "type": "onetimepin" + }' +``` + +## Multiple IdPs + +You can configure multiple identity providers. Users will see a selection screen unless: +- `auto_redirect_to_identity: true` on the app (redirects to first IdP) +- `allowed_idps` specified on the app (limits choices) + +```json +{ + "name": "Internal App", + "domain": "app.example.com", + "allowed_idps": ["okta-idp-uuid", "azure-idp-uuid"] +} +``` + +## SCIM User/Group Sync + +### View Synced Users + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/identity_providers/$IDP_ID/scim/users" +``` + +### View Synced Groups + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/identity_providers/$IDP_ID/scim/groups" +``` + +## Troubleshooting + +### Common Issues + +1. **Redirect URI mismatch** - Ensure exact match including trailing slash +2. **Invalid client secret** - Regenerate and update +3. **Missing scopes** - Add required scopes in IdP app config +4. **SAML signature verification failed** - Update IdP certificate + +### Test IdP Connection + +```bash +# Get IdP details +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/identity_providers/$IDP_ID" +``` + +## See Also + +- [Groups](./groups.md) - Using IdP groups in policies +- [Gotchas](./gotchas.md) - Common IdP issues diff --git a/skills/cloudflare/references/access/policies.md b/skills/cloudflare/references/access/policies.md new file mode 100644 index 0000000..886836f --- /dev/null +++ b/skills/cloudflare/references/access/policies.md @@ -0,0 +1,266 @@ +# Access Policies + +Control who can access applications and under what conditions. + +## Overview + +Policies define access rules for applications: +- **Application-specific policies** - Attached to individual apps +- **Reusable policies** - Shared across multiple applications +- **Precedence ordering** - Lower number = higher priority + +## Policy Structure + +```json +{ + "name": "Allow Engineering", + "decision": "allow", + "precedence": 1, + "include": [ + {"group": {"id": "engineering-group-uuid"}} + ], + "exclude": [ + {"email": {"email": "contractor@example.com"}} + ], + "require": [ + {"warp": {}}, + {"device_posture": {"integration_uid": "antivirus-uuid"}} + ], + "purpose_justification_required": false, + "purpose_justification_prompt": "", + "approval_required": false, + "approval_groups": [] +} +``` + +## Decisions + +| Decision | Effect | +|----------|--------| +| `allow` | Grant access if rules match | +| `deny` | Block access if rules match | +| `bypass` | Skip authentication entirely | +| `non_identity` | Service tokens only (no user auth) | + +## Rule Logic + +``` +Final Decision = (matches include) AND (matches all require) AND NOT (matches any exclude) +``` + +- **include** - User must match at least ONE rule (OR logic) +- **require** - User must match ALL rules (AND logic) +- **exclude** - User must NOT match ANY rule + +## Rule Examples + +### Allow by Email Domain + +```json +{ + "name": "Company Employees", + "decision": "allow", + "include": [ + {"email_domain": {"domain": "company.com"}} + ] +} +``` + +### Allow Specific Users + +```json +{ + "name": "Admins", + "decision": "allow", + "include": [ + {"email": {"email": "admin@company.com"}}, + {"email": {"email": "backup-admin@company.com"}} + ] +} +``` + +### Allow Group with Device Requirements + +```json +{ + "name": "Engineering with Secure Device", + "decision": "allow", + "include": [ + {"group": {"id": "engineering-group-uuid"}} + ], + "require": [ + {"warp": {}}, + {"device_posture": {"integration_uid": "disk-encryption-uuid"}} + ] +} +``` + +### Block Specific Countries + +```json +{ + "name": "Block Sanctioned Countries", + "decision": "deny", + "precedence": 1, + "include": [ + {"country": {"code": "KP"}}, + {"country": {"code": "IR"}}, + {"country": {"code": "CU"}} + ] +} +``` + +### Service Token Access + +```json +{ + "name": "API Access", + "decision": "non_identity", + "include": [ + {"service_token": {"token_id": "token-uuid"}} + ] +} +``` + +### Public Access (Bypass) + +```json +{ + "name": "Public Endpoints", + "decision": "bypass", + "include": [ + {"everyone": {}} + ] +} +``` + +## Precedence + +Policies are evaluated in order of precedence (lowest number first): + +``` +1. Block Sanctioned Countries (deny, precedence: 1) +2. Allow Admins (allow, precedence: 2) +3. Allow Engineering (allow, precedence: 3) +4. Allow Everyone Else (allow, precedence: 10) +``` + +**Best practice:** Use gaps in precedence (1, 10, 20) to allow inserting policies later. + +## Purpose Justification + +Require users to explain why they need access: + +```json +{ + "name": "Sensitive Data Access", + "decision": "allow", + "include": [{"group": {"id": "data-team-uuid"}}], + "purpose_justification_required": true, + "purpose_justification_prompt": "Please explain why you need access to this data" +} +``` + +## Approval Workflows + +Require manager/admin approval before granting access: + +```json +{ + "name": "Production Access with Approval", + "decision": "allow", + "include": [{"group": {"id": "engineers-uuid"}}], + "approval_required": true, + "approval_groups": [ + { + "email_list_uuid": "approvers-uuid", + "approvals_needed": 1 + } + ] +} +``` + +## Reusable Policies + +Create policies that can be attached to multiple apps: + +```bash +# Create reusable policy +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/policies" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Standard Corporate Access", + "decision": "allow", + "include": [{"email_domain": {"domain": "company.com"}}], + "require": [{"warp": {}}] + }' + +# List reusable policies +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/policies" +``` + +## Common Patterns + +### Tiered Access + +``` +App: Production Dashboard +├─ Policy 1 (precedence: 1): Block non-US (deny) +├─ Policy 2 (precedence: 2): Allow SRE team (allow) +└─ Policy 3 (precedence: 10): Allow viewers read-only (allow, different app path) +``` + +### Contractor Access + +```json +{ + "name": "Contractors", + "decision": "allow", + "include": [ + {"email_domain": {"domain": "contractor-company.com"}} + ], + "require": [ + {"device_posture": {"integration_uid": "managed-device-uuid"}}, + {"country": {"code": "US"}} + ], + "exclude": [ + {"email": {"email": "terminated@contractor-company.com"}} + ] +} +``` + +### Time-Limited Access + +Use service tokens with short duration for temporary access: + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/service_tokens" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"name": "Temp Contractor Access", "duration": "24h"}' +``` + +## Policy Testing + +### Test Policy Evaluation + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/policy-tests" \ + -d '{"email": "user@example.com", "app_id": "app-uuid"}' +``` + +### View User's Effective Access + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/apps/$APP_ID/user_policy_checks" +``` + +## See Also + +- [Groups](./groups.md) - Reusable rule collections +- [Service Tokens](./service-tokens.md) - Machine authentication +- [Gotchas](./gotchas.md) - Common policy issues diff --git a/skills/cloudflare/references/access/service-tokens.md b/skills/cloudflare/references/access/service-tokens.md new file mode 100644 index 0000000..51c3c32 --- /dev/null +++ b/skills/cloudflare/references/access/service-tokens.md @@ -0,0 +1,228 @@ +# Service Tokens + +Machine-to-machine authentication for APIs and automated systems. + +## Overview + +Service tokens provide: +- **Non-interactive authentication** - No user login required +- **API access** - Authenticate automated systems +- **Time-limited access** - Configurable expiration +- **Rotatable credentials** - Refresh without downtime + +## Creating Service Tokens + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/service_tokens" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "CI/CD Pipeline", + "duration": "8760h" + }' +``` + +**Response (save immediately!):** +```json +{ + "result": { + "id": "token-uuid", + "name": "CI/CD Pipeline", + "client_id": "abc123.access", + "client_secret": "secret-value-only-shown-once", + "expires_at": "2025-01-15T00:00:00Z" + } +} +``` + +**Duration options:** `8760h` (1 year), `17520h` (2 years), `26280h` (3 years), `forever` + +## Using Service Tokens + +### HTTP Headers + +```bash +curl -H "CF-Access-Client-Id: abc123.access" \ + -H "CF-Access-Client-Secret: secret-value" \ + https://app.example.com/api/data +``` + +### In Code (Node.js) + +```javascript +const response = await fetch('https://app.example.com/api/data', { + headers: { + 'CF-Access-Client-Id': process.env.CF_ACCESS_CLIENT_ID, + 'CF-Access-Client-Secret': process.env.CF_ACCESS_CLIENT_SECRET + } +}); +``` + +### In Code (Python) + +```python +import requests + +response = requests.get( + 'https://app.example.com/api/data', + headers={ + 'CF-Access-Client-Id': os.environ['CF_ACCESS_CLIENT_ID'], + 'CF-Access-Client-Secret': os.environ['CF_ACCESS_CLIENT_SECRET'] + } +) +``` + +## Configuring Application Policies + +### Allow Only Service Tokens + +```json +{ + "name": "API-Only Access", + "decision": "non_identity", + "include": [ + {"service_token": {}} + ] +} +``` + +### Allow Specific Token + +```json +{ + "name": "CI/CD Pipeline Only", + "decision": "non_identity", + "include": [ + {"service_token": {"token_id": "token-uuid"}} + ] +} +``` + +### Allow Both Users and Service Tokens + +```json +{ + "name": "Users and Services", + "decision": "allow", + "include": [ + {"group": {"id": "developers-uuid"}}, + {"service_token": {"token_id": "cicd-token-uuid"}} + ] +} +``` + +## Token Management + +### List All Tokens + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/service_tokens" | \ + jq '.result[] | {id, name, expires_at}' +``` + +### Rotate Token (Get New Secret) + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/service_tokens/$TOKEN_ID/rotate" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" +``` + +**Important:** Rotation generates a new secret. Old secret remains valid until the new one is used, allowing graceful rotation. + +### Refresh Token (Extend Expiration) + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/service_tokens/$TOKEN_ID/refresh" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" +``` + +### Delete Token + +```bash +curl -X DELETE "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/service_tokens/$TOKEN_ID" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" +``` + +## Best Practices + +### 1. Use Descriptive Names + +```bash +# Good +{"name": "GitHub Actions - Production Deploy"} +{"name": "Jenkins - Staging Tests"} + +# Bad +{"name": "token1"} +{"name": "temp"} +``` + +### 2. Scope Tokens to Specific Apps + +Create separate tokens for different services/applications rather than one "master" token. + +### 3. Set Appropriate Expiration + +- CI/CD pipelines: 1 year with automated rotation +- Short-term access: Match project duration +- Critical infrastructure: Use `forever` with regular audits + +### 4. Store Securely + +- Use secrets managers (Vault, AWS Secrets Manager, 1Password) +- Never commit to source control +- Use environment variables in CI/CD + +### 5. Implement Rotation + +Automate token rotation before expiration: + +```bash +#!/bin/bash +# Rotate token and update secrets manager +NEW_SECRET=$(curl -X POST ".../service_tokens/$TOKEN_ID/rotate" ... | jq -r '.result.client_secret') +# Update your secrets manager with $NEW_SECRET +``` + +## Monitoring Token Usage + +### Check Token Last Used + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/service_tokens/$TOKEN_ID" | \ + jq '{name, last_seen_at, expires_at}' +``` + +### Audit Token Access in Logs + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/logs/access_requests?limit=100" | \ + jq '.result[] | select(.is_service_token == true)' +``` + +## Terraform Example + +```hcl +resource "cloudflare_zero_trust_access_service_token" "cicd" { + account_id = var.cloudflare_account_id + name = "GitHub Actions" + duration = "8760h" +} + +output "client_id" { + value = cloudflare_zero_trust_access_service_token.cicd.client_id +} + +output "client_secret" { + value = cloudflare_zero_trust_access_service_token.cicd.client_secret + sensitive = true +} +``` + +## See Also + +- [Policies](./policies.md) - Configuring service token policies +- [API](./api.md) - Full API reference diff --git a/skills/cloudflare/references/devices/README.md b/skills/cloudflare/references/devices/README.md new file mode 100644 index 0000000..039ff0e --- /dev/null +++ b/skills/cloudflare/references/devices/README.md @@ -0,0 +1,176 @@ +# Cloudflare Devices (WARP) + +Endpoint security with the WARP client for DNS filtering, secure web gateway, and private network access. + +## Overview + +WARP client provides: +- **DNS filtering** - Gateway DNS policies on device +- **Secure Web Gateway** - HTTP/HTTPS inspection and filtering +- **Private network access** - Access resources via Tunnel +- **Device posture** - Verify device security state +- **Split tunneling** - Control what traffic goes through WARP + +## WARP Modes + +| Mode | DNS | HTTP | Private Networks | Use Case | +|------|-----|------|------------------|----------| +| Gateway with WARP | Yes | Yes | Yes | Full protection | +| Gateway with DoH | Yes | No | No | DNS filtering only | +| WARP (Consumer) | No | No | No | Consumer privacy VPN | + +## Quick Start + +### 1. Deploy WARP Client + +**macOS:** +```bash +brew install --cask cloudflare-warp +``` + +**Windows:** Download from https://developers.cloudflare.com/warp-client/get-started/windows/ + +**Linux:** +```bash +curl -fsSL https://pkg.cloudflareclient.com/pubkey.gpg | sudo gpg --yes --dearmor -o /usr/share/keyrings/cloudflare-warp-archive-keyring.gpg +echo "deb [signed-by=/usr/share/keyrings/cloudflare-warp-archive-keyring.gpg] https://pkg.cloudflareclient.com/ jammy main" | sudo tee /etc/apt/sources.list.d/cloudflare-client.list +sudo apt update && sudo apt install cloudflare-warp +``` + +### 2. Enroll Device + +**User-initiated (recommended):** +1. Open WARP client +2. Click Settings → Account → Login with Cloudflare for Teams +3. Enter organization name: `your-org.cloudflareaccess.com` + +**MDM deployment:** +Use `mdm.xml` configuration file with organization settings. + +### 3. Verify Connection + +```bash +warp-cli status +# Expected: Status: Connected, Mode: Gateway with WARP +``` + +## Device Enrollment + +### Authentication Options + +1. **Identity Provider** - User logs in via Okta, Azure AD, etc. +2. **One-Time PIN** - Email-based verification +3. **Service Token** - Automated deployment +4. **Device Certificate** - mTLS enrollment + +### Enrollment Policy + +Configure in: Zero Trust → Settings → WARP Client → Device Enrollment + +```json +{ + "enrollment": { + "auth_enabled": true, + "allowed_identity_providers": ["okta-idp-uuid"], + "rule": { + "include": [ + {"email_domain": {"domain": "company.com"}} + ] + } + } +} +``` + +## Split Tunneling + +### Include Mode (Recommended) + +Only specified traffic goes through WARP: + +```json +{ + "include": [ + {"address": "10.0.0.0/8"}, + {"address": "172.16.0.0/12"}, + {"host": "internal.company.com"} + ] +} +``` + +### Exclude Mode + +All traffic goes through WARP except specified: + +```json +{ + "exclude": [ + {"address": "192.168.1.0/24"}, + {"host": "local-only.com"} + ] +} +``` + +## CLI Commands + +```bash +# Status +warp-cli status + +# Connect/Disconnect +warp-cli connect +warp-cli disconnect + +# Registration +warp-cli registration show +warp-cli registration delete # For re-enrollment + +# Settings +warp-cli settings + +# Switch modes +warp-cli mode warp+doh # Gateway with WARP +warp-cli mode doh # Gateway with DoH only +warp-cli mode warp # Consumer WARP + +# Diagnostics +warp-cli debug qlog # Enable QUIC logging +warp-cli debug connectivity # Test connectivity +``` + +## Device Management API + +### List Devices + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/devices" | \ + jq '.result[] | {id, name, user_email, last_seen}' +``` + +### Get Device + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/devices/$DEVICE_ID" +``` + +### Revoke Device + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/devices/$DEVICE_ID/revoke" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" +``` + +## In This Reference + +- [WARP](./warp.md) - WARP client configuration +- [Posture](./posture.md) - Device posture checks +- [Policies](./policies.md) - Device settings policies +- [API](./api.md) - Full API reference +- [Gotchas](./gotchas.md) - Common issues + +## See Also + +- [Gateway](../gateway/README.md) - DNS and HTTP filtering +- [Tunnel](../tunnel/README.md) - Private network access +- [Access](../access/README.md) - Device posture in access policies diff --git a/skills/cloudflare/references/devices/api.md b/skills/cloudflare/references/devices/api.md new file mode 100644 index 0000000..cf91f6d --- /dev/null +++ b/skills/cloudflare/references/devices/api.md @@ -0,0 +1,315 @@ +# Devices API Reference + +Base URL: `https://api.cloudflare.com/client/v4/accounts/{account_id}` + +## Devices + +### List All Devices + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/devices" | jq '.result' +``` + +### Search Devices by User + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/devices?search=user@example.com" +``` + +### Get Device Details + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/devices/$DEVICE_ID" +``` + +### Revoke Device + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/devices/$DEVICE_ID/revoke" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" +``` + +### List Physical Devices + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/devices/physical-devices" +``` + +## Device Posture + +### List Posture Rules + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/devices/posture" | jq '.result' +``` + +### Create Posture Rule + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/devices/posture" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Disk Encryption", + "type": "disk_encryption", + "description": "Requires full disk encryption", + "schedule": "5m", + "match": [ + {"platform": "mac"}, + {"platform": "windows"} + ], + "input": { + "require_all": true + } + }' +``` + +### Update Posture Rule + +```bash +curl -X PUT "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/devices/posture/$RULE_ID" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Updated Name", + "schedule": "15m" + }' +``` + +### Delete Posture Rule + +```bash +curl -X DELETE "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/devices/posture/$RULE_ID" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" +``` + +## Posture Integrations + +### List Integrations + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/devices/posture/integration" +``` + +### Create Integration + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/devices/posture/integration" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "CrowdStrike", + "type": "crowdstrike", + "config": { + "api_url": "https://api.crowdstrike.com", + "client_id": "YOUR_CLIENT_ID", + "client_secret": "YOUR_CLIENT_SECRET" + } + }' +``` + +### Update Integration + +```bash +curl -X PATCH "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/devices/posture/integration/$INTEGRATION_ID" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "config": { + "client_secret": "NEW_SECRET" + } + }' +``` + +### Delete Integration + +```bash +curl -X DELETE "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/devices/posture/integration/$INTEGRATION_ID" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" +``` + +## Device Policies + +### Get Default Policy + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/devices/policy" +``` + +### Update Default Policy + +```bash +curl -X PATCH "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/devices/policy" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "gateway_unique_id": "YOUR_GATEWAY_ID", + "disable_auto_fallback": true, + "captive_portal": 180, + "support_url": "https://help.company.com" + }' +``` + +### List All Policies + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/devices/policies" +``` + +### Create Device Policy + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/devices/policy" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Engineering Policy", + "description": "Policy for engineering team", + "match": "any(identity.groups[*] in {\"Engineering\"})", + "precedence": 100, + "gateway_unique_id": "YOUR_GATEWAY_ID", + "service_mode_v2": { + "mode": "warp" + } + }' +``` + +### Update Device Policy + +```bash +curl -X PATCH "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/devices/policy/$POLICY_ID" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Updated Policy Name" + }' +``` + +### Delete Device Policy + +```bash +curl -X DELETE "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/devices/policy/$POLICY_ID" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" +``` + +## Split Tunnel Configuration + +### Get Default Exclude List + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/devices/policy/exclude" +``` + +### Update Default Exclude List + +```bash +curl -X PUT "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/devices/policy/exclude" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '[ + {"address": "192.168.1.0/24", "description": "Local network"}, + {"host": "local-only.example.com", "description": "Local service"} + ]' +``` + +### Get Default Include List + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/devices/policy/include" +``` + +### Update Default Include List + +```bash +curl -X PUT "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/devices/policy/include" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '[ + {"address": "10.0.0.0/8", "description": "Internal network"}, + {"address": "172.16.0.0/12", "description": "VPC"} + ]' +``` + +### Get Policy-Specific Split Tunnel + +```bash +# Exclude list +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/devices/policy/$POLICY_ID/exclude" + +# Include list +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/devices/policy/$POLICY_ID/include" +``` + +## Fallback Domains + +### Get Default Fallback Domains + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/devices/policy/fallback_domains" +``` + +### Update Default Fallback Domains + +```bash +curl -X PUT "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/devices/policy/fallback_domains" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '[ + {"suffix": "internal.company.com", "dns_server": ["10.0.0.53"]}, + {"suffix": "corp.local", "dns_server": ["10.0.0.53", "10.0.1.53"]} + ]' +``` + +## Networks + +### List Device Networks + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/devices/networks" +``` + +### Create Device Network + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/devices/networks" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "type": "tls", + "name": "Corporate WiFi", + "config": { + "tls_sockaddr": "foobar.cloudflareaccess.com:443", + "sha256": "SHA256_HASH_OF_CERT" + } + }' +``` + +### Delete Device Network + +```bash +curl -X DELETE "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/devices/networks/$NETWORK_ID" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" +``` + +## Device Registrations + +### List Registrations + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/devices/registrations" +``` diff --git a/skills/cloudflare/references/devices/gotchas.md b/skills/cloudflare/references/devices/gotchas.md new file mode 100644 index 0000000..d6a473f --- /dev/null +++ b/skills/cloudflare/references/devices/gotchas.md @@ -0,0 +1,284 @@ +# Devices Gotchas + +Common issues, limitations, and troubleshooting for WARP and device management. + +## Common Errors + +### "WARP Not Connecting" + +**Symptoms:** WARP shows "Connecting..." or "Unable to Connect" + +**Causes & Solutions:** + +1. **Network blocking UDP 443/QUIC:** + ```bash + # Test connectivity + warp-cli debug connectivity + ``` + - Switch to TCP mode if QUIC blocked + +2. **Firewall blocking Cloudflare IPs:** + - Allow outbound to Cloudflare ranges: https://www.cloudflare.com/ips/ + +3. **VPN/proxy conflict:** + - Disable other VPN clients + - Check for conflicting proxy settings + +4. **DNS resolution issues:** + ```bash + # Test DNS + dig @1.1.1.1 cloudflare.com + ``` + +5. **Certificate issues:** + ```bash + # Reset WARP + warp-cli registration delete + # Re-enroll + ``` + +### "Device Posture Check Failed" + +**Symptoms:** User blocked from accessing resources + +**Debug:** +```bash +# Check posture status on device +warp-cli debug posture + +# Check posture via API +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/devices/$DEVICE_ID" | \ + jq '.result.device_posture' +``` + +**Common causes:** + +1. **Disk encryption not enabled:** + - macOS: Enable FileVault + - Windows: Enable BitLocker + +2. **Firewall disabled:** + - macOS: System Preferences → Security → Firewall → Turn On + - Windows: Windows Security → Firewall → Turn On + +3. **OS version outdated:** + - Update to minimum required version + +4. **EDR agent not running:** + - Verify CrowdStrike/SentinelOne service is active + +5. **Check hasn't run yet:** + - Wait for next scheduled check (5-15 min) + - Or disconnect/reconnect WARP + +### "Cannot Enroll Device" + +**Causes:** + +1. **Organization not configured:** + - Verify org name: `your-org.cloudflareaccess.com` + +2. **Enrollment rules blocking:** + ```bash + # Check enrollment settings via API + curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/devices/policy" + ``` + +3. **User not in allowed groups:** + - Check Identity Provider groups + +4. **Device limit reached:** + - Check account device limits + +### "Split Tunnel Not Working" + +**Symptoms:** Traffic going through WARP that should be excluded (or vice versa) + +**Debug:** +```bash +# Check current routes +warp-cli settings +``` + +**Solutions:** + +1. **Verify split tunnel config:** + ```bash + curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/devices/policy/exclude" + ``` + +2. **Check for overlapping routes:** + - More specific routes take precedence + +3. **Verify correct mode:** + - Include mode: Only listed routes go through WARP + - Exclude mode: All traffic except listed goes through WARP + +4. **Restart WARP after config change:** + ```bash + warp-cli disconnect + warp-cli connect + ``` + +### "Private Network Access Not Working" + +**Symptoms:** Cannot reach IPs routed through Tunnel + +**Debug:** +```bash +# Check if WARP connected +warp-cli status + +# Check routes +warp-cli debug routes + +# Test connectivity +ping 10.0.0.1 # Internal IP +``` + +**Solutions:** + +1. **Verify tunnel has IP route:** + ```bash + cloudflared tunnel route ip show + ``` + +2. **Check virtual network assignment:** + - WARP device and tunnel must use same virtual network + +3. **Verify Gateway mode enabled:** + ```bash + warp-cli settings + # Should show: Mode = Gateway with WARP + ``` + +4. **Check split tunnel includes the IP range:** + - If using include mode, internal IPs must be listed + +### "TLS Certificate Errors" + +**Cause:** Gateway TLS inspection without root CA installed + +**Solutions:** + +1. **Install Cloudflare root CA:** + - Download from Gateway dashboard + - Install in system trust store + +2. **Or bypass TLS inspection:** + - Create "do_not_inspect" rule for affected sites + +### "WARP Disconnects Frequently" + +**Causes:** + +1. **Captive portal detection:** + - Connect to captive portal first + - Adjust captive portal timeout: + ```json + {"captive_portal": 300} + ``` + +2. **Network instability:** + - Check network connection + - Try different network + +3. **Sleep/wake issues:** + - Disable auto-disconnect on sleep + +### "Gateway DNS Not Filtering" + +**Causes:** + +1. **DoH/DoT bypass:** + - Browser using its own DNS provider + - Disable DoH in browser + +2. **WARP in wrong mode:** + ```bash + warp-cli mode warp+doh # Ensure Gateway mode + ``` + +3. **Application hardcoded DNS:** + - Some apps ignore system DNS + +## Limits + +| Limit | Value | +|-------|-------| +| Devices per user | 5 (configurable) | +| Device policies | 100 | +| Posture checks | 100 | +| Split tunnel entries | 1,000 | +| Fallback domains | 100 | + +## Platform-Specific Issues + +### macOS + +1. **System Extension approval required:** + - System Preferences → Security → Allow "Cloudflare" + +2. **Network Extension not loading:** + ```bash + systemextensionsctl list + ``` + +3. **FileVault check false negative:** + - Ensure FileVault fully enabled (not just enabled for current user) + +### Windows + +1. **WinTun driver issues:** + - Reinstall WARP client + +2. **BitLocker check false negative:** + - Ensure BitLocker enabled on all drives + +3. **Domain join check:** + - Verify AD domain membership status + +### Linux + +1. **Limited features:** + - No GUI, CLI only + - Fewer posture checks available + +2. **systemd service:** + ```bash + sudo systemctl status warp-svc + ``` + +## Debugging Commands + +```bash +# Full status +warp-cli status + +# Current settings +warp-cli settings + +# Account info +warp-cli registration show + +# Debug posture +warp-cli debug posture + +# Debug connectivity +warp-cli debug connectivity + +# Debug logs (macOS) +log stream --predicate 'subsystem == "com.cloudflare.1dot1dot1dot1.macos"' --level debug + +# Debug logs (Linux) +journalctl -u warp-svc -f +``` + +## See Also + +- [WARP](./warp.md) - WARP configuration +- [Posture](./posture.md) - Device posture setup +- [Gateway Gotchas](../gateway/gotchas.md) - DNS/HTTP filtering issues diff --git a/skills/cloudflare/references/devices/posture.md b/skills/cloudflare/references/devices/posture.md new file mode 100644 index 0000000..1ec0e74 --- /dev/null +++ b/skills/cloudflare/references/devices/posture.md @@ -0,0 +1,289 @@ +# Device Posture + +Verify device security compliance before granting access. + +## Overview + +Device posture checks verify: +- **OS version** - Minimum OS requirements +- **Disk encryption** - FileVault, BitLocker enabled +- **Firewall** - Host firewall active +- **Antivirus/EDR** - Security software running +- **Domain joined** - AD/Azure AD membership +- **Device serial** - Known corporate devices + +## Posture Check Types + +| Type | Platforms | Description | +|------|-----------|-------------| +| `file` | All | Check if file exists | +| `application` | All | Check if app is running | +| `serial_number` | All | Match device serial | +| `unique_client_id` | All | Match WARP client ID | +| `os_version` | All | Minimum OS version | +| `disk_encryption` | All | Full disk encryption | +| `firewall` | macOS, Windows | Host firewall enabled | +| `domain_joined` | Windows | AD domain membership | +| `client_certificate` | All | mTLS certificate present | +| `workspace_one` | All | VMware Workspace ONE status | +| `crowdstrike` | All | CrowdStrike Falcon status | +| `intune` | Windows | Microsoft Intune compliance | +| `kolide` | All | Kolide device health | +| `tanium` | All | Tanium endpoint status | +| `sentinelone` | All | SentinelOne agent status | + +## Creating Posture Checks + +### Disk Encryption Check + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/devices/posture" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Disk Encryption Required", + "type": "disk_encryption", + "description": "Requires FileVault or BitLocker", + "schedule": "5m", + "match": [ + {"platform": "mac"}, + {"platform": "windows"} + ], + "input": { + "require_all": true + } + }' +``` + +### OS Version Check + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/devices/posture" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Minimum OS Version", + "type": "os_version", + "match": [{"platform": "mac"}], + "input": { + "version": "13.0.0", + "operator": ">=" + } + }' +``` + +### Firewall Check + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/devices/posture" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Firewall Enabled", + "type": "firewall", + "match": [ + {"platform": "mac"}, + {"platform": "windows"} + ], + "input": { + "enabled": true + } + }' +``` + +### Application Check + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/devices/posture" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Antivirus Running", + "type": "application", + "match": [{"platform": "windows"}], + "input": { + "operating_system": "windows", + "path": { + "windows": "C:\\Program Files\\Defender\\MsMpEng.exe" + }, + "thumbprint": "", + "sha256": "", + "running": true + } + }' +``` + +### File Check + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/devices/posture" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "MDM Profile Installed", + "type": "file", + "match": [{"platform": "mac"}], + "input": { + "operating_system": "mac", + "path": { + "mac": "/Library/Managed Preferences/com.company.mdm.plist" + }, + "exists": true + } + }' +``` + +### Serial Number Check + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/devices/posture" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Corporate Device", + "type": "serial_number", + "input": { + "serial_numbers": ["ABC123", "DEF456", "GHI789"] + } + }' +``` + +## EDR/MDM Integrations + +### CrowdStrike + +```bash +# Create integration +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/devices/posture/integration" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "CrowdStrike Falcon", + "type": "crowdstrike", + "config": { + "api_url": "https://api.crowdstrike.com", + "client_id": "YOUR_CLIENT_ID", + "client_secret": "YOUR_CLIENT_SECRET" + } + }' + +# Create posture check using integration +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/devices/posture" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "CrowdStrike Protected", + "type": "crowdstrike_s2s", + "input": { + "connection_id": "INTEGRATION_UUID", + "state": "online", + "last_seen": "24h" + } + }' +``` + +### Microsoft Intune + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/devices/posture/integration" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Microsoft Intune", + "type": "intune", + "config": { + "client_id": "AZURE_APP_ID", + "client_secret": "AZURE_CLIENT_SECRET", + "tenant_id": "AZURE_TENANT_ID" + } + }' +``` + +### SentinelOne + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/devices/posture/integration" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "SentinelOne", + "type": "sentinelone_s2s", + "config": { + "api_url": "https://usea1.sentinelone.net", + "api_key": "YOUR_API_KEY" + } + }' +``` + +## Using Posture in Access Policies + +### Require Posture Check + +```json +{ + "name": "Secure Access", + "decision": "allow", + "include": [ + {"email_domain": {"domain": "company.com"}} + ], + "require": [ + {"device_posture": {"integration_uid": "disk-encryption-check-uuid"}}, + {"device_posture": {"integration_uid": "firewall-check-uuid"}} + ] +} +``` + +### Require WARP + Posture + +```json +{ + "require": [ + {"warp": {}}, + {"device_posture": {"integration_uid": "crowdstrike-check-uuid"}} + ] +} +``` + +## List Posture Checks + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/devices/posture" | \ + jq '.result[] | {id, name, type}' +``` + +## Check Device Posture Status + +```bash +# Get device posture results +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/devices/$DEVICE_ID" | \ + jq '.result.device_posture' +``` + +## Schedule + +Posture checks run on a schedule: +- `5m` - Every 5 minutes (recommended for critical checks) +- `15m` - Every 15 minutes +- `1h` - Every hour + +```json +{ + "schedule": "5m" +} +``` + +## Best Practices + +1. **Start with critical checks** - Disk encryption, OS version +2. **Test before enforcing** - Create policy without `require` first +3. **Use appropriate schedule** - Balance security vs. performance +4. **Document requirements** - Communicate to users what's required +5. **Provide remediation guidance** - Help users fix posture issues + +## See Also + +- [Policies](./policies.md) - Device settings policies +- [Access Groups](../access/groups.md) - Using posture in groups +- [Gotchas](./gotchas.md) - Common posture issues diff --git a/skills/cloudflare/references/dlp/README.md b/skills/cloudflare/references/dlp/README.md new file mode 100644 index 0000000..c375899 --- /dev/null +++ b/skills/cloudflare/references/dlp/README.md @@ -0,0 +1,287 @@ +# Cloudflare DLP (Data Loss Prevention) + +Detect and protect sensitive data in HTTP traffic. + +## Overview + +Cloudflare DLP provides: +- **Content inspection** - Detect sensitive data in uploads/downloads +- **Predefined profiles** - PII, PCI, HIPAA patterns +- **Custom patterns** - Regex-based detection +- **Integration** - Works with Gateway HTTP policies + +**Requires:** Gateway with TLS inspection enabled + +## How It Works + +``` +User → WARP → Gateway HTTP Policy → DLP Scan → Allow/Block +``` + +1. HTTP traffic flows through Gateway +2. TLS inspection decrypts HTTPS +3. DLP profiles scan request/response bodies +4. Match triggers configured action (block, log) + +## Quick Start + +### 1. Enable TLS Inspection + +```bash +curl -X PUT "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/gateway/configuration" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"settings": {"tls_decrypt": {"enabled": true}}}' +``` + +### 2. Create DLP Profile + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/dlp/profiles/custom" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Credit Card Detection", + "entries": [ + { + "name": "Credit Card Numbers", + "enabled": true, + "pattern": { + "regex": "\\b(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|3[47][0-9]{13})\\b", + "validation": "luhn" + } + } + ] + }' +``` + +### 3. Create Gateway Policy with DLP + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/gateway/rules" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Block Credit Card Uploads", + "enabled": true, + "action": "block", + "filters": ["http"], + "traffic": "http.request.method == \"POST\" and any(dlp.profiles[*] in {\"PROFILE_UUID\"})", + "rule_settings": { + "block_page_enabled": true, + "block_reason": "Upload blocked: sensitive data detected" + } + }' +``` + +## Predefined Profiles + +Cloudflare provides ready-to-use profiles: + +| Profile | Detects | +|---------|---------| +| Financial | Credit cards, bank accounts | +| PII | SSN, passport, driver's license | +| Credentials | API keys, passwords, tokens | +| Source Code | Code patterns, secrets in code | +| Health | Medical record numbers (HIPAA) | + +### Enable Predefined Profile + +```bash +# List predefined profiles +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/dlp/profiles/predefined" + +# Enable entries in a predefined profile +curl -X PUT "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/dlp/profiles/predefined/$PROFILE_ID/config" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "entries": [ + {"id": "ENTRY_ID_1", "enabled": true}, + {"id": "ENTRY_ID_2", "enabled": true} + ] + }' +``` + +## Custom Profiles + +### Create Custom Profile + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/dlp/profiles/custom" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Company Confidential", + "entries": [ + { + "name": "Project Codename", + "enabled": true, + "pattern": { + "regex": "\\b(PROJECT[_-]?FALCON|CLASSIFIED)\\b", + "validation": "regex" + } + }, + { + "name": "Internal Document ID", + "enabled": true, + "pattern": { + "regex": "\\bDOC-[A-Z]{3}-[0-9]{6}\\b", + "validation": "regex" + } + } + ] + }' +``` + +### Pattern Validation Types + +| Type | Description | +|------|-------------| +| `regex` | Standard regex match | +| `luhn` | Credit card checksum validation | + +## Detection Locations + +DLP can scan: +- HTTP request body (uploads) +- HTTP response body (downloads) +- File contents +- Form data +- JSON payloads + +## Datasets (Exact Match) + +For known sensitive values (employee IDs, account numbers): + +### Create Dataset + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/dlp/datasets" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Employee IDs", + "description": "List of employee IDs to protect" + }' +``` + +### Upload Data to Dataset + +```bash +# Get upload URL +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/dlp/datasets/$DATASET_ID/upload" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" + +# Upload CSV (one value per line) +curl -X PUT "$UPLOAD_URL" \ + -H "Content-Type: text/csv" \ + --data-binary @employee_ids.csv +``` + +### Use Dataset in Policy + +```json +{ + "traffic": "any(dlp.datasets[*] in {\"DATASET_UUID\"})" +} +``` + +## Gateway Integration + +### Block on DLP Match + +```json +{ + "name": "Block Sensitive Data Uploads", + "action": "block", + "filters": ["http"], + "traffic": "http.request.method in {\"POST\" \"PUT\"} and any(dlp.profiles[*] in {\"pii-profile-uuid\" \"financial-profile-uuid\"})" +} +``` + +### Log Only (Audit Mode) + +```json +{ + "name": "Log Sensitive Data", + "action": "allow", + "filters": ["http"], + "traffic": "any(dlp.profiles[*] in {\"pii-profile-uuid\"})", + "rule_settings": { + "add_headers": { + "X-DLP-Match": "true" + } + } +} +``` + +### Block Uploads, Allow Downloads + +```json +{ + "name": "Block PII Uploads Only", + "action": "block", + "filters": ["http"], + "traffic": "http.request.method in {\"POST\" \"PUT\"} and any(dlp.profiles[*] in {\"pii-uuid\"})" +} +``` + +## Payload Logging + +Capture matched content for investigation: + +```bash +curl -X PUT "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/dlp/payload_log" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "public_key": "YOUR_PUBLIC_KEY_FOR_ENCRYPTION" + }' +``` + +**Note:** Payloads are encrypted with your public key for security. + +## Common Patterns + +### Credit Cards + +```regex +\b(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|3[47][0-9]{13}|6(?:011|5[0-9]{2})[0-9]{12})\b +``` + +### US SSN + +```regex +\b\d{3}-\d{2}-\d{4}\b +``` + +### Email Addresses + +```regex +\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b +``` + +### API Keys (Generic) + +```regex +\b[A-Za-z0-9]{32,}\b +``` + +### AWS Access Keys + +```regex +\bAKIA[0-9A-Z]{16}\b +``` + +## In This Reference + +- [Profiles](./profiles.md) - Profile configuration details +- [API](./api.md) - Full API reference + +## See Also + +- [Gateway HTTP Policies](../gateway/http-policies.md) - Using DLP in policies +- [Gateway Gotchas](../gateway/gotchas.md) - TLS inspection requirements diff --git a/skills/cloudflare/references/dlp/api.md b/skills/cloudflare/references/dlp/api.md new file mode 100644 index 0000000..5178a61 --- /dev/null +++ b/skills/cloudflare/references/dlp/api.md @@ -0,0 +1,332 @@ +# DLP API Reference + +Base URL: `https://api.cloudflare.com/client/v4/accounts/{account_id}` + +## Profiles + +### List All Profiles + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/dlp/profiles" | jq '.result' +``` + +### List Predefined Profiles + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/dlp/profiles/predefined" +``` + +### Get Predefined Profile + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/dlp/profiles/predefined/$PROFILE_ID" +``` + +### Configure Predefined Profile + +```bash +curl -X PUT "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/dlp/profiles/predefined/$PROFILE_ID/config" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "entries": [ + {"id": "entry-uuid-1", "enabled": true}, + {"id": "entry-uuid-2", "enabled": true}, + {"id": "entry-uuid-3", "enabled": false} + ] + }' +``` + +### List Custom Profiles + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/dlp/profiles/custom" +``` + +### Create Custom Profile + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/dlp/profiles/custom" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Sensitive Data Profile", + "description": "Detects company sensitive information", + "entries": [ + { + "name": "Credit Card Numbers", + "enabled": true, + "pattern": { + "regex": "\\b(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14})\\b", + "validation": "luhn" + } + }, + { + "name": "Internal Project Codes", + "enabled": true, + "pattern": { + "regex": "\\bPROJ-[A-Z]{3}-[0-9]{4}\\b", + "validation": "regex" + } + } + ], + "allowed_match_count": 0 + }' +``` + +### Get Custom Profile + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/dlp/profiles/custom/$PROFILE_ID" +``` + +### Update Custom Profile + +```bash +curl -X PUT "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/dlp/profiles/custom/$PROFILE_ID" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Updated Profile Name", + "entries": [ + { + "name": "New Entry", + "enabled": true, + "pattern": { + "regex": "\\bNEW-PATTERN\\b", + "validation": "regex" + } + } + ] + }' +``` + +### Delete Custom Profile + +```bash +curl -X DELETE "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/dlp/profiles/custom/$PROFILE_ID" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" +``` + +## Entries + +### List All Entries + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/dlp/entries" +``` + +### List Predefined Entries + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/dlp/entries/predefined" +``` + +### Get Entry + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/dlp/entries/$ENTRY_ID" +``` + +### Update Entry + +```bash +curl -X PUT "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/dlp/entries/custom/$ENTRY_ID" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Updated Entry Name", + "pattern": { + "regex": "\\bUPDATED-PATTERN\\b", + "validation": "regex" + } + }' +``` + +## Datasets + +### List Datasets + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/dlp/datasets" +``` + +### Create Dataset + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/dlp/datasets" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Sensitive Keywords", + "description": "List of sensitive keywords to detect" + }' +``` + +### Get Dataset + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/dlp/datasets/$DATASET_ID" +``` + +### Delete Dataset + +```bash +curl -X DELETE "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/dlp/datasets/$DATASET_ID" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" +``` + +### Create Upload URL + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/dlp/datasets/$DATASET_ID/upload" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" +``` + +### Upload Data + +```bash +# Get upload URL first, then: +curl -X PUT "$UPLOAD_URL" \ + -H "Content-Type: text/csv" \ + --data-binary @data.csv +``` + +### Get Dataset Version + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/dlp/datasets/$DATASET_ID/versions/$VERSION" +``` + +## Payload Logging + +### Get Payload Log Settings + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/dlp/payload_log" +``` + +### Configure Payload Logging + +```bash +curl -X PUT "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/dlp/payload_log" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "public_key": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...\n-----END PUBLIC KEY-----" + }' +``` + +## Pattern Validation + +### Validate Pattern + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/dlp/patterns/validate" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "regex": "\\b[A-Z]{3}-[0-9]{6}\\b" + }' +``` + +## Limits + +### Get DLP Limits + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/dlp/limits" +``` + +## Using DLP in Gateway Policies + +### Create Policy with DLP Profile + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/gateway/rules" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Block Sensitive Data", + "enabled": true, + "action": "block", + "filters": ["http"], + "traffic": "any(dlp.profiles[*] in {\"profile-uuid-1\" \"profile-uuid-2\"})", + "rule_settings": { + "block_page_enabled": true, + "block_reason": "Sensitive data detected" + } + }' +``` + +### Create Policy with DLP Dataset + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/gateway/rules" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Block Known Sensitive Values", + "enabled": true, + "action": "block", + "filters": ["http"], + "traffic": "any(dlp.datasets[*] in {\"dataset-uuid\"})" + }' +``` + +## Terraform + +```hcl +resource "cloudflare_zero_trust_dlp_profile" "pii" { + account_id = var.cloudflare_account_id + name = "PII Detection" + description = "Detects personally identifiable information" + type = "custom" + + entry { + name = "SSN" + enabled = true + pattern { + regex = "\\b\\d{3}-\\d{2}-\\d{4}\\b" + validation = "regex" + } + } + + entry { + name = "Credit Card" + enabled = true + pattern { + regex = "\\b(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14})\\b" + validation = "luhn" + } + } +} + +resource "cloudflare_zero_trust_gateway_policy" "dlp_block" { + account_id = var.cloudflare_account_id + name = "Block PII Uploads" + description = "Block uploads containing PII" + action = "block" + enabled = true + filters = ["http"] + traffic = "http.request.method in {\"POST\" \"PUT\"} and any(dlp.profiles[*] in {\"${cloudflare_zero_trust_dlp_profile.pii.id}\"})" + + rule_settings { + block_page_enabled = true + block_page_reason = "Upload blocked: sensitive data detected" + } +} +``` diff --git a/skills/cloudflare/references/gateway/README.md b/skills/cloudflare/references/gateway/README.md new file mode 100644 index 0000000..8179d31 --- /dev/null +++ b/skills/cloudflare/references/gateway/README.md @@ -0,0 +1,195 @@ +# Cloudflare Gateway + +Secure Web Gateway for DNS and HTTP filtering, threat protection, and data loss prevention. + +## Overview + +Cloudflare Gateway provides: +- **DNS filtering** - Block domains by category, threat, or custom lists +- **HTTP filtering** - Inspect and filter HTTP/HTTPS traffic +- **Network filtering** - Control Layer 4 traffic +- **Threat protection** - Block malware, phishing, C2 domains +- **Data loss prevention** - Detect sensitive data in traffic + +**Architecture:** +- DNS policies: WARP client or DNS endpoint → Gateway DNS resolver +- HTTP policies: WARP client (with TLS inspection) → Gateway proxy + +## Quick Start + +### 1. Create DNS Policy + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/gateway/rules" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Block Malware Domains", + "enabled": true, + "action": "block", + "traffic": "dns", + "filters": ["dns"], + "rule_settings": { + "block_page_enabled": true, + "block_reason": "This domain is blocked due to security threats" + }, + "conditions": [ + { + "type": "traffic", + "expression": { + "any": { + "security_category": ["malware", "phishing", "spyware"] + } + } + } + ] + }' +``` + +### 2. Create HTTP Policy + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/gateway/rules" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Block File Uploads to Cloud Storage", + "enabled": true, + "action": "block", + "traffic": "http", + "filters": ["http"], + "conditions": [ + { + "type": "traffic", + "expression": { + "and": [ + {"http.request.method": "PUT"}, + {"any": {"application": ["Google Drive", "Dropbox", "OneDrive"]}} + ] + } + } + ] + }' +``` + +## Policy Types + +| Type | Traffic | Use Case | +|------|---------|----------| +| DNS | `dns` | Block domains, categories, threats | +| HTTP | `http` | Inspect web traffic, block uploads, DLP | +| Network | `l4` | Control TCP/UDP connections | + +## Actions + +| Action | Effect | +|--------|--------| +| `allow` | Permit traffic | +| `block` | Block with optional block page | +| `isolate` | Open in remote browser (Browser Isolation) | +| `do_not_inspect` | Skip TLS inspection | +| `do_not_scan` | Skip antivirus scanning | +| `egress` | Route through specific egress IP | +| `audit_ssh` | Log SSH commands | + +## Traffic Selectors (Conditions) + +### DNS Policies + +```json +// Block specific domain +{"dns.fqdn": "malware.example.com"} + +// Block domain pattern +{"dns.fqdn_regex": ".*\\.torrent\\..*"} + +// Block by category +{"any": {"content_category": ["gambling", "adult_content"]}} + +// Block by security category +{"any": {"security_category": ["malware", "phishing"]}} + +// Match resolved IP +{"dns.resolved_ip": "192.168.1.0/24"} +``` + +### HTTP Policies + +```json +// Match URL +{"http.request.uri": "https://example.com/sensitive/*"} + +// Match host +{"http.host": "dropbox.com"} + +// Match by application +{"any": {"application": ["Slack", "Discord"]}} + +// Match by file type (uploads) +{"http.upload.file_type": ["exe", "dll", "bat"]} + +// Match HTTP method +{"http.request.method": "POST"} + +// Match by user identity +{"identity.email": "*@company.com"} + +// Match by device posture +{"device_posture.check_passed": "disk-encryption-uuid"} +``` + +### Network Policies + +```json +// Match destination port +{"net.dst_port": 22} + +// Match destination IP +{"net.dst_ip": "10.0.0.0/8"} + +// Match protocol +{"net.protocol": "TCP"} +``` + +## Decision Flow + +1. Evaluate rules in precedence order (lowest first) +2. First matching rule wins +3. If no rules match, traffic is allowed by default + +## Block Page + +Configure a custom block page: + +```bash +curl -X PUT "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/gateway/configuration" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "settings": { + "block_page": { + "enabled": true, + "name": "Company Security Block", + "mailto_subject": "Request Access", + "mailto_address": "security@company.com", + "header_text": "Access Blocked", + "footer_text": "Contact IT if you need access" + } + } + }' +``` + +## In This Reference + +- [DNS Policies](./dns-policies.md) - DNS filtering details +- [HTTP Policies](./http-policies.md) - HTTP/HTTPS filtering +- [Lists](./lists.md) - Custom allow/block lists +- [Locations](./locations.md) - DNS endpoint configuration +- [API](./api.md) - Full API reference +- [Gotchas](./gotchas.md) - Common issues + +## See Also + +- [Devices](../devices/README.md) - WARP client deployment +- [DLP](../dlp/README.md) - Data loss prevention +- [Access](../access/README.md) - Application access control diff --git a/skills/cloudflare/references/gateway/api.md b/skills/cloudflare/references/gateway/api.md new file mode 100644 index 0000000..5908fca --- /dev/null +++ b/skills/cloudflare/references/gateway/api.md @@ -0,0 +1,343 @@ +# Gateway API Reference + +Base URL: `https://api.cloudflare.com/client/v4/accounts/{account_id}` + +## Rules (Policies) + +### List Rules + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/gateway/rules" | jq '.result' +``` + +### Get Rule + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/gateway/rules/$RULE_ID" +``` + +### Create DNS Rule + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/gateway/rules" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Block Social Media", + "description": "Block social media during work hours", + "enabled": true, + "action": "block", + "filters": ["dns"], + "traffic": "any(dns.content_category[*] in {\"Social Networks\"})", + "rule_settings": { + "block_page_enabled": true, + "block_reason": "Social media is blocked during work hours" + }, + "precedence": 100 + }' +``` + +### Create HTTP Rule + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/gateway/rules" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Block File Uploads", + "enabled": true, + "action": "block", + "filters": ["http"], + "traffic": "any(http.upload.file_type[*] in {\"exe\" \"dll\" \"bat\"})", + "rule_settings": { + "block_page_enabled": true + }, + "precedence": 50 + }' +``` + +### Create Network Rule + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/gateway/rules" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Block SSH to External", + "enabled": true, + "action": "block", + "filters": ["l4"], + "traffic": "net.dst.port == 22 and not net.dst.ip in $internal_networks", + "precedence": 25 + }' +``` + +### Update Rule + +```bash +curl -X PUT "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/gateway/rules/$RULE_ID" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Updated Rule Name", + "enabled": false + }' +``` + +### Delete Rule + +```bash +curl -X DELETE "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/gateway/rules/$RULE_ID" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" +``` + +## Lists + +### List All Lists + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/gateway/lists" | jq '.result' +``` + +### Create List + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/gateway/lists" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "blocked_domains", + "description": "Custom blocked domains", + "type": "DOMAIN", + "items": [ + {"value": "malware.example.com"}, + {"value": "phishing.example.com"} + ] + }' +``` + +**List types:** `DOMAIN`, `URL`, `IP`, `SERIAL`, `EMAIL`, `FILE` + +### Get List Items + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/gateway/lists/$LIST_ID/items" +``` + +### Add Items to List + +```bash +curl -X PATCH "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/gateway/lists/$LIST_ID" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "append": [ + {"value": "newdomain.com"}, + {"value": "anotherdomain.com"} + ] + }' +``` + +### Remove Items from List + +```bash +curl -X PATCH "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/gateway/lists/$LIST_ID" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "remove": ["malware.example.com"] + }' +``` + +### Delete List + +```bash +curl -X DELETE "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/gateway/lists/$LIST_ID" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" +``` + +## Locations (DNS Endpoints) + +### List Locations + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/gateway/locations" +``` + +### Create Location + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/gateway/locations" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Office NYC", + "client_default": false, + "networks": [ + {"network": "203.0.113.0/24"} + ] + }' +``` + +### Update Location + +```bash +curl -X PUT "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/gateway/locations/$LOCATION_ID" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Office NYC Updated", + "networks": [ + {"network": "203.0.113.0/24"}, + {"network": "198.51.100.0/24"} + ] + }' +``` + +### Delete Location + +```bash +curl -X DELETE "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/gateway/locations/$LOCATION_ID" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" +``` + +## Categories + +### List Content Categories + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/gateway/categories" | \ + jq '.result[] | {id, name, class}' +``` + +### List Application Types + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/gateway/app_types" | \ + jq '.result[] | {id, name}' +``` + +## Configuration + +### Get Gateway Configuration + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/gateway/configuration" +``` + +### Update Configuration + +```bash +curl -X PUT "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/gateway/configuration" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "settings": { + "antivirus": { + "enabled_download_phase": true, + "enabled_upload_phase": true, + "fail_closed": false + }, + "tls_decrypt": { + "enabled": true + }, + "activity_log": { + "enabled": true + }, + "block_page": { + "enabled": true, + "name": "Security Block", + "header_text": "This site is blocked" + } + } + }' +``` + +## Certificates (TLS Inspection) + +### List Certificates + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/gateway/certificates" +``` + +### Create Certificate + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/gateway/certificates" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "validity_period_days": 1825 + }' +``` + +### Activate Certificate + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/gateway/certificates/$CERT_ID/activate" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" +``` + +## Logging + +### Get Logging Configuration + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/gateway/logging" +``` + +### Update Logging + +```bash +curl -X PUT "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/gateway/logging" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "settings_by_rule_type": { + "dns": { + "log_all": true + }, + "http": { + "log_all": true + }, + "l4": { + "log_all": true + } + }, + "redact_pii": true + }' +``` + +## Proxy Endpoints + +### List Proxy Endpoints + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/gateway/proxy_endpoints" +``` + +### Create Proxy Endpoint + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/gateway/proxy_endpoints" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "PAC File Endpoint", + "networks": [ + {"network": "10.0.0.0/8"} + ] + }' +``` diff --git a/skills/cloudflare/references/gateway/dns-policies.md b/skills/cloudflare/references/gateway/dns-policies.md new file mode 100644 index 0000000..de42af8 --- /dev/null +++ b/skills/cloudflare/references/gateway/dns-policies.md @@ -0,0 +1,298 @@ +# Gateway DNS Policies + +Filter and control DNS resolution for your organization. + +## Overview + +DNS policies evaluate DNS queries and can: +- Block malicious domains +- Filter by content category +- Allow/block custom domain lists +- Override DNS responses +- Log DNS activity + +## Policy Structure + +```json +{ + "name": "Block Malware", + "enabled": true, + "action": "block", + "filters": ["dns"], + "traffic": "any(dns.security_category[*] in {\"Malware\" \"Phishing\" \"Spyware\"})", + "rule_settings": { + "block_page_enabled": true, + "block_reason": "Blocked for security reasons" + }, + "precedence": 10 +} +``` + +## Traffic Expressions + +### Domain Matching + +``` +# Exact domain +dns.fqdn == "example.com" + +# Subdomain matching (includes *.example.com) +dns.fqdn matches ".*\\.example\\.com$" + +# Domain in list +dns.fqdn in $blocked_domains + +# Ends with +dns.fqdn matches ".*\\.ru$" +``` + +### Content Categories + +``` +# Single category +any(dns.content_category[*] in {"Gambling"}) + +# Multiple categories +any(dns.content_category[*] in {"Gambling" "Adult Content" "Dating"}) + +# Exclude category +not any(dns.content_category[*] in {"Business"}) +``` + +**Common categories:** +- `Adult Content`, `Dating`, `Gambling` +- `Social Networks`, `Streaming Media`, `Gaming` +- `News`, `Shopping`, `Technology` +- `Education`, `Government`, `Health` + +### Security Categories + +``` +# Block security threats +any(dns.security_category[*] in {"Malware" "Phishing" "Spyware" "Botnet"}) + +# Block command & control +any(dns.security_category[*] in {"Command and Control"}) +``` + +**Security categories:** +- `Malware`, `Phishing`, `Spyware` +- `Command and Control`, `Botnet` +- `Cryptomining`, `DGA Domains` +- `Newly Seen Domains`, `Parked Domains` + +### Resolved IP Matching + +``` +# Block if resolves to private IP +dns.resolved_ip in {10.0.0.0/8 172.16.0.0/12 192.168.0.0/16} + +# Block specific IP +dns.resolved_ip == "93.184.216.34" +``` + +### DNS Record Type + +``` +# Only MX records +dns.query_rtype == "MX" + +# Block TXT lookups (often used for data exfiltration) +dns.query_rtype == "TXT" +``` + +### Identity Conditions + +``` +# By user email +identity.email == "user@example.com" + +# By user group +any(identity.groups[*] in {"Engineering"}) + +# By device posture +device_posture.checks.passed[*] == "disk-encryption-uuid" +``` + +### Location Conditions + +``` +# Specific office location +identity.location.id == "location-uuid" + +# By source IP +identity.source_ip in {203.0.113.0/24} +``` + +## Common Policies + +### Block All Security Threats + +```json +{ + "name": "Block Security Threats", + "enabled": true, + "action": "block", + "filters": ["dns"], + "traffic": "any(dns.security_category[*] in {\"Malware\" \"Phishing\" \"Spyware\" \"Botnet\" \"Command and Control\" \"Cryptomining\"})", + "precedence": 1 +} +``` + +### Block Adult Content + +```json +{ + "name": "Block Adult Content", + "enabled": true, + "action": "block", + "filters": ["dns"], + "traffic": "any(dns.content_category[*] in {\"Adult Content\" \"Adult Themes\"})", + "precedence": 10 +} +``` + +### Block Social Media (Except for Marketing Team) + +```json +{ + "name": "Allow Marketing - Social Media", + "enabled": true, + "action": "allow", + "filters": ["dns"], + "traffic": "any(dns.content_category[*] in {\"Social Networks\"}) and any(identity.groups[*] in {\"Marketing\"})", + "precedence": 5 +}, +{ + "name": "Block Social Media", + "enabled": true, + "action": "block", + "filters": ["dns"], + "traffic": "any(dns.content_category[*] in {\"Social Networks\"})", + "precedence": 10 +} +``` + +### Block Newly Seen Domains + +```json +{ + "name": "Block New Domains", + "enabled": true, + "action": "block", + "filters": ["dns"], + "traffic": "any(dns.security_category[*] in {\"Newly Seen Domains\"})", + "rule_settings": { + "block_page_enabled": true, + "block_reason": "New domain detected - please contact IT if this is needed" + }, + "precedence": 5 +} +``` + +### Allow Specific Domains (Bypass) + +```json +{ + "name": "Allow Business Critical", + "enabled": true, + "action": "allow", + "filters": ["dns"], + "traffic": "dns.fqdn in $allowed_domains", + "precedence": 1 +} +``` + +### Block DNS over HTTPS/TLS (DoH/DoT) Providers + +```json +{ + "name": "Block DoH Providers", + "enabled": true, + "action": "block", + "filters": ["dns"], + "traffic": "dns.fqdn in {\"dns.google\" \"cloudflare-dns.com\" \"dns.quad9.net\" \"doh.opendns.com\"}", + "precedence": 2 +} +``` + +## Using Lists + +### Create a Domain List + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/gateway/lists" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "blocked_domains", + "type": "DOMAIN", + "items": [ + {"value": "badsite.com"}, + {"value": "malware.example.com"} + ] + }' +``` + +### Use List in Policy + +```json +{ + "traffic": "dns.fqdn in $blocked_domains" +} +``` + +## DNS Override + +Redirect DNS resolution to a different IP: + +```json +{ + "name": "Override Internal DNS", + "enabled": true, + "action": "allow", + "filters": ["dns"], + "traffic": "dns.fqdn == \"internal.example.com\"", + "rule_settings": { + "override_ips": ["10.0.0.100"] + } +} +``` + +## SafeSearch Enforcement + +Force SafeSearch on search engines: + +```bash +curl -X PUT "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/gateway/configuration" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "settings": { + "safesearch": { + "enabled": true + }, + "youtube_restricted_mode": { + "enabled": true + } + } + }' +``` + +## Precedence + +- Lower number = higher priority +- First matching rule wins +- Use gaps (10, 20, 30) to allow future insertions + +**Recommended order:** +1. `1-9`: Critical allows (bypass for essential services) +2. `10-49`: Security blocks (malware, phishing) +3. `50-99`: Content filtering (categories) +4. `100+`: Default policies + +## See Also + +- [HTTP Policies](./http-policies.md) - HTTP traffic filtering +- [Lists](./lists.md) - Custom domain lists +- [Gotchas](./gotchas.md) - Common DNS policy issues diff --git a/skills/cloudflare/references/gateway/gotchas.md b/skills/cloudflare/references/gateway/gotchas.md new file mode 100644 index 0000000..7198703 --- /dev/null +++ b/skills/cloudflare/references/gateway/gotchas.md @@ -0,0 +1,256 @@ +# Gateway Gotchas + +Common issues, limitations, and troubleshooting for Cloudflare Gateway. + +## Common Errors + +### "DNS Query Not Being Filtered" + +**Symptoms:** Traffic bypasses Gateway DNS policies + +**Causes & Solutions:** + +1. **WARP not connected** - Check WARP client is connected and Gateway enabled + ```bash + warp-cli status + warp-cli settings + ``` + +2. **DNS over HTTPS (DoH) bypass** - Browser using its own DoH provider + - Disable DoH in browser settings + - Create DNS policy to block DoH providers + +3. **Hardcoded DNS servers** - Application using its own DNS + - Block UDP/TCP 53 to non-Gateway IPs + +4. **Split tunnel excluding traffic** - Check device policy split tunnel config + +### "HTTPS Sites Not Being Filtered" + +**Cause:** TLS inspection not enabled or certificate not installed + +**Solutions:** + +1. **Enable TLS inspection:** + ```bash + curl -X PUT "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/gateway/configuration" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"settings": {"tls_decrypt": {"enabled": true}}}' + ``` + +2. **Install root CA on devices:** + - Download CA from Gateway dashboard + - Install in system trust store + - For managed devices, deploy via MDM + +3. **Check do_not_inspect policies** - Verify the site isn't being bypassed + +### "Certificate Errors After Enabling TLS Inspection" + +**Cause:** Root CA not trusted or certificate pinning + +**Solutions:** + +1. **Install root CA** - Deploy to all devices via MDM +2. **Bypass pinned applications:** + ```json + { + "name": "Bypass Certificate Pinning", + "action": "do_not_inspect", + "filters": ["http"], + "traffic": "any(http.application[*] in {\"Banking App\" \"Healthcare App\"})" + } + ``` + +3. **Bypass specific domains:** + ```json + { + "traffic": "http.host in $pinned_domains" + } + ``` + +### "Policy Not Taking Effect" + +**Causes:** + +1. **Precedence conflict** - Lower precedence (higher number) policy being matched first + ```bash + curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/gateway/rules" | \ + jq '.result | sort_by(.precedence) | .[] | {name, precedence, action, enabled}' + ``` + +2. **Policy disabled** - Check `enabled: true` + +3. **Traffic expression wrong** - Test with simpler expression first + +4. **Propagation delay** - Wait 60 seconds for changes to propagate + +### "Block Page Not Showing" + +**Causes:** + +1. **Block page not enabled:** + ```json + "rule_settings": { + "block_page_enabled": true + } + ``` + +2. **HTTPS traffic without TLS inspection** - Only DNS block page works without TLS inspection + +3. **Non-browser application** - Block pages only work for browsers + +### "Application Detection Not Working" + +**Cause:** Application signatures require TLS inspection to identify traffic + +**Solution:** Enable TLS inspection for accurate application detection + +### "False Positives - Legitimate Site Blocked" + +**Solutions:** + +1. **Check which rule blocked it:** + ```bash + # Check Gateway activity logs in dashboard + # or via Logpush to see matched rule + ``` + +2. **Add to allow list:** + ```json + { + "name": "Allow False Positive", + "action": "allow", + "filters": ["dns"], + "traffic": "dns.fqdn == \"legitimate-site.com\"", + "precedence": 1 + } + ``` + +3. **Request recategorization** via Cloudflare Radar + +### "Slow DNS Resolution" + +**Causes:** + +1. **Many complex policies** - Simplify or reduce policy count +2. **Large lists** - Break into smaller, targeted lists +3. **Logging overhead** - Reduce logging verbosity for non-critical traffic + +### "Gateway Logs Missing" + +**Solutions:** + +1. **Enable logging:** + ```bash + curl -X PUT "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/gateway/logging" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "settings_by_rule_type": { + "dns": {"log_all": true}, + "http": {"log_all": true} + } + }' + ``` + +2. **Set up Logpush** for real-time log export + +3. **Check retention** - Gateway logs have limited retention + +## Limits + +| Limit | Value | +|-------|-------| +| Gateway rules | 500 | +| Lists per account | 100 | +| Items per list | 5,000 (20,000 Enterprise) | +| Locations | 100 | +| DNS query size | 512 bytes (UDP), 65535 (TCP/DoH) | +| HTTP body inspection | 100 KB | +| File scan size | 15 MB | + +## TLS Inspection Limitations + +### Applications That May Break + +- Apps with certificate pinning (banking, healthcare) +- VPN clients +- Some desktop applications +- IoT devices + +### Bypass Categories to Consider + +- Financial Services +- Health +- Government (some) + +### Common Bypass List + +```json +{ + "name": "TLS Bypass Domains", + "type": "DOMAIN", + "items": [ + {"value": "*.apple.com"}, + {"value": "*.icloud.com"}, + {"value": "*.microsoft.com"}, + {"value": "*.windowsupdate.com"}, + {"value": "*.digicert.com"}, + {"value": "*.letsencrypt.org"}, + {"value": "*.banking.com"}, + {"value": "*.healthcare.com"} + ] +} +``` + +## Performance Considerations + +### Policy Order + +- Put allow rules before block rules when possible +- Use specific matchers over broad ones +- Avoid regex when exact match works + +### List Optimization + +- Use multiple smaller lists vs one large list +- Keep frequently matched items at the top +- Remove stale entries regularly + +## Debugging + +### Test DNS Resolution + +```bash +# Through WARP +dig @1.1.1.1 example.com + +# Check Gateway is being used +dig +short TXT debug.cloudflare.com +``` + +### Test HTTP Filtering + +```bash +# This should be blocked if malware category blocked +curl -v https://malware.testcategory.com + +# Check WARP is proxying +curl -I https://cloudflare.com/cdn-cgi/trace +``` + +### Check Gateway Configuration + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/gateway/configuration" | jq . +``` + +## See Also + +- [DNS Policies](./dns-policies.md) - DNS filtering +- [HTTP Policies](./http-policies.md) - HTTP filtering +- [Devices Gotchas](../devices/gotchas.md) - WARP client issues diff --git a/skills/cloudflare/references/gateway/http-policies.md b/skills/cloudflare/references/gateway/http-policies.md new file mode 100644 index 0000000..7bc66f6 --- /dev/null +++ b/skills/cloudflare/references/gateway/http-policies.md @@ -0,0 +1,322 @@ +# Gateway HTTP Policies + +Inspect and filter HTTP/HTTPS traffic with deep content inspection. + +## Overview + +HTTP policies require: +- **WARP client** with Gateway proxy enabled +- **TLS inspection** for HTTPS traffic (optional but recommended) +- **Root CA** installed on devices for TLS inspection + +## Policy Structure + +```json +{ + "name": "Block Uploads to Cloud Storage", + "enabled": true, + "action": "block", + "filters": ["http"], + "traffic": "http.request.method == \"PUT\" and any(http.application[*] in {\"Google Drive\" \"Dropbox\" \"OneDrive\"})", + "rule_settings": { + "block_page_enabled": true, + "block_reason": "File uploads to cloud storage are not allowed" + }, + "precedence": 50 +} +``` + +## Actions + +| Action | Description | +|--------|-------------| +| `allow` | Permit request | +| `block` | Block with optional block page | +| `isolate` | Open in remote browser isolation | +| `do_not_inspect` | Skip TLS decryption (pass through) | +| `do_not_scan` | Skip antivirus scanning | + +## Traffic Expressions + +### URL/Host Matching + +``` +# Exact host +http.host == "dropbox.com" + +# Host contains +http.host contains "google" + +# URL path +http.request.uri.path == "/upload" + +# Full URL +http.request.uri matches "https://example\\.com/api/.*" + +# URL in list +http.host in $blocked_hosts +``` + +### HTTP Method + +``` +# Block uploads +http.request.method in {"PUT" "POST"} + +# Block specific methods +http.request.method == "DELETE" +``` + +### Application Detection + +``` +# Single application +any(http.application[*] in {"Slack"}) + +# Multiple applications +any(http.application[*] in {"Slack" "Discord" "Teams"}) + +# Application category +any(http.application_type[*] in {"File Sharing"}) +``` + +**Common applications:** `Slack`, `Discord`, `Teams`, `Zoom`, `Google Drive`, `Dropbox`, `OneDrive`, `Box`, `GitHub`, `GitLab`, `AWS`, `Azure`, `GCP` + +### File Type Detection (Uploads/Downloads) + +``` +# Block executable uploads +any(http.upload.file_type[*] in {"exe" "dll" "bat" "ps1" "msi"}) + +# Block executable downloads +any(http.download.file_type[*] in {"exe" "dll" "bat"}) + +# Block by MIME type +http.request.content_type contains "application/x-executable" +``` + +### Content Categories + +``` +# Block by category +any(http.content_category[*] in {"Gambling" "Adult Content"}) + +# Security categories +any(http.security_category[*] in {"Malware" "Phishing"}) +``` + +### Identity Conditions + +``` +# By user email +identity.email == "user@example.com" + +# By email domain +identity.email matches ".*@contractor\\.com$" + +# By group +any(identity.groups[*] in {"Engineering"}) + +# By device posture +device_posture.checks.passed[*] == "uuid" +``` + +### Request Headers + +``` +# Check user agent +http.request.headers["user-agent"] contains "curl" + +# Check authorization +not http.request.headers["authorization"] +``` + +## Common Policies + +### Block File Uploads to Personal Cloud Storage + +```json +{ + "name": "Block Personal Cloud Uploads", + "enabled": true, + "action": "block", + "filters": ["http"], + "traffic": "http.request.method in {\"PUT\" \"POST\"} and any(http.application[*] in {\"Google Drive\" \"Dropbox\" \"OneDrive\" \"iCloud\" \"Box\"})", + "precedence": 50 +} +``` + +### Block Executable Downloads + +```json +{ + "name": "Block Executable Downloads", + "enabled": true, + "action": "block", + "filters": ["http"], + "traffic": "any(http.download.file_type[*] in {\"exe\" \"dll\" \"msi\" \"bat\" \"ps1\" \"vbs\" \"jar\"})", + "rule_settings": { + "block_page_enabled": true, + "block_reason": "Downloading executables is blocked. Contact IT for approved software." + }, + "precedence": 20 +} +``` + +### Isolate Social Media + +```json +{ + "name": "Isolate Social Media", + "enabled": true, + "action": "isolate", + "filters": ["http"], + "traffic": "any(http.content_category[*] in {\"Social Networks\"})", + "rule_settings": { + "isolate_settings": { + "disable_copy_paste": true, + "disable_download": true, + "disable_upload": true + } + }, + "precedence": 30 +} +``` + +### Do Not Inspect Banking Sites + +```json +{ + "name": "Bypass Inspection - Banking", + "enabled": true, + "action": "do_not_inspect", + "filters": ["http"], + "traffic": "any(http.content_category[*] in {\"Financial Services\"})", + "precedence": 5 +} +``` + +### Block Shadow IT + +```json +{ + "name": "Block Unapproved SaaS", + "enabled": true, + "action": "block", + "filters": ["http"], + "traffic": "any(http.application_type[*] in {\"File Sharing\" \"Cloud Storage\"}) and not any(http.application[*] in {\"SharePoint\" \"OneDrive\"})", + "precedence": 40 +} +``` + +### Allow Uploads Only to Corporate Domain + +```json +{ + "name": "Block External Uploads", + "enabled": true, + "action": "block", + "filters": ["http"], + "traffic": "http.request.method == \"POST\" and not http.host matches \".*\\.company\\.com$\"", + "precedence": 60 +} +``` + +## TLS Inspection + +### Enable TLS Inspection + +```bash +curl -X PUT "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/gateway/configuration" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "settings": { + "tls_decrypt": { + "enabled": true + } + } + }' +``` + +### Download Root CA + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/gateway/certificates" | \ + jq -r '.result[] | select(.active == true) | .certificate' +``` + +### Bypass Inspection for Specific Hosts + +```json +{ + "name": "Do Not Inspect - Banking", + "enabled": true, + "action": "do_not_inspect", + "filters": ["http"], + "traffic": "http.host in $banking_domains", + "precedence": 1 +} +``` + +## Browser Isolation + +### Full Isolation + +```json +{ + "action": "isolate", + "rule_settings": { + "isolate_settings": { + "disable_copy_paste": true, + "disable_download": true, + "disable_upload": true, + "disable_printing": true, + "disable_keyboard": false + } + } +} +``` + +### Partial Isolation (Read-only) + +```json +{ + "action": "isolate", + "rule_settings": { + "isolate_settings": { + "disable_copy_paste": false, + "disable_download": true, + "disable_upload": true + } + } +} +``` + +## Antivirus Scanning + +### Enable AV Scanning + +```bash +curl -X PUT "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/gateway/configuration" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "settings": { + "antivirus": { + "enabled_download_phase": true, + "enabled_upload_phase": true, + "fail_closed": false + } + } + }' +``` + +**Note:** `fail_closed: true` blocks files if scanning fails. + +## See Also + +- [DNS Policies](./dns-policies.md) - DNS-level filtering +- [DLP](../dlp/README.md) - Data loss prevention in HTTP traffic +- [Gotchas](./gotchas.md) - Common HTTP policy issues diff --git a/skills/cloudflare/references/gateway/lists.md b/skills/cloudflare/references/gateway/lists.md new file mode 100644 index 0000000..339f347 --- /dev/null +++ b/skills/cloudflare/references/gateway/lists.md @@ -0,0 +1,287 @@ +# Gateway Lists + +Custom allow/block lists for domains, URLs, IPs, and more. + +## Overview + +Lists provide reusable collections for Gateway policies: +- **Domain lists** - FQDNs for DNS policies +- **URL lists** - Full URLs for HTTP policies +- **IP lists** - IPs/CIDRs for network policies +- **Serial lists** - Device serial numbers +- **Email lists** - Email addresses + +## List Types + +| Type | Use Case | Example | +|------|----------|---------| +| `DOMAIN` | DNS filtering | `malware.com` | +| `URL` | HTTP filtering | `https://site.com/path` | +| `IP` | Network filtering | `10.0.0.0/8` | +| `SERIAL` | Device serial numbers | `ABC123DEF` | +| `EMAIL` | User emails | `user@example.com` | + +## Creating Lists + +### Domain List + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/gateway/lists" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "blocked_domains", + "description": "Domains to block", + "type": "DOMAIN", + "items": [ + {"value": "malware.example.com"}, + {"value": "phishing.example.com"}, + {"value": "*.gambling.com"} + ] + }' +``` + +### URL List + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/gateway/lists" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "blocked_urls", + "description": "Specific URLs to block", + "type": "URL", + "items": [ + {"value": "https://example.com/malware.exe"}, + {"value": "https://badsite.com/phishing/*"} + ] + }' +``` + +### IP List + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/gateway/lists" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "internal_networks", + "description": "Internal IP ranges", + "type": "IP", + "items": [ + {"value": "10.0.0.0/8"}, + {"value": "172.16.0.0/12"}, + {"value": "192.168.0.0/16"} + ] + }' +``` + +## Managing List Items + +### Add Items + +```bash +curl -X PATCH "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/gateway/lists/$LIST_ID" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "append": [ + {"value": "newdomain.com"}, + {"value": "anotherdomain.com"} + ] + }' +``` + +### Remove Items + +```bash +curl -X PATCH "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/gateway/lists/$LIST_ID" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "remove": ["domain-to-remove.com"] + }' +``` + +### Replace All Items + +```bash +curl -X PUT "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/gateway/lists/$LIST_ID" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "blocked_domains", + "items": [ + {"value": "only-these.com"}, + {"value": "domains-remain.com"} + ] + }' +``` + +### Get List Items + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/gateway/lists/$LIST_ID/items" +``` + +## Using Lists in Policies + +### DNS Policy with Domain List + +```json +{ + "name": "Block Custom Domains", + "action": "block", + "filters": ["dns"], + "traffic": "dns.fqdn in $blocked_domains" +} +``` + +### HTTP Policy with URL List + +```json +{ + "name": "Block Specific URLs", + "action": "block", + "filters": ["http"], + "traffic": "http.request.uri in $blocked_urls" +} +``` + +### Network Policy with IP List + +```json +{ + "name": "Allow Internal Traffic", + "action": "allow", + "filters": ["l4"], + "traffic": "net.dst.ip in $internal_networks" +} +``` + +### Combine List with Other Conditions + +```json +{ + "name": "Block Except for Engineering", + "action": "block", + "filters": ["dns"], + "traffic": "dns.fqdn in $risky_domains and not any(identity.groups[*] in {\"Engineering\"})" +} +``` + +## Bulk Import + +### From File (CSV) + +```bash +# Prepare CSV (one item per line) +cat > domains.csv << EOF +malware1.com +malware2.com +phishing1.com +EOF + +# Read and create list +ITEMS=$(cat domains.csv | jq -R -s 'split("\n") | map(select(length > 0)) | map({"value": .})') + +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/gateway/lists" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d "{ + \"name\": \"imported_domains\", + \"type\": \"DOMAIN\", + \"items\": $ITEMS + }" +``` + +### Sync from External Source + +```bash +#!/bin/bash +# Sync list from threat intel feed +DOMAINS=$(curl -s "https://threatfeed.example.com/domains.txt" | head -1000 | jq -R -s 'split("\n") | map(select(length > 0)) | map({"value": .})') + +curl -X PUT "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/gateway/lists/$LIST_ID" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d "{ + \"name\": \"threat_intel_domains\", + \"items\": $DOMAINS + }" +``` + +## Common List Patterns + +### Allow List (Safe Sites) + +```json +{ + "name": "allowed_domains", + "type": "DOMAIN", + "items": [ + {"value": "company.com"}, + {"value": "*.microsoft.com"}, + {"value": "*.google.com"} + ] +} +``` + +**Policy:** +```json +{ + "name": "Allow Safe Domains", + "action": "allow", + "filters": ["dns"], + "traffic": "dns.fqdn in $allowed_domains", + "precedence": 1 +} +``` + +### Block List (Risky Sites) + +```json +{ + "name": "blocked_domains", + "type": "DOMAIN", + "items": [ + {"value": "*.torrent.com"}, + {"value": "proxy*.com"}, + {"value": "vpn-free.com"} + ] +} +``` + +### Corporate Assets + +```json +{ + "name": "corporate_ips", + "type": "IP", + "items": [ + {"value": "203.0.113.0/24", "description": "NYC Office"}, + {"value": "198.51.100.0/24", "description": "LA Office"} + ] +} +``` + +## Wildcard Support + +- Domain lists support wildcards: `*.example.com` +- URL lists support wildcards: `https://example.com/*` +- IP lists support CIDR notation: `10.0.0.0/8` + +## Limits + +| Limit | Value | +|-------|-------| +| Lists per account | 100 | +| Items per list | 5,000 (Standard), 20,000 (Enterprise) | +| Item value length | 255 characters | + +## See Also + +- [DNS Policies](./dns-policies.md) - Using lists in DNS rules +- [HTTP Policies](./http-policies.md) - Using lists in HTTP rules +- [API](./api.md) - Full list API reference diff --git a/skills/cloudflare/references/gateway/locations.md b/skills/cloudflare/references/gateway/locations.md new file mode 100644 index 0000000..5a88169 --- /dev/null +++ b/skills/cloudflare/references/gateway/locations.md @@ -0,0 +1,249 @@ +# Gateway Locations + +Configure DNS endpoints for offices and networks without WARP. + +## Overview + +Locations provide Gateway DNS filtering for: +- **Office networks** - Route office DNS through Gateway +- **IoT devices** - Filter devices that can't run WARP +- **Legacy systems** - Apply policies to non-WARP traffic + +**How it works:** +1. Create location with source IP ranges +2. Configure network devices to use Gateway DNS IPs +3. Gateway applies DNS policies to matching traffic + +## Quick Start + +### 1. Create Location + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/gateway/locations" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "NYC Office", + "client_default": false, + "networks": [ + {"network": "203.0.113.0/24"} + ] + }' +``` + +### 2. Configure DNS on Network + +Point DNS to Gateway endpoints: +- **IPv4:** `172.64.36.1` and `172.64.36.2` +- **IPv6:** `2606:4700:4700::1111` and `2606:4700:4700::1001` +- **DoH:** `https://.cloudflare-gateway.com/dns-query` +- **DoT:** `.cloudflare-gateway.com` + +## Location Types + +### Office Network + +Filter all DNS from an office by source IP: + +```json +{ + "name": "NYC Office", + "networks": [ + {"network": "203.0.113.0/24"} + ] +} +``` + +### Default Location + +For traffic not matching any specific location: + +```json +{ + "name": "Default", + "client_default": true +} +``` + +### DoH Endpoint + +For DNS-over-HTTPS clients: + +```json +{ + "name": "Roaming Users (DoH)", + "doh_subdomain": "abc123xyz" +} +``` + +Users configure: `https://abc123xyz.cloudflare-gateway.com/dns-query` + +## API Operations + +### List Locations + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/gateway/locations" | \ + jq '.result[] | {id, name, networks}' +``` + +### Get Location + +```bash +curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/gateway/locations/$LOCATION_ID" +``` + +### Create Location + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/gateway/locations" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "LA Office", + "networks": [ + {"network": "198.51.100.0/24"} + ], + "ecs_support": false + }' +``` + +### Update Location + +```bash +curl -X PUT "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/gateway/locations/$LOCATION_ID" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "LA Office (Updated)", + "networks": [ + {"network": "198.51.100.0/24"}, + {"network": "198.51.101.0/24"} + ] + }' +``` + +### Delete Location + +```bash +curl -X DELETE "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/gateway/locations/$LOCATION_ID" \ + -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" +``` + +## Using Locations in Policies + +### Policy for Specific Location + +```json +{ + "name": "Block Social Media - NYC Office", + "action": "block", + "filters": ["dns"], + "traffic": "any(dns.content_category[*] in {\"Social Networks\"}) and identity.location.id == \"location-uuid\"" +} +``` + +### Policy for All Locations Except One + +```json +{ + "traffic": "any(dns.content_category[*] in {\"Gambling\"}) and identity.location.id != \"exec-location-uuid\"" +} +``` + +## Network Configuration + +### Router/Firewall DNS Settings + +Configure upstream DNS servers: +- Primary: `172.64.36.1` +- Secondary: `172.64.36.2` + +### DHCP Settings + +Distribute Gateway DNS to clients via DHCP. + +### DNS-over-HTTPS (DoH) + +For DoH-capable devices: +``` +https://.cloudflare-gateway.com/dns-query +``` + +### DNS-over-TLS (DoT) + +For DoT-capable devices: +``` +.cloudflare-gateway.com:853 +``` + +## IPv6 Support + +Enable IPv6 DNS for the location: + +```json +{ + "name": "IPv6 Enabled Office", + "networks": [ + {"network": "203.0.113.0/24"}, + {"network": "2001:db8::/32"} + ] +} +``` + +IPv6 Gateway DNS: +- `2606:4700:4700::1111` +- `2606:4700:4700::1001` + +## ECS (EDNS Client Subnet) + +Enable ECS for more accurate geolocation: + +```json +{ + "name": "ECS Enabled Location", + "networks": [{"network": "203.0.113.0/24"}], + "ecs_support": true +} +``` + +**Note:** ECS may expose client subnet to authoritative DNS servers. + +## Troubleshooting + +### Verify Traffic Reaching Location + +1. Check DNS query logs in Gateway dashboard +2. Filter by location name +3. Verify source IPs match configured networks + +### Test DNS Resolution + +```bash +# Test from office network +dig @172.64.36.1 example.com + +# Verify Gateway is answering +dig @172.64.36.1 TXT debug.cloudflare.com +``` + +### Location Not Matching + +- Verify source IP is in configured CIDR range +- Check for NAT/proxy changing source IP +- Ensure DNS traffic egresses from expected IP + +## Limits + +| Limit | Value | +|-------|-------| +| Locations per account | 100 | +| Networks per location | 10 | +| IP ranges overlap | Not allowed | + +## See Also + +- [DNS Policies](./dns-policies.md) - Using locations in policies +- [Devices](../devices/README.md) - WARP client alternative