CLOUDFLARE
ZERO TRUST
Never trust, always verify. Cloudflare Zero Trust replaces the corporate VPN with identity-aware, policy-driven access — for every user, device, and application, on any network.
WHAT IS ZERO TRUST
Zero Trust is a security model, not a product. The principle: no user, device, or network location is trusted by default — even inside your own network. Every request for access must be authenticated, authorised, and continuously validated.
The traditional model assumes that anything inside the corporate network is safe. That assumption broke the moment teams went remote, started using SaaS, and attackers learned to move laterally after a single breach. The perimeter disappeared. Zero Trust acknowledges that and removes the concept of "inside" entirely.
Cloudflare Zero Trust is the implementation: a suite of five products that enforce Zero Trust principles at the network level, between users and applications, and between services and services — all running on Cloudflare's global network.
Treat every access request as if it originates from an untrusted network. Verify identity. Enforce policy. Log everything. Assume breach.
WHY IT REPLACES VPN
- Binary access — you're in or you're out
- Full network access once connected
- No per-application policies
- Lateral movement after breach is trivial
- Slow — all traffic hairpins through a central server
- Painful to manage at scale
- No device posture checks
- Hard to audit who accessed what
- Per-application, per-resource policies
- Identity + device posture required every session
- Breach is contained — no lateral movement
- Traffic routes via nearest Cloudflare PoP (fast)
- Works on any network, any device
- Full audit log of every access event
- Free for teams under 50 users
- Integrates with any IdP (Google, Okta, GitHub, etc.)
THE 5 COMPONENTS
Cloudflare Zero Trust is five products that work together. You can deploy them independently or as a suite.
Sits in front of your apps. Every request goes through Cloudflare. Access checks identity and policy before passing the request on.
A daemon running on your server that creates an outbound-only encrypted tunnel to Cloudflare. No open inbound ports. No public IP needed.
Client installed on user devices. Routes device traffic through Cloudflare. Enables device posture checks and private network access.
Cloudflare's Secure Web Gateway. Filters DNS queries and HTTP traffic. Blocks malware, phishing, and content categories. Full inspection.
Runs the browser in Cloudflare's network. Nothing executes on the user device. Protects against malicious web content and data exfiltration.
ACCESS — APPLICATION GATEWAY
Cloudflare Access is the front door for every application you want to protect. Instead of putting a login form on the app itself, Access intercepts every request and checks identity and policy before the request reaches your app.
The user experience: hit a protected URL → Cloudflare redirects to your IdP (Google, GitHub, Okta, etc.) → user authenticates → Cloudflare issues a signed JWT → app receives the request with the JWT as proof of identity.
How it works
Policy rules
Access policies define who can reach what. Rules can be based on:
- Email —
craig@2nth.aior*@2nth.ai - Identity provider groups — GitHub org, Google Workspace group
- Country — allow ZA, block specific regions
- Device posture — require WARP, check OS version, require disk encryption
- mTLS certificate — for machine-to-machine service access
Self-hosted vs SaaS applications
| Type | What it protects | How |
|---|---|---|
| Self-hosted | Apps on your own infrastructure (admin panels, internal tools, APIs) | Combine with Cloudflare Tunnel — no public IP needed |
| SaaS | Third-party apps that support SAML/OIDC (Jira, Notion, GitHub) | Access acts as IdP proxy — enforces additional policy before SSO |
| SSH / RDP | Server access via browser | Access renders SSH in browser — no client needed |
Validating the JWT in your app
Once Access is in front of your app, every request carries a Cf-Access-Jwt-Assertion header. You should validate it to prevent bypassing Access by hitting your origin directly.
// Cloudflare Worker — validate Access JWT
import { jwtVerify, importX509 } from 'jose';
const TEAM_DOMAIN = 'https://your-team.cloudflareaccess.com';
const CERTS_URL = `${TEAM_DOMAIN}/cdn-cgi/access/certs`;
export async function validateAccessJWT(request: Request): Promise<boolean> {
const token = request.headers.get('Cf-Access-Jwt-Assertion');
if (!token) return false;
// Fetch public keys from Cloudflare
const certsRes = await fetch(CERTS_URL);
const { public_cert } = await certsRes.json() as any;
try {
const key = await importX509(public_cert, 'RS256');
await jwtVerify(token, key, { issuer: TEAM_DOMAIN });
return true;
} catch {
return false;
}
}
TUNNEL — SECURE INBOUND CONNECTOR
Cloudflare Tunnel (formerly Argo Tunnel) solves a fundamental problem: how do you expose a service to the internet without opening inbound firewall ports or having a public IP address?
The answer: run cloudflared on your server. It creates an outbound-only encrypted connection to Cloudflare's nearest data centre. Traffic flows in through Cloudflare, down the tunnel to your service. The internet never touches your server directly.
Tunnel architecture
No inbound ports open. No public IP required. Your server firewall can drop all incoming connections. The only traffic that reaches your app came through Cloudflare.
Setup
# Install cloudflared
brew install cloudflare/cloudflare/cloudflared # macOS
# or download from https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/
# Authenticate
cloudflared tunnel login
# Create a named tunnel
cloudflared tunnel create my-app
# Create config file (~/.cloudflared/config.yml)
tunnel: <TUNNEL-ID>
credentials-file: /home/user/.cloudflared/<TUNNEL-ID>.json
ingress:
- hostname: app.example.com
service: http://localhost:3000
- hostname: api.example.com
service: http://localhost:8787
- service: http_status:404
# Add DNS record (proxied CNAME to <tunnel-id>.cfargotunnel.com)
cloudflared tunnel route dns my-app app.example.com
# Run the tunnel
cloudflared tunnel run my-app
# Or run as a system service
cloudflared service install
Using Tunnel with Access
Tunnel + Access is the standard pattern for protecting internal apps without a VPN:
- Tunnel exposes your app at
app.example.comthrough Cloudflare - Access policy sits in front — only authenticated users matching the policy reach the tunnel
- Your server sees only traffic that already passed the Access check
- Close all inbound firewall ports entirely
WARP — DEVICE AGENT
WARP is the client application installed on user devices (Mac, Windows, iOS, Android, Linux). It routes device traffic through Cloudflare's network and is the enforcement point for device posture policies.
For Zero Trust, WARP does two things: (1) it identifies the device to Cloudflare so Access policies can include device posture rules, and (2) it routes traffic through Gateway for DNS and HTTP filtering.
WARP modes
| Mode | What it does | Use case |
|---|---|---|
| WARP | Routes all traffic through Cloudflare. DNS + HTTP filtering active. | Endpoint protection for team devices |
| Gateway with WARP | Enables Gateway policies and device posture checks | Required for ZT Access device posture rules |
| Proxy mode | Routes only traffic bound for private networks | Accessing internal apps without full traffic routing |
Device posture checks
Once WARP is deployed, Access policies can require device health checks before granting access:
- WARP running — device must have WARP active
- OS version — macOS 14+, Windows 11+
- Disk encryption — FileVault / BitLocker enabled
- Application running — e.g. require CrowdStrike or Sentinel
- Domain joined — device registered with your organisation's directory
# Deploy WARP via MDM (example: Jamf)
# Set the organisation name to your Zero Trust team name
# Users get WARP pre-configured — just needs authentication on first launch
# Or manually:
# 1. Install WARP from cloudflare.com/warp
# 2. Click Preferences → Account → Login with Cloudflare Zero Trust
# 3. Enter team name (your-team.cloudflareaccess.com)
GATEWAY — DNS & HTTP FILTERING
Cloudflare Gateway is the Secure Web Gateway (SWG) component. It sits between your users and the internet, inspecting DNS queries and HTTP/S traffic, applying filtering policies, and logging everything.
DNS filtering
All DNS queries from WARP-enrolled devices go through Cloudflare's resolver. Gateway can block queries based on:
- Categories (malware, phishing, adult content, social media)
- Domain lists (custom blocklists)
- Security intelligence (Cloudflare's threat feeds)
- New domains (flag domains registered in the last 30 days)
HTTP filtering
Gateway can perform full TLS inspection, enabling policy enforcement on HTTPS traffic:
- Block file uploads to unauthorised cloud storage
- Prevent data exfiltration via HTTP POST
- Enforce Safe Search on Google/Bing/YouTube
- Block OAuth to unauthorised SaaS apps
Point your DNS resolver to 1.1.1.1 using your Gateway DoH/DoT endpoint: https://<team>.cloudflareaccess.com/dns-query. Instant filtering with no client software.
Resolver policies (example)
# Gateway DNS policy examples (configured in dashboard)
# Block known malware/phishing domains
IF Security Category = Malware OR Phishing
THEN Block
# Block social media during work hours (optional)
IF Category = Social Networks AND Time = 09:00-17:00
THEN Block
# Log all new/uncategorised domains
IF Domain Age < 30 days
THEN Log + Allow (review)
# Allow everything else
THEN Allow
BROWSER ISOLATION
Browser Isolation runs the web browser inside Cloudflare's network rather than on the user's device. The user sees a pixel stream of the browser — interactive but with no code executing locally.
This eliminates an entire class of attack: malicious JavaScript, drive-by downloads, browser exploits — none of them can reach the user's device because the browser isn't running there.
Use cases
- Unmanaged device access — contractors using personal devices accessing corporate apps
- Risky web categories — any URL matching a risky policy opens in isolated browser automatically
- Data protection — copy/paste, download, and printing can be disabled for sensitive apps
- Legacy browser support — old apps that require IE-era rendering
For end users, an isolated browser looks and feels like a normal browser tab. The isolation is transparent — there's no separate app to open or noticeable latency for most content.
FULL ZERO TRUST ARCHITECTURE
How all five components work together in a complete deployment:
HOW 2NTH.AI USES ZERO TRUST
2nth.ai runs its admin infrastructure behind Cloudflare Zero Trust. Here's the current and planned deployment:
| Resource | Protection | Policy |
|---|---|---|
| Admin API routes | Access policy on /admin/* | Email in @2nth.ai, @imbila.ai, or @b2bs.co.za |
| bill.2nth.ai admin view | Role check in Worker session | Admin email list from env var — moving to Access policy |
| Wrangler / CF API access | API tokens with scoped permissions | Separate token per project, minimum required scopes |
| Client portals (planned) | Access per-client subdomain | Per-client email domain + WARP device posture |
| Internal tooling (planned) | Tunnel + Access | No public IP, identity gate for every tool |
Zero Trust is how one operator secures infrastructure that would normally need a dedicated security team. No VPN to manage. No firewall rules to maintain. Policy-as-config in the Cloudflare dashboard.
SETUP GUIDE
Getting a basic Access + Tunnel deployment running from scratch.
Step 1 — Create a Zero Trust organisation
- Go to dash.cloudflare.com → Zero Trust (left sidebar)
- Choose a team name (e.g.
2nth→ your apps will be at*.cloudflareaccess.com) - Select the Free plan (up to 50 users, no credit card)
Step 2 — Connect an identity provider
# In Zero Trust dashboard → Settings → Authentication → Add new IdP
# Google Workspace
# 1. Create OAuth2 app in Google Cloud Console
# 2. Authorised redirect URI: https://<team>.cloudflareaccess.com/cdn-cgi/access/callback
# 3. Add Client ID + Secret to Cloudflare
# GitHub (good for dev teams)
# 1. Create OAuth app in GitHub → Settings → Developer settings
# 2. Homepage: https://<team>.cloudflareaccess.com
# 3. Callback: https://<team>.cloudflareaccess.com/cdn-cgi/access/callback
# One-time PIN (no IdP needed — email OTP)
# Built-in, no setup required — good for testing
Step 3 — Create an Access application
# Zero Trust → Access → Applications → Add an application
Type: Self-hosted
Application name: 2nth Admin
Subdomain: admin (→ admin.2nth.ai)
Session duration: 24 hours
# Add a policy
Policy name: 2nth team
Action: Allow
Include: Email ends in @2nth.ai OR @imbila.ai
Step 4 — Create a Tunnel
# Install cloudflared
brew install cloudflare/cloudflare/cloudflared
# Login
cloudflared tunnel login
# Create tunnel
cloudflared tunnel create 2nth-admin
# Create ~/.cloudflared/config.yml
tunnel: <your-tunnel-id>
credentials-file: /Users/you/.cloudflared/<tunnel-id>.json
ingress:
- hostname: admin.2nth.ai
service: http://localhost:8788 # your local admin server
- service: http_status:404
# Route DNS
cloudflared tunnel route dns 2nth-admin admin.2nth.ai
# Run
cloudflared tunnel run 2nth-admin
Step 5 — Verify
- Visit
admin.2nth.aiin a browser - Cloudflare Access redirects you to your IdP login
- After login, Access issues a JWT and passes the request to your tunnel
- Your local service handles the request — never exposed publicly
POLICY PATTERNS
Common policy configurations for 2nth-style operator deployments:
| Pattern | Rule | When to use |
|---|---|---|
| Email domain allow | Email ends in @yourcompany.com | Team-wide access to internal tools |
| Specific emails | Email in [list] | Tightly scoped admin access |
| Country block | Country NOT in [ZA, GB, US] | Geo-restrict sensitive apps |
| Device posture required | WARP enrolled + OS version ≥ X | High-security apps requiring managed devices |
| IdP group | GitHub org = imbilawork | Grant access to all org members |
| Bypass (no auth) | IP in [known CIDRs] | Server-to-server calls that don't carry a user token |
| Service token | Service token = [token] | Machine-to-machine API calls through Access |
mTLS AND SERVICE TOKENS
For machine-to-machine communication — where there's no user to authenticate — Zero Trust uses either mutual TLS (mTLS) or service tokens.
Service tokens
A service token is a Client ID + Client Secret pair. The calling service includes these as headers. Access validates them and passes the request.
# Calling a Zero Trust protected API from a Worker
const response = await fetch('https://internal-api.example.com/data', {
headers: {
'CF-Access-Client-Id': env.CF_ACCESS_CLIENT_ID,
'CF-Access-Client-Secret': env.CF_ACCESS_CLIENT_SECRET,
}
});
# In Access policy:
# Action: Service Auth
# Include: Service Token = [your token name]
mTLS
mTLS requires both sides of a connection to present a certificate. Cloudflare can validate client certificates before passing requests to your origin — useful for IoT devices, embedded hardware, and B2B API integrations where you control both ends.
# Upload your CA certificate to Cloudflare
# (Settings → mTLS → Upload Certificate Authority)
# In Access policy or WAF rule:
# Require: client certificate issued by your CA
# This blocks any request that doesn't present a valid cert — regardless of other auth
PRICING
| Plan | Users | Cost | What you get |
|---|---|---|---|
| Free | Up to 50 | $0 | Access, Tunnel, WARP, Gateway DNS filtering, basic policies |
| Pay-as-you-go | 51+ | $3/user/month | All Free features + HTTP filtering, Browser Isolation (add-on), more policy types |
| Zero Trust Enterprise | Unlimited | Custom | Full Browser Isolation, DLP, CASB, Remote Network, Priority support |
The Free plan handles all operator-scale infrastructure. 50 users is enough for a 2nth team + client portal access. You hit pay-as-you-go only when running client environments at scale — and at $3/user/month, it's cheaper than any alternative.