ZTNA: Cloudflare Access policy design, tunnels, and machine identity
ZTNA (Cloudflare Access)
The ZTNA domain is tested through Access: how identity and device signals become a per-application authorization decision, how traffic reaches the app, and how non-human clients authenticate. Expect policy-writing items where several answers look reasonable and only one has the correct action, selector logic, and origin lockdown.
The two planes
Access decides at Cloudflare's edge; the application must not be reachable directly. Publish self-hosted apps through Cloudflare Tunnel using outbound-only cloudflared connectors, and private IP ranges through WARP-to-Tunnel. If the origin still answers on a public address, the policy is decorative and an attacker simply skips it.
Policy anatomy
- Action: Allow, Block, Bypass, Service Auth.
- Include means OR; Require means AND, with every listed condition passing. This distinction drives most wrong answers.
- Selectors: emails, email domain, IdP groups, country, IP, device posture, service token, mTLS certificate, reusable Access groups, external evaluation.
- Device posture needs an enrolled WARP client plus an integration (CrowdStrike, Intune, SentinelOne, Workspace ONE, Kolide, Tanium, ServiceNow) or a custom posture API.
A matching Block overrides Allow, so put explicit denies high in the list. Bypass skips authentication entirely; it is not a lenient Allow and must never be used to work around a broken IdP. Service tokens authenticate machines with CF-Access-Client-Id and CF-Access-Client-Secret and require the Service Auth action.
Worked example
Requirement: admin.example.com for the finance IdP group, only from encrypted managed laptops; deny contractors; allow a CI job to deploy.
- Block policy, Include = contractor group.
- Allow policy, Include = finance group, Require = disk encryption posture AND OS version posture.
- Service Auth policy, Include = service token
ci-deploy, short session duration.
Reasoning: the deny is explicit and evaluated first; Require forces both posture checks to pass together rather than either one; the CI path cannot use Allow because a service token cannot satisfy an identity selector.
Traps
- Leaving the origin publicly reachable, or tunnelling traffic without locking the origin to Cloudflare.
- Reading Include as AND, so the policy admits anyone in any listed group.
- Posture checks silently failing because the user reached the app without the WARP client.
- Long default session duration on high-value admin apps.
- Assuming Access covers SSH or RDP without configuring Infrastructure Apps and short-lived certificates.
- Believing IdP-side removal instantly ends an existing Access session.